Skip to main content
After a tax prep run completes, the work moves to review: read the leadsheets the run produced, walk the issues it flagged, and record sign-offs on the sheets and rows you have reviewed. This recipe is the minimal call sequence for that flow against one client and one completed tax prep task. Every call uses a workspaceToken (see Authentication) and goes to the single GraphQL endpoint:
https://router.apps.filed.com/graphql
This recipe does not re-document the types it touches. For the full Leadsheets / Leadsheet / LeadsheetSheetIssue / LeadsheetFieldRow / LeadsheetTrace field list, and for the DocumentMessage annotation and thread APIs, see Leadsheets and review. For how to start and poll the TAX_PREP task whose taskId you feed into this flow, see Run tax prep end to end and Tasks.

1. Read the leadsheets for a completed run

A leadsheets tree is the output of a TAX_PREP (or TAX_REVIEW) background task. Pass that task’s taskId to binder.leadsheets(taskId:) to read the exact tree that run produced. The query below mirrors the web app’s GetBinderLeadsheets document: it walks sheets, their issues and per-severity counts, and the field rows with their traces and sources.
query GetClientLeadsheets($clientId: ID!, $taskId: ID) {
  me {
    ... on WorkspaceUser {
      id
      workspace {
        id
        clients(filters: { ids: [$clientId] }) {
          id
          binder {
            id
            leadsheets(taskId: $taskId) {
              id
              documentPath
              issueCount
              returnType
              taxYear
              sheets {
                id
                formName
                category
                issueCount {
                  critical
                  high
                  medium
                  low
                }
                issues {
                  id
                  markType
                  anchorPoint
                  body
                  createdBy
                  createdAt
                  hiddenAt
                  resolved
                }
                fields {
                  id
                  rows {
                    id
                    fieldPath
                    value
                    trace {
                      reasoning
                      sources {
                        subdocId
                        label
                        amount
                        page
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}
{
  "clientId": "018f9c2a-3d5f-7a10-b2c4-9e8d7f6a5b4c",
  "taskId": "018f9c2b-7c4d-7e10-9a22-6b3c4d5e6f70"
}
curl -X POST https://router.apps.filed.com/graphql \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_WORKSPACE_TOKEN" \
  -d '{
    "query": "query GetClientLeadsheets($clientId: ID!, $taskId: ID) { me { ... on WorkspaceUser { id workspace { id clients(filters: { ids: [$clientId] }) { id binder { id leadsheets(taskId: $taskId) { id documentPath issueCount returnType taxYear sheets { id formName category issueCount { critical high medium low } issues { id markType anchorPoint body createdBy createdAt hiddenAt resolved } fields { id rows { id fieldPath value trace { reasoning sources { subdocId label amount page } } } } } } } } } } } } }",
    "variables": {
      "clientId": "018f9c2a-3d5f-7a10-b2c4-9e8d7f6a5b4c",
      "taskId": "018f9c2b-7c4d-7e10-9a22-6b3c4d5e6f70"
    }
  }'
{
  "data": {
    "me": {
      "id": "019f0fb6-37b1-7800-b7bc-0d11288504b1",
      "workspace": {
        "id": "019f0fb6-3001-7900-b7bc-0d11288504b1",
        "clients": [
          {
            "id": "018f9c2a-3d5f-7a10-b2c4-9e8d7f6a5b4c",
            "binder": {
              "id": "018f9c2a-4b6f-7a10-b2c4-9e8d7f6a5b4d",
              "leadsheets": {
                "id": "018f9c2b-8e10-7f20-9a33-7c4d5e6f7081",
                "documentPath": "leadsheets",
                "issueCount": 3,
                "returnType": "F1040",
                "taxYear": 2025,
                "sheets": [
                  {
                    "id": "leadsheets/schedule_b/0",
                    "formName": "Schedule B",
                    "category": "income",
                    "issueCount": {
                      "critical": 0,
                      "high": 1,
                      "medium": 1,
                      "low": 1
                    },
                    "issues": [
                      {
                        "id": "018f9c2c-1a2b-7f30-9b44-7d5e6f708190",
                        "markType": "flag",
                        "anchorPoint": { "page": 1, "coordinates": { "x": 0, "y": 0 } },
                        "body": "Schedule B interest total differs from 1099-INT sum by $42.",
                        "createdBy": "019f0fb6-3001-7900-b7bc-0d11288504b1",
                        "createdAt": "2026-07-04T10:12:00.000Z",
                        "hiddenAt": null,
                        "resolved": false
                      }
                    ],
                    "fields": [
                      {
                        "id": "leadsheets/schedule_b/0/interest_income",
                        "rows": [
                          {
                            "id": "leadsheets/schedule_b/0/interest_income/0",
                            "fieldPath": "interest_income.total",
                            "value": "428.00",
                            "trace": {
                              "reasoning": "Total interest is the sum of the three 1099-INT sources in the binder.",
                              "sources": [
                                {
                                  "subdocId": "018f9c2a-7b1e-7c3d-9a4e-2f6b1c8d0e5a",
                                  "label": "1099-INT from Acme Broker",
                                  "amount": "210.00",
                                  "page": 1
                                },
                                {
                                  "subdocId": "018f9c2a-7c3d-7c3d-9a4e-2f6b1c8d2f7b",
                                  "label": "1099-INT from Globex",
                                  "amount": "176.00",
                                  "page": 1
                                },
                                {
                                  "subdocId": "018f9c2a-8e2f-7c3d-9a4e-2f6b1c8d3f8c",
                                  "label": "1099-INT from Initech",
                                  "amount": "42.00",
                                  "page": 1
                                }
                              ]
                            }
                          }
                        ]
                      }
                    ]
                  }
                ]
              }
            }
          }
        ]
      }
    }
  }
}
The shape above matches what the web app’s binder and review screens read. Use Leadsheets.issueCount for a quick “needs attention” number, drill into Leadsheet.issueCount for per-severity counts, then page through LeadsheetSheetIssue for per-issue detail. resolved is server-computed from the document messages on the sheet, so refresh the query after every sign-off or undo (see step 4).
There is no top-level leadsheets query. Leadsheets belong to a client’s binder, so you read them through me { ... on WorkspaceUser { workspace { clients(filters: { ids: [$clientId] }) { binder { leadsheets(taskId: $taskId) { ... } } } } } }. The workspaceToken already identifies the workspace. See Leadsheets and review for the full field list.

2. Record a sign-off

There is no signOffSubDocuments mutation. The schema defines an input type called SignOffSubDocumentsInput, but no field on Mutation is wired to it. The real sign-off write surface is createDocumentMessage with type: "activity" and markType: "signoff", one call per subdocument you are signing off on. Do not look for a signOffSubDocuments mutation, it does not exist.
A sign-off is a createDocumentMessage call anchored to the subdocument path you are signing off on. The anchorPoint carries the reviewer’s level and user_role so the UI renders the sign-off with the right label.
mutation CreateDocumentMessage($input: CreateDocumentMessageInput!) {
  createDocumentMessage(input: $input) {
    id
    documentPath
    type
    markType
    anchorPoint
    body
    createdBy
    createdAt
    hiddenAt
  }
}
{
  "input": {
    "clientId": "018f9c2a-3d5f-7a10-b2c4-9e8d7f6a5b4c",
    "documentPath": "018f9c2a-7b1e-7c3d-9a4e-2f6b1c8d0e5a",
    "type": "activity",
    "markType": "signoff",
    "anchorPoint": {
      "page": 1,
      "coordinates": { "x": 0, "y": 0 },
      "level": 2,
      "user_role": "l2"
    }
  }
}
curl -X POST https://router.apps.filed.com/graphql \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_WORKSPACE_TOKEN" \
  -d '{
    "query": "mutation CreateDocumentMessage($input: CreateDocumentMessageInput!) { createDocumentMessage(input: $input) { id documentPath type markType anchorPoint body createdBy createdAt hiddenAt } }",
    "variables": {
      "input": {
        "clientId": "018f9c2a-3d5f-7a10-b2c4-9e8d7f6a5b4c",
        "documentPath": "018f9c2a-7b1e-7c3d-9a4e-2f6b1c8d0e5a",
        "type": "activity",
        "markType": "signoff",
        "anchorPoint": {
          "page": 1,
          "coordinates": { "x": 0, "y": 0 },
          "level": 2,
          "user_role": "l2"
        }
      }
    }
  }'
{
  "data": {
    "createDocumentMessage": {
      "id": "018f9c2c-2b3c-7f40-9b55-7e6f70829001",
      "documentPath": "018f9c2a-7b1e-7c3d-9a4e-2f6b1c8d0e5a",
      "type": "activity",
      "markType": "signoff",
      "anchorPoint": {
        "page": 1,
        "coordinates": { "x": 0, "y": 0 },
        "level": 2,
        "user_role": "l2"
      },
      "body": null,
      "createdBy": "019f0fb6-3001-7900-b7bc-0d11288504b1",
      "createdAt": "2026-07-04T18:22:01.000Z",
      "hiddenAt": null
    }
  }
}
Save the returned id. You pass it to hideDocumentMessage in step 3 if you ever need to undo the sign-off.
documentPath for a subdocument sign-off is the subdocument’s ID (the same value you read as LeadsheetFieldRow.id parent path, or the anchor a LeadsheetSheetIssue is tied to). The web app signs off one subdocument at a time, one createDocumentMessage call per subdocument.

3. Undo a sign-off

Undoing a sign-off is a soft-hide of the sign-off DocumentMessage. The sign-off row stays in history (with hiddenAt set), and the leadsheets query’s resolved and issueCount recomputation backs it out.
mutation HideDocumentMessage($id: ID!) {
  hideDocumentMessage(id: $id) {
    id
    documentPath
    type
    markType
    hiddenAt
    hiddenBy
  }
}
{
  "id": "018f9c2c-2b3c-7f40-9b55-7e6f70829001"
}
curl -X POST https://router.apps.filed.com/graphql \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_WORKSPACE_TOKEN" \
  -d '{
    "query": "mutation HideDocumentMessage($id: ID!) { hideDocumentMessage(id: $id) { id documentPath type markType hiddenAt hiddenBy } }",
    "variables": { "id": "018f9c2c-2b3c-7f40-9b55-7e6f70829001" }
  }'
{
  "data": {
    "hideDocumentMessage": {
      "id": "018f9c2c-2b3c-7f40-9b55-7e6f70829001",
      "documentPath": "018f9c2a-7b1e-7c3d-9a4e-2f6b1c8d0e5a",
      "type": "activity",
      "markType": "signoff",
      "hiddenAt": "2026-07-04T18:30:00.000Z",
      "hiddenBy": "019f0fb6-3001-7900-b7bc-0d11288504b1"
    }
  }
}

4. Refetch the leadsheets query

LeadsheetSheetIssue.resolved and Leadsheet.issueCount (and the per-severity Leadsheet.issueCount { critical high medium low } breakdown) are server-computed from the document messages on the sheet. After any sign-off or undo, refetch the GetClientLeadsheets query from step 1 so the server recomputes them. The web app does exactly this: it refetches the leadsheets query (and the missing-items query) whenever the review task ends or a sign-off is toggled.
# Same query as step 1. Re-run it with the same { clientId, taskId }.
query GetClientLeadsheets($clientId: ID!, $taskId: ID) {
  me {
    ... on WorkspaceUser {
      workspace {
        clients(filters: { ids: [$clientId] }) {
          binder {
            leadsheets(taskId: $taskId) {
              id
              issueCount
              sheets {
                id
                formName
                issueCount { critical high medium low }
                issues { id resolved }
                signOffs { id markType hiddenAt }
              }
            }
          }
        }
      }
    }
  }
}
After the refetch, the issue you signed off on shows resolved: true, the sheet’s issueCount drops by one, and the Leadsheets.issueCount total reflects the new state. If you undo a sign-off, the same refetch backs the issue out again.

See also

  • Leadsheets and review for the full Leadsheets, Leadsheet, LeadsheetSheetIssue, LeadsheetFieldRow, and LeadsheetTrace type definitions, plus the DocumentMessage annotation and thread APIs.
  • Document messages for the broader DocumentMessage API (annotations, flags, threads, update/unhide) that sign-offs are one use of.
  • Tasks for the polling mechanics behind the TAX_PREP task whose taskId you feed into leadsheets(taskId:).
  • Run tax prep end to end for the recipe that produces the review items this flow consumes.