Skip to main content
Leadsheets are the per-form workpapers a tax prep or review run produces: each leadsheet maps a tax form (for example Schedule B) to the binder sources that feed every line, flags the issues the run found on that form, and carries the sign-offs a reviewer records against it. Read them with the leadsheets field on the client’s binder, and record sign-offs with the createDocumentMessage mutation (a sign-off is an activity document message with markType: "signoff", see Document messages). Leadsheets are reached through the me query resolved as a WorkspaceUser, so reading them and recording sign-offs both require a workspaceToken (see Authentication). All requests go to:
https://router.apps.filed.com/graphql
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.
A leadsheets tree is the output of a TAX_PREP (or TAX_REVIEW) background task. Pass that task’s taskId to leadsheets(taskId:) to read the exact tree that run produced. See Tasks for how to start and poll a run, and Tax prep for the TaskTaxPrepResult shape (whose reviewItems are the same review items surfaced here as LeadsheetSheetIssue).

The Leadsheets type

The top-level leadsheets container for one client and one task run.
type Leadsheets {
  id: ID!
  task: Task!
  documentPath: String!
  sheets(id: ID): [Leadsheet!]!
  issueCount: Int!
  returnType: String!
  taxYear: Int
}
id
ID!
The leadsheets container ID.
task
Task!
The background task that produced this leadsheets tree. Its type is TAX_PREP or TAX_REVIEW; its status tells you whether the tree is still being built (RUNNING) or ready to read (COMPLETED).
documentPath
String!
The binder path the leadsheets container lives at.
sheets
[Leadsheet!]!
The per-form leadsheets. Pass sheets(id: $id) to fetch a single leadsheet by ID; omit the argument to list them all. See Leadsheet.
issueCount
Int!
Total number of unresolved issues across every sheet. Use this as a quick “needs attention” count before paging into sheets.
returnType
String!
The return form this run targeted, for example "F1040". This is a String here, not the ReturnType enum (see clients).
taxYear
Int
The tax year this run targeted, for example 2025. Nullable: older runs may not record it.

The Leadsheet type

One form’s leadsheet: its fields, the issues the run flagged on it, and the sign-offs reviewers have recorded against it.
type Leadsheet {
  id: ID!
  formName: String!
  category: String!
  issueCount: IssueCountBySeverity!
  issues: [LeadsheetSheetIssue!]!
  signOffs: [DocumentMessage!]!
  fields: [LeadsheetField!]!
}
id
ID!
The leadsheet ID. Pass it to Leadsheets.sheets(id:) to fetch this sheet alone. The ID encodes the form name and shard index (for example leadsheets/schedule_b/0).
formName
String!
The tax form this leadsheet covers, for example "Schedule B" or "Form 1040".
category
String!
The grouping category the binder assigns this form to (for example income or deductions).
issueCount
IssueCountBySeverity!
Issue counts broken down by severity. See IssueCountBySeverity.
issues
[LeadsheetSheetIssue!]!
The issues the run flagged on this sheet. See LeadsheetSheetIssue.
signOffs
[DocumentMessage!]!
Sign-off messages reviewers have recorded against this sheet. Each entry is a DocumentMessage with markType: "signoff" (see Sign off on a sheet or row). A sheet with no sign-offs returns an empty array.
fields
[LeadsheetField!]!
The form’s fields, each with its rows of values, prior-year values, source anchors, and traces. See LeadsheetField.

The LeadsheetSheetIssue type

One issue the run flagged on a sheet: a mismatch, a missing form, a value the extractor was not confident about, and so on.
type LeadsheetSheetIssue {
  id: String!
  ruleId: String!
  severity: Severity!
  category: String!
  title: String!
  description: String
  evidence: String
  expectedValue: String
  actualValue: String
  fieldPath: String
  lineRef: String
  columnRef: String
  mappingNote: String
  binderMessageId: String
  resolved: Boolean!
}

enum Severity {
  CRITICAL
  HIGH
  MEDIUM
  LOW
}
id
String!
The issue’s unique identifier.
ruleId
String!
The rule that fired this issue. Stable across runs of the same rule set, so you can use it to deduplicate or track an issue across re-runs.
severity
Severity!
How blocking the issue is: CRITICAL, HIGH, MEDIUM, or LOW.
category
String!
A machine-readable grouping, for example value_mismatch or missing_form.
title
String!
A short, human-readable summary of the issue.
description
String
A longer explanation. Nullable: some rules only emit a title.
evidence
String
The evidence the rule used, for example the binder text the value was extracted from.
expectedValue
String
What the rule expected to find, when relevant.
actualValue
String
What the rule actually found, when relevant.
fieldPath
String
The leadsheet field path the issue is anchored to, when the issue is tied to a specific field.
lineRef
String
A reference to the line on the form, when relevant.
columnRef
String
A reference to the column on the form, when relevant.
mappingNote
String
A note about how the issue’s field was mapped to the form, when the mapping is ambiguous.
binderMessageId
String
The ID of the binder message (annotation, flag, or sign-off) linked to this issue, when one exists.
resolved
Boolean!
Whether the issue has been resolved. An issue is resolved when the underlying document message is hidden (for example a reviewer dismissed the flag, or a sign-off covered it). Refetch the leadsheets query after a sign-off to recompute this.

The LeadsheetField type

A single field on a form, with one row per occurrence (for example one row per 1099-INT under “Interest Income”).
type LeadsheetField {
  id: ID!
  rows: [LeadsheetFieldRow!]!
}
id
ID!
The field ID.
rows
[LeadsheetFieldRow!]!
The rows for this field. See LeadsheetFieldRow.

The LeadsheetFieldRow type

One row of a field: its value, the prior-year value, the source anchor in the binder, the trace that explains where the value came from, and the issues and sign-offs tied to this row.
type LeadsheetFieldRow {
  id: ID!
  fieldPath: String!
  value: String
  priorYearValue: String
  sourceAnchor: SubDocBBox
  trace: LeadsheetTrace
  issues: [DocumentMessage!]!
  signOffs: [DocumentMessage!]!
}
id
ID!
The row ID.
fieldPath
String!
The path of this field on the form, for example interest_income.total.
value
String
The value extracted for this row, for example "428.00". Nullable when the row exists for layout but carries no value.
priorYearValue
String
The value the same field held in the prior year, when prior-year data is available.
sourceAnchor
SubDocBBox
The bounding box in the binder subdocument this value was extracted from. See SubDocBBox.
trace
LeadsheetTrace
The trace explaining how this row’s value was sourced and reconciled. See LeadsheetTrace.
issues
[DocumentMessage!]!
Document messages (flags, notes) anchored to this row. Each is a DocumentMessage; see Document messages.
signOffs
[DocumentMessage!]!
Sign-off messages reviewers have recorded against this row. Each is a DocumentMessage with markType: "signoff". See Sign off on a sheet or row.

The LeadsheetTrace type

The reasoning and source citations behind a row’s value: why the extractor chose this value, and which binder subdocuments (and pages, and bounding boxes) it came from.
type LeadsheetTrace {
  reasoning: String
  sources: [LeadsheetTraceSource!]!
}
reasoning
String
A human-readable explanation of how the value was sourced and reconciled.
sources
[LeadsheetTraceSource!]!
The binder sources this value was taken from. See LeadsheetTraceSource.

The LeadsheetTraceSource type

One source contributing to a trace: a subdocument, a label, an amount, and a page-level bounding box.
type LeadsheetTraceSource {
  subdocId: ID!
  label: String!
  amount: String
  page: Int
  bbox: SubDocBBox
}
subdocId
ID!
The binder subdocument this source came from.
label
String!
A human-readable label for the source, for example "1099-INT from Acme Broker".
amount
String
The amount this source contributed, as a string, for example "42.00".
page
Int
The page number inside the subdocument, when relevant.
bbox
SubDocBBox
The bounding box on the page that pins this source. See SubDocBBox.

The SubDocBBox type

A bounding box that pins a value or source to a specific region on a specific page of a binder subdocument.
type SubDocBBox {
  yMin: Int!
  xMin: Int!
  yMax: Int!
  xMax: Int!
  pageNumber: Int!
  subdocId: String!
}
yMin
Int!
Top edge of the box, in page pixels.
xMin
Int!
Left edge of the box, in page pixels.
yMax
Int!
Bottom edge of the box, in page pixels.
xMax
Int!
Right edge of the box, in page pixels.
pageNumber
Int!
The page this box is on, 1-indexed.
subdocId
String!
The subdocument this box belongs to.

The IssueCountBySeverity type

Issue counts bucketed by severity, used by Leadsheet.issueCount.
type IssueCountBySeverity {
  critical: Int!
  high: Int!
  medium: Int!
  low: Int!
}
critical
Int!
Number of CRITICAL issues.
high
Int!
Number of HIGH issues.
medium
Int!
Number of MEDIUM issues.
low
Int!
Number of LOW issues.

The DocumentMessage type

A document message is the underlying write surface for annotations, flags, and sign-offs on binder documents and leadsheet rows. A sign-off is a DocumentMessage with type: "activity" and markType: "signoff".
type DocumentMessage {
  id: ID!
  workspaceId: ID!
  documentPath: String!
  type: DocumentMessageType!
  markType: String!
  anchorPoint: JSON!
  contentPath: String
  body: String
  hiddenAt: String
  hiddenBy: ID
  createdBy: ID!
  createdAt: String!
  updatedAt: String!
  threads: [DocumentMessageThread!]!
  taggedUsers: [DocumentMessageTaggedUser!]!
}

enum DocumentMessageType {
  annotation
  activity
  missing_document
}
id
ID!
The message ID.
workspaceId
ID!
The workspace the message belongs to.
documentPath
String!
The binder path the message is anchored to. For a sign-off on a subdocument or leadsheet row, this is the subdocument’s path.
type
DocumentMessageType!
annotation, activity, or missing_document. Sign-offs use activity.
markType
String!
The kind of mark: signoff, flag, note, and so on. The schema types this as a free-form String; the web app treats signoff as the sign-off mark.
anchorPoint
JSON!
A JSON object pinning the message to a location. For a sign-off it carries { page, coordinates: { x, y }, level, user_role }, where level is the reviewer’s sign-off level and user_role is their workspace role.
contentPath
String
An optional content path.
body
String
An optional body, for notes and replies.
hiddenAt
String
When the message was soft-hidden (for example when a sign-off is undone). null while the message is visible.
hiddenBy
ID
The user who hid the message, when applicable.
createdBy
ID!
The user who created the message.
createdAt
String!
When the message was created.
updatedAt
String!
When the message was last updated.
threads
[DocumentMessageThread!]!
Reply threads on the message.
taggedUsers
[DocumentMessageTaggedUser!]!
Users tagged on the message.
A sign-off is a DocumentMessage with type: "activity" and markType: "signoff". This page documents only the two operations the review sign-off flow uses: createDocumentMessage and hideDocumentMessage. The wider DocumentMessage API (annotations, flags, threads, hide/unhide on binder documents) is documented separately.

Read a client’s leadsheets

Read binder.leadsheets(taskId:) to get the leadsheets tree a specific run produced. Pass the taskId of the TAX_PREP or TAX_REVIEW task you want the tree for; omit it to read the client’s most recent tree.
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
                }
                signOffs {
                  id
                  markType
                  anchorPoint
                  body
                  createdBy
                  createdAt
                  hiddenAt
                }
                fields {
                  id
                  rows {
                    id
                    fieldPath
                    value
                    priorYearValue
                    sourceAnchor {
                      yMin
                      xMin
                      yMax
                      xMax
                      pageNumber
                      subdocId
                    }
                    trace {
                      reasoning
                      sources {
                        subdocId
                        label
                        amount
                        page
                        bbox {
                          yMin
                          xMin
                          yMax
                          xMax
                          pageNumber
                          subdocId
                        }
                      }
                    }
                    issues {
                      id
                      markType
                      anchorPoint
                      body
                      createdBy
                      createdAt
                      hiddenAt
                    }
                    signOffs {
                      id
                      markType
                      anchorPoint
                      body
                      createdBy
                      createdAt
                      hiddenAt
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

Arguments

clientId
ID!
required
The client whose leadsheets you want to read. Pass it via filters.ids on clients.
taskId
ID
Optional. The TAX_PREP or TAX_REVIEW task whose tree you want. Omit it to read the client’s most recent tree.
sheets.id
ID
Optional. Pass it on Leadsheets.sheets(id:) to fetch a single leadsheet by ID instead of listing them all.
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 } signOffs { id markType anchorPoint body createdBy createdAt hiddenAt } fields { id rows { id fieldPath value priorYearValue sourceAnchor { yMin xMin yMax xMax pageNumber subdocId } trace { reasoning sources { subdocId label amount page bbox { yMin xMin yMax xMax pageNumber subdocId } } } issues { id markType anchorPoint body createdBy createdAt hiddenAt } signOffs { id markType anchorPoint body createdBy createdAt hiddenAt } } } } } } } } } } } }",
    "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
                    },
                    "signOffs": [],
                    "fields": [
                      {
                        "id": "leadsheets/schedule_b/0/interest_income",
                        "rows": [
                          {
                            "id": "leadsheets/schedule_b/0/interest_income/0",
                            "fieldPath": "interest_income.total",
                            "value": "428.00",
                            "priorYearValue": "386.00",
                            "sourceAnchor": {
                              "yMin": 412,
                              "xMin": 88,
                              "yMax": 428,
                              "xMax": 220,
                              "pageNumber": 1,
                              "subdocId": "018f9c2a-7b1e-7c3d-9a4e-2f6b1c8d0e5a"
                            },
                            "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,
                                  "bbox": {
                                    "yMin": 412,
                                    "xMin": 88,
                                    "yMax": 428,
                                    "xMax": 220,
                                    "pageNumber": 1,
                                    "subdocId": "018f9c2a-7b1e-7c3d-9a4e-2f6b1c8d0e5a"
                                  }
                                },
                                {
                                  "subdocId": "018f9c2a-7c3d-7c3d-9a4e-2f6b1c8d2f7b",
                                  "label": "1099-INT from Globex",
                                  "amount": "176.00",
                                  "page": 1,
                                  "bbox": {
                                    "yMin": 300,
                                    "xMin": 88,
                                    "yMax": 316,
                                    "xMax": 220,
                                    "pageNumber": 1,
                                    "subdocId": "018f9c2a-7c3d-7c3d-9a4e-2f6b1c8d2f7b"
                                  }
                                },
                                {
                                  "subdocId": "018f9c2a-8e2f-7c3d-9a4e-2f6b1c8d3f8c",
                                  "label": "1099-INT from Initech",
                                  "amount": "42.00",
                                  "page": 1,
                                  "bbox": {
                                    "yMin": 244,
                                    "xMin": 88,
                                    "yMax": 260,
                                    "xMax": 220,
                                    "pageNumber": 1,
                                    "subdocId": "018f9c2a-8e2f-7c3d-9a4e-2f6b1c8d3f8c"
                                  }
                                }
                              ]
                            },
                            "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
                              }
                            ],
                            "signOffs": []
                          }
                        ]
                      }
                    ]
                  }
                ]
              }
            }
          }
        ]
      }
    }
  }
}
The leadsheets query is the same one the web app’s binder and review screens run. After a sign-off, refetch it to recompute LeadsheetSheetIssue.resolved and Leadsheet.issueCount (the server recomputes them from the document messages you just wrote).

Sign off on a sheet or row

A sign-off is a createDocumentMessage call with type: "activity" and markType: "signoff", anchored to the subdocument path you are signing off on. The anchorPoint carries the reviewer’s level and user_role so the UI can render the sign-off with the right label.
mutation CreateDocumentMessage($input: CreateDocumentMessageInput!) {
  createDocumentMessage(input: $input) {
    id
    documentPath
    type
    markType
    anchorPoint
    body
    createdBy
    createdAt
    hiddenAt
  }
}

Input: CreateDocumentMessageInput

input CreateDocumentMessageInput {
  clientId: ID!
  documentPath: String!
  type: DocumentMessageType!
  markType: String!
  anchorPoint: JSON!
  body: String
  taskId: ID
  taggedUserIds: [ID!]
}
clientId
ID!
required
The client whose binder you are signing off in.
documentPath
String!
required
The binder path you are signing off on. For a subdocument or leadsheet row sign-off, this is the subdocument’s path (the same value you read as LeadsheetFieldRow.id or LeadsheetSheetIssue is anchored to).
type
DocumentMessageType!
required
activity for a sign-off (the only value the sign-off flow uses).
markType
String!
required
"signoff" for a sign-off.
anchorPoint
JSON!
required
A JSON object pinning the sign-off. The web app uses { "page": 1, "coordinates": { "x": 0, "y": 0 }, "level": <number>, "user_role": "<role>" }, where level is the reviewer’s sign-off level (for example 2 for an l2 reviewer) and user_role is their workspace role.
body
String
An optional note attached to the sign-off.
taskId
ID
The task the sign-off belongs to, when relevant.
taggedUserIds
[ID!]
Workspace users to tag on the sign-off.

Returns: DocumentMessage!

The created DocumentMessage. Its id is what you pass to hideDocumentMessage to undo the sign-off.
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
    }
  }
}
The schema also defines an input type called SignOffSubDocumentsInput ({ binderId, subDocumentPaths }), but no mutation field is wired to it: there is no signOffSubDocuments (or similar) mutation on the live Mutation type. The real sign-off write surface is createDocumentMessage with markType: "signoff", one call per subdocument. Do not look for a signOffSubDocuments mutation, it does not exist.

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 / issueCount recomputation backs it out.
mutation HideDocumentMessage($id: ID!) {
  hideDocumentMessage(id: $id) {
    id
    documentPath
    type
    markType
    hiddenAt
    hiddenBy
  }
}

Input

id
ID!
required
The ID of the sign-off DocumentMessage to undo (the id returned by createDocumentMessage).

Returns: DocumentMessage!

The hidden DocumentMessage, with hiddenAt and hiddenBy now populated.
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"
    }
  }
}

Exporting leadsheets

The LeadsheetsExportFormat enum is used by the workpaper bundle generator (generateWorkpaperBundle), not by the leadsheets query itself. When you generate a workpaper bundle and want leadsheets included, pass includeLeadsheets: true and pick a format.
enum LeadsheetsExportFormat {
  EXCEL
  CSV
}
The generateWorkpaperBundle mutation and its GenerateWorkpaperBundleInput input are documented on the Workpapers page. This page lists LeadsheetsExportFormat only to anchor where the enum is actually consumed.