Skip to main content
Once a client’s binder has been browsed and the team is ready to package the work, the next step is to assemble a workpaper bundle: a single downloadable archive that combines the binder’s PDF, leadsheets, forms, checklist, and source documents in the order the firm wants. The bundle is rendered server-side from the binder, so you queue a render job and poll it until the download URL is ready. This recipe walks through the real call sequence an integration uses to generate, poll, and download a workpaper bundle, and the two optional operations around it. Every operation here is documented on the Workpapers reference page; this recipe sequences them rather than re-documenting the types. Everything uses a workspaceToken (see Authentication) and goes to the single GraphQL endpoint:
https://router.apps.filed.com/graphql
The bundle render does not use the Tasks polling pattern. It has its own workflowExecutionId and its own WorkpaperRenderStatus enum (RUNNING, COMPLETED, FAILED), reached through me { ... on WorkspaceUser { workspace { checkWorkpaperGenerationStatus(...) } } }. Do not conflate it with TaskStatus. The optional triggerWorkpaperTranslate flow at the end of this recipe is the one that does use the Task pattern.

1. Start a render

Call generateWorkpaperBundle with the client’s binderId, the orderedGroups outline that drives the bundle’s section order, and the flags for which sections to include. The mutation returns a WorkpaperRenderJob carrying the workflowExecutionId you poll in step 2.
mutation GenerateWorkpaperBundle($input: GenerateWorkpaperBundleInput!) {
  generateWorkpaperBundle(input: $input) {
    workflowExecutionId
    status
  }
}
{
  "input": {
    "binderId": "018f9c2a-4b6f-7a10-b2c4-9e8d7f6a5b4d",
    "orderedGroups": [
      {
        "bucketLabel": "Income",
        "categories": [
          {
            "category": "1099s",
            "subdocumentIds": [
              "018f9c2a-7b1e-7c3d-9a4e-2f6b1c8d0e5a",
              "018f9c2a-7b3d-7c3d-9a4e-2f6b1c8d2f7b"
            ]
          }
        ]
      },
      {
        "bucketLabel": "Deductions",
        "categories": [
          {
            "category": "Receipts",
            "subdocumentIds": [
              "018f9c2a-7c4d-7c3d-9a4e-2f6b1c8d3f8c"
            ]
          }
        ]
      }
    ],
    "clientName": "Jane Taxpayer",
    "includePdf": true,
    "includeLeadsheets": true,
    "leadsheetsFormat": "EXCEL",
    "includeForms": true,
    "includeChecklist": true,
    "includeSourceDocs": false
  }
}
The subdocumentIds in orderedGroups are the id values from binder.subdocuments. Run the browse the binder recipe first to collect them, then group them into the buckets and categories that match the firm’s preferred workpaper outline.
curl -X POST https://router.apps.filed.com/graphql \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_WORKSPACE_TOKEN" \
  -d '{
    "query": "mutation GenerateWorkpaperBundle($input: GenerateWorkpaperBundleInput!) { generateWorkpaperBundle(input: $input) { workflowExecutionId status } }",
    "variables": {
      "input": {
        "binderId": "018f9c2a-4b6f-7a10-b2c4-9e8d7f6a5b4d",
        "orderedGroups": [
          {
            "bucketLabel": "Income",
            "categories": [
              {
                "category": "1099s",
                "subdocumentIds": [
                  "018f9c2a-7b1e-7c3d-9a4e-2f6b1c8d0e5a",
                  "018f9c2a-7b3d-7c3d-9a4e-2f6b1c8d2f7b"
                ]
              }
            ]
          }
        ],
        "clientName": "Jane Taxpayer",
        "includePdf": true,
        "includeLeadsheets": true,
        "leadsheetsFormat": "EXCEL",
        "includeForms": true,
        "includeChecklist": true,
        "includeSourceDocs": false
      }
    }
  }'
{
  "data": {
    "generateWorkpaperBundle": {
      "workflowExecutionId": "018f9c2c-3d4e-7f50-9b66-7f7082901234",
      "status": "RUNNING"
    }
  }
}

2. Poll until the render completes

Poll checkWorkpaperGenerationStatus with the workflowExecutionId from step 1 until status is COMPLETED (then read downloadUrl.url) or FAILED (then read errorMessage). The web app polls every 1500ms with a hard timeout of 5 minutes; mirror that pattern and treat a missing downloadUrl on COMPLETED as a failure.
query CheckWorkpaperGenerationStatus($workflowExecutionId: ID!) {
  me {
    ... on WorkspaceUser {
      id
      workspace {
        id
        checkWorkpaperGenerationStatus(workflowExecutionId: $workflowExecutionId) {
          workflowExecutionId
          status
          downloadUrl {
            filePath
            url
          }
          generatedAt
          errorMessage
        }
      }
    }
  }
}
{
  "workflowExecutionId": "018f9c2c-3d4e-7f50-9b66-7f7082901234"
}
This poll pattern is distinct from the Task polling pattern. Workpaper renders use a workflowExecutionId and the WorkpaperRenderStatus enum (RUNNING, COMPLETED, FAILED) on a WorkpaperRender shape returned by Workspace.checkWorkpaperGenerationStatus. They do not use the Task / TaskStatus type, the clients.tasks list, or TaskResult unions. A workpaper render is not a Task and cannot be read through tasks(type:, status:).
curl -X POST https://router.apps.filed.com/graphql \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_WORKSPACE_TOKEN" \
  -d '{
    "query": "query CheckWorkpaperGenerationStatus($workflowExecutionId: ID!) { me { ... on WorkspaceUser { id workspace { id checkWorkpaperGenerationStatus(workflowExecutionId: $workflowExecutionId) { workflowExecutionId status downloadUrl { filePath url } generatedAt errorMessage } } } } }",
    "variables": {
      "workflowExecutionId": "018f9c2c-3d4e-7f50-9b66-7f7082901234"
    }
  }'
{
  "data": {
    "me": {
      "id": "019f0fb6-37b1-7800-b7bc-0d11288504b1",
      "workspace": {
        "id": "019f0fb6-3001-7900-b7bc-0d11288504b1",
        "checkWorkpaperGenerationStatus": {
          "workflowExecutionId": "018f9c2c-3d4e-7f50-9b66-7f7082901234",
          "status": "COMPLETED",
          "downloadUrl": {
            "filePath": "workpaper-bundles/018f9c2a-3d5f-7a10-b2c4-9e8d7f6a5b4c/2026-07-05.zip",
            "url": "https://signed.example.com/workpaper-bundles/018f9c2a-3d5f-7a10-b2c4-9e8d7f6a5b4c/2026-07-05.zip?token=..."
          },
          "generatedAt": "2026-07-05T14:10:11.000Z",
          "errorMessage": null
        }
      }
    }
  }
}
A RUNNING response (poll again) looks like:
{
  "data": {
    "me": {
      "id": "019f0fb6-37b1-7800-b7bc-0d11288504b1",
      "workspace": {
        "id": "019f0fb6-3001-7900-b7bc-0d11288504b1",
        "checkWorkpaperGenerationStatus": {
          "workflowExecutionId": "018f9c2c-3d4e-7f50-9b66-7f7082901234",
          "status": "RUNNING",
          "downloadUrl": null,
          "generatedAt": null,
          "errorMessage": null
        }
      }
    }
  }
}
And a FAILED response (stop polling and surface the error) looks like:
{
  "data": {
    "me": {
      "id": "019f0fb6-37b1-7800-b7bc-0d11288504b1",
      "workspace": {
        "id": "019f0fb6-3001-7900-b7bc-0d11288504b1",
        "checkWorkpaperGenerationStatus": {
          "workflowExecutionId": "018f9c2c-3d4e-7f50-9b66-7f7082901234",
          "status": "FAILED",
          "downloadUrl": null,
          "generatedAt": null,
          "errorMessage": "Subdocument 018f9c2a-7b1e-7c3d-9a4e-2f6b1c8d0e5a is no longer in the binder."
        }
      }
    }
  }
}

3. Download the bundle

Once status is COMPLETED, fetch downloadUrl.url (or hand it to the browser to download). The url is a signed link with a limited lifetime, so download it promptly. downloadUrl.filePath shows where the bundle is stored server-side and is useful for support tickets but is not a fetch target itself.
curl -L -o jane-taxpayer-workpapers.zip \
  "https://signed.example.com/workpaper-bundles/018f9c2a-3d5f-7a10-b2c4-9e8d7f6a5b4c/2026-07-05.zip?token=..."
The web app opens downloadUrl.url in a new tab to trigger the browser’s download prompt; either approach works.

Optional: Save an edited workpaper xlsx

If your integration edits the client’s tax_workpaper.xlsx (for example an in-browser workbook editor), commit each edit back with saveTaxWorkpaperXlsx. It takes the full xlsx workbook base64-encoded plus a short summary (typically the cell coordinate that changed) and returns a ClientCommit describing the new git commit in the client’s file store.
mutation SaveTaxWorkpaperXlsx($input: SaveTaxWorkpaperXlsxInput!) {
  saveTaxWorkpaperXlsx(input: $input) {
    sha
    shortSha
    message
    committedAt
  }
}
{
  "input": {
    "clientId": "018f9c2a-3d5f-7a10-b2c4-9e8d7f6a5b4c",
    "xlsxBase64": "UEsDBBQACAgIAAAAAAAAAAAAAAAAAAAAAAA=",
    "summary": "Trial Balance!B12"
  }
}
curl -X POST https://router.apps.filed.com/graphql \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_WORKSPACE_TOKEN" \
  -d '{
    "query": "mutation SaveTaxWorkpaperXlsx($input: SaveTaxWorkpaperXlsxInput!) { saveTaxWorkpaperXlsx(input: $input) { sha shortSha message committedAt } }",
    "variables": {
      "input": {
        "clientId": "018f9c2a-3d5f-7a10-b2c4-9e8d7f6a5b4c",
        "xlsxBase64": "UEsDBBQACAgIAAAAAAAAAAAAAAAAAAAAAAA=",
        "summary": "Trial Balance!B12"
      }
    }
  }'
{
  "data": {
    "saveTaxWorkpaperXlsx": {
      "sha": "7c4d5e6f7081901a2b3c4d5e6f7081901a2b3c4d",
      "shortSha": "7c4d5e6",
      "message": "Edit Trial Balance!B12",
      "committedAt": "2026-07-05T14:08:22.000Z"
    }
  }
}

Optional: Trigger a translation task

When you want a fresh workpaper produced for a return type from a prior run’s output, call triggerWorkpaperTranslate. Unlike the bundle render, this is a real background Task: it returns a TriggerTaskResult { taskId } and you poll me { ... on WorkspaceUser { workspace { clients(filters: { ids: [$clientId] }) { tasks(type: ..., limit: 1) { id status } } } } } until status is COMPLETED or FAILED.
mutation TriggerWorkpaperTranslate($input: TriggerWorkpaperTranslateInput!) {
  triggerWorkpaperTranslate(input: $input) {
    taskId
  }
}
{
  "input": {
    "clientId": "018f9c2a-3d5f-7a10-b2c4-9e8d7f6a5b4c",
    "returnType": "F1120",
    "templateName": "default"
  }
}
curl -X POST https://router.apps.filed.com/graphql \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_WORKSPACE_TOKEN" \
  -d '{
    "query": "mutation TriggerWorkpaperTranslate($input: TriggerWorkpaperTranslateInput!) { triggerWorkpaperTranslate(input: $input) { taskId } }",
    "variables": {
      "input": {
        "clientId": "018f9c2a-3d5f-7a10-b2c4-9e8d7f6a5b4c",
        "returnType": "F1120",
        "templateName": "default"
      }
    }
  }'
{
  "data": {
    "triggerWorkpaperTranslate": {
      "taskId": "018f9c2c-4e5f-7f60-9c77-8082901234ab"
    }
  }
}
List the available template names for a return type with workpaperTemplates:
query WorkpaperTemplates($returnType: ReturnType!) {
  me {
    ... on WorkspaceUser {
      id
      workspace {
        id
        workpaperTemplates(returnType: $returnType)
      }
    }
  }
}
{ "returnType": "F1120" }
curl -X POST https://router.apps.filed.com/graphql \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_WORKSPACE_TOKEN" \
  -d '{
    "query": "query WorkpaperTemplates($returnType: ReturnType!) { me { ... on WorkspaceUser { id workspace { id workpaperTemplates(returnType: $returnType) } } } }",
    "variables": { "returnType": "F1120" }
  }'
{
  "data": {
    "me": {
      "id": "019f0fb6-37b1-7800-b7bc-0d11288504b1",
      "workspace": {
        "id": "019f0fb6-3001-7900-b7bc-0d11288504b1",
        "workpaperTemplates": ["default", "detailed"]
      }
    }
  }
}
As of this writing, triggerWorkpaperTranslate and workpaperTemplates are not yet wired into the web app’s surface code. They exist in the live schema and are documented here for completeness, but verify the behavior end to end against your own workspace before relying on a specific shape.

Next steps

From here you can go deeper on the related surfaces:
  • Browse the binder first: the subdocumentIds that drive orderedGroups come from binder.subdocuments. See Browse a client’s binder.
  • Leadsheets export format: when includeLeadsheets is true, set leadsheetsFormat to EXCEL or CSV (see Exporting leadsheets).
  • Task polling (for the translate flow): the optional triggerWorkpaperTranslate flow returns a taskId you poll through the standard clients.tasks pattern. See Tasks.
  • Full reference: every workpaper mutation, type, and Workspace field is documented on Workpapers.