Export comments for reporting, archival, and reconciliation

Read thread snapshots, retrieve one conversation, or drain the fund-wide comment feed.

Before you start: complete the authentication and TypeScript client setup in Commenting before running these examples.

Export workflows

Use the Comment API to read conversations out of Anduin — for a full archive, a reconciliation pass after an import, or a periodic sync into your external system. Three read endpoints cover it, from coarsest to finest:

  1. ListCommentThreadsGET /{fundId}/comment-threads lists every non-empty thread in the fund with its anchor, visibility, status, assignee, and comment count. Use it for the per-conversation view — for example, to group an export by form question or to report on open versus resolved threads.
  2. ExportFundCommentsGET /{fundId}/comments returns one flat feed of every comment (opening comments and replies) across the whole fund. This is the simplest way to export everything: each comment carries its threadId, so you can rebuild conversations on your side or join them against the thread list from step 1.
  3. ListThreadCommentsGET /{fundId}/comment-threads/{threadId}/comments returns one thread's comments when you already know which conversation you want — for example, to verify an import.

What a thread snapshot looks like

This is a real ListCommentThreads response for a small demo fund — one investor order carrying its two general threads, an AML/KYC document thread, and a form-question thread:

{
  "threads": [
    {
      "threadId": "txnoo7ed9y1mzrwy.offis00.isuyd6r57",
      "anchorResourceId": "txnoo7ed9y1mzrwy.fsb4x2o.lpp9lq1n0o",
      "anchorResourceType": "Order",
      "anchor": { "subType": "general" },
      "visibility": "internal",
      "status": "open",
      "createdAt": "2026-07-22T04:52:52.174Z",
      "lastCommentAt": "2026-07-23T08:06:25.290Z"
    },
    {
      "threadId": "txnoo7ed9y1mzrwy.offis00.isumg3083",
      "anchorResourceId": "txnoo7ed9y1mzrwy.fsb4x2o.lpp9lq1n0o",
      "anchorResourceType": "Order",
      "anchor": { "subType": "general" },
      "visibility": "shared",
      "status": "open",
      "createdAt": "2026-07-22T04:52:53.448Z",
      "lastCommentAt": "2026-07-23T08:07:32.037Z"
    },
    {
      "threadId": "txnoo7ed9y1mzrwy.offis00.isu60rgng",
      "anchorResourceId": "txnoo7ed9y1mzrwy.fsb4x2o.lpp9lq1n0o",
      "anchorResourceType": "Order",
      "anchor": { "subType": "amlKycDocument", "docType": "W-9" },
      "visibility": "internal",
      "status": "open",
      "createdAt": "2026-07-23T08:05:26.817Z",
      "lastCommentAt": "2026-07-23T08:13:15.188Z"
    },
    {
      "threadId": "txnoo7ed9y1mzrwy.offis00.isuvz3vl2",
      "anchorResourceId": "txnoo7ed9y1mzrwy.fsb4x2o.lpp9lq1n0o",
      "anchorResourceType": "Order",
      "anchor": {
        "subType": "formQuestion",
        "formVersionId": "forocr00000000000002.fveversio0.lfv81kl0gn",
        "fieldAlias": "entityname",
        "tableOfContentTitle": "Section I - General Information",
        "description": "1. Registration Name(s) in Which Investment Should Be Held (“Subscriber”)"
      },
      "visibility": "shared",
      "status": "open",
      "createdAt": "2026-07-23T08:12:23.236Z",
      "lastCommentAt": "2026-07-23T08:13:12.612Z"
    }
  ],
  "hasMore": false
}

Notice: the thread snapshot is metadata only — it never contains comment bodies. A snapshot row tells you what a conversation is about (anchor), who can see it (visibility), and where it stands (status, commentCount, lastCommentAt) — but not what was said. To read the messages, follow up with ExportFundComments (fund-wide) or ListThreadComments (one thread).

Draining the comment feed

All three read endpoints are cursor-paginated. To export every comment, page through the fund-wide feed with the standard cursor loop:

const fundId = "YOUR_FUND_ID";
const comments = [];
let cursor: string | undefined;

do {
  const { data: page, error } = await client.GET("/api/v1/fundsub/{fund-id}/comments", {
    params: {
      path: { "fund-id": fundId },
      query: { limit: 200, ...(cursor ? { cursor } : {}) },
    },
  });
  if (error) throw new Error(JSON.stringify(error));
  comments.push(...page.comments);
  cursor = page.hasMore ? page.nextCursor : undefined;
} while (cursor);

Each element of the feed is one comment. Here are two consecutive comments from the same fund's feed (abridged) — note how you tell an imported comment apart from one typed in the application:

[
  {
    "commentId": "txnoo7ed9y1mzrwy.offis00.isuvz3vl2.dcmroot000000",
    "threadId": "txnoo7ed9y1mzrwy.offis00.isuvz3vl2",
    "author": { "email": "[email protected]", "displayName": "Virginia Lee" },
    "body": "<p>Should this be under my personal name or our family trust? The trust registration is still pending.</p>",
    "createdAt": "2026-07-23T08:12:23.236Z",
    "mentions": [],
    "isRoot": true
  },
  {
    "commentId": "txnoo7ed9y1mzrwy.offis00.isuvz3vl2.dcmjyjyqrz4oz",
    "threadId": "txnoo7ed9y1mzrwy.offis00.isuvz3vl2",
    "author": { "email": "[email protected]", "displayName": "Dev Service Account" },
    "body": "Hi Virginia — please use the family trust, exactly as it is registered with us: \"Lee Family Trust\". We'll sync the final registration name back to the CRM once the trust paperwork completes.",
    "createdAt": "2026-07-23T08:13:12.612Z",
    "mentions": [],
    "importedMetadata": {
      "onBehalfOf": "Jordan Lee (External CRM)",
      "externalRef": "crm-comment-2049"
    },
    "isRoot": false
  }
]

When reading exported comments, keep the following in mind:

  • importedMetadata (containing your onBehalfOf and externalRef) identifies comments that were imported through this API. Comments typed in the Anduin application do not have it — the first comment above was typed by the investor, the second was imported.
  • Comments typed in the application may carry lightweight HTML in body (the <p> wrapper above). Normalize it if your system expects plain text.
  • Mentions appear in body as plain-text tokens: @[[email protected]] and @[team:TEAM-ID].
  • The fund-wide feed is delivered at-least-once. Deduplicate the feed using commentId.

Limits and pagination

  • List endpoints return 50 records by default and support a maximum limit of 200.
  • Follow nextCursor until hasMore is false.
  • Keep the same anchorResourceId filter when following a cursor from a filtered request.
  • Treat cursors, thread IDs, and comment IDs as opaque values.

Troubleshooting

No threads are returned

The fund may have no comments yet, or its general threads may still be empty. ListCommentThreads returns only non-empty threads.

The API returns 403

Confirm that the API service account can read the thread's visibility tier. Internal comments require fund-team access.

The API returns 404 for a thread

Confirm that the threadId belongs to the fund in the request URL. Thread IDs cannot be used across funds.

Endpoints used

All paths below are relative to /api/v1/fundsub.

OperationMethod and pathPurpose
ListCommentThreadsGET /{fundId}/comment-threadsList non-empty comment threads in a fund.
ListThreadCommentsGET /{fundId}/comment-threads/{threadId}/commentsRead comments in one thread.
ExportFundCommentsGET /{fundId}/commentsExport the fund-wide comment feed.

Did this page help you?