> ## Documentation Index
> Fetch the complete documentation index at: https://docs.apps.filed.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Run tax planning / advisory

> Start a tax advisor run for a client, poll it to completion, read the plan, and update a strategy's status

Tax planning (the advisor) reads a client's binder and produces an `AdvisorPlan`:
a global summary plus a list of `AdvisorStrategy` recommendations, each with
evidence, an implementation plan, an optional savings estimate, and a `status`
you can drive. It runs as a background task, so the end-to-end sequence is:
start the run, poll the task until it finishes, read the plan, then accept or
dismiss each strategy. This recipe is the minimal call sequence to do that for
one client.

Every call uses a **`workspaceToken`** (see [Authentication](/guides/authentication))
and goes to the single GraphQL endpoint:

```
https://router.apps.filed.com/graphql
```

```mermaid theme={null}
flowchart LR
  A["Client with<br/>ingested binder"] -->|"initiateTaxAdvisor"| B["TAX_ADVISOR task"]
  A2["Documents<br/>already ingested"] -->|"triggerTaxAdvisor"| B
  B -->|"poll tasks(type: TAX_ADVISOR)"| C{"status"}
  C -->|RUNNING| B
  C -->|COMPLETED| D["advisorPlan"]
  D -->|strategies| E["Each AdvisorStrategy"]
  E -->|"setAdvisorStrategyStatus"| F["PROPOSED / SELECTED / DISMISSED"]
```

This recipe does not re-document the types it touches. For the full
`AdvisorPlan` and `AdvisorStrategy` field lists, the second trigger path, the
task result member, and the `SavingsHorizon` enum, see
[Tax planning](/apis/planning). For the polling mechanics, see
[Tasks](/apis/tasks).

## 1. Start the run

There are two trigger mutations for an advisor run, both requiring a
**`workspaceToken`** and both returning a `taskId` you poll as a `TAX_ADVISOR`
task. Use the one that matches how you stage documents.

The Filed web app's `/planning` route uses
[`initiateTaxAdvisor`](/apis/planning#trigger-via-initiatetaxadvisor) because
the in-app flow stages fresh uploads at the same moment it kicks off the run.
Recommend it as the primary path: upload files first (see
[Uploading documents](/guides/uploading-documents)) to get `uploadIds`, then
call the mutation.

```graphql theme={null}
mutation InitiateTaxAdvisor($input: InitiateTaxAdvisorInput!) {
  initiateTaxAdvisor(input: $input) {
    taskId
  }
}
```

```json theme={null}
{
  "input": {
    "clientId": "018f9c2a-3d5f-7a10-b2c4-9e8d7f6a5b4c",
    "uploadIds": ["018f9c2a-7b1e-7c3d-9a4e-2f6b1c8d0e5a"]
  }
}
```

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST https://router.apps.filed.com/graphql \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer YOUR_WORKSPACE_TOKEN" \
    -d '{
      "query": "mutation InitiateTaxAdvisor($input: InitiateTaxAdvisorInput!) { initiateTaxAdvisor(input: $input) { taskId } }",
      "variables": {
        "input": {
          "clientId": "018f9c2a-3d5f-7a10-b2c4-9e8d7f6a5b4c",
          "uploadIds": ["018f9c2a-7b1e-7c3d-9a4e-2f6b1c8d0e5a"]
        }
      }
    }'
  ```
</RequestExample>

<ResponseExample>
  ```json theme={null}
  {
    "data": {
      "initiateTaxAdvisor": {
        "taskId": "018f9c2c-4a1b-7e20-8b33-7c4d5e6f7080"
      }
    }
  }
  ```
</ResponseExample>

<Note>
  `InitiateTaxAdvisorResult.taskId` is nullable. A `null` value means the
  ingestion accepted the upload but did not start a task; treat it as a soft
  error and retry. See [Tax planning, trigger via initiateTaxAdvisor](/apis/planning#trigger-via-initiatetaxadvisor).
</Note>

<Tip>
  If the client's binder is already populated and you do not need to stage fresh
  uploads, use
  [`triggerTaxAdvisor(input: { clientId, returnType, taxYear })`](/apis/planning#trigger-via-triggertaxadvisor)
  instead. It returns a non-null `TriggerTaskResult.taskId`. To scope which
  workspace and user skills apply to this run, pass
  `skills: { workspace: [...], user: [...] }` (`RunSkillSelectionInput`) on
  either mutation; omit it to apply all active skills.
</Tip>

Save the `taskId`. You will use it to find the task in the poll step.

## 2. Poll the task to completion

There is no `task(id:)` query. Poll the task you just started by listing the
client's `TAX_ADVISOR` tasks and reading the entry whose `id` matches the
`taskId` returned above. The polling mechanics are documented on
[Tasks](/apis/tasks#check-a-single-tasks-status); the short version:

```graphql theme={null}
query PollTaxAdvisor($clientId: ID!) {
  me {
    ... on WorkspaceUser {
      workspace {
        clients(filters: { ids: [$clientId] }) {
          tasks(type: TAX_ADVISOR, limit: 1) {
            id
            status
            startedAt
            completedAt
            errorMessage
            subTasks {
              type
              status
            }
          }
        }
      }
    }
  }
}
```

```json theme={null}
{
  "clientId": "018f9c2a-3d5f-7a10-b2c4-9e8d7f6a5b4c"
}
```

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST https://router.apps.filed.com/graphql \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer YOUR_WORKSPACE_TOKEN" \
    -d '{
      "query": "query PollTaxAdvisor($clientId: ID!) { me { ... on WorkspaceUser { workspace { clients(filters: { ids: [$clientId] }) { tasks(type: TAX_ADVISOR, limit: 1) { id status startedAt completedAt errorMessage subTasks { type status } } } } } } }",
      "variables": { "clientId": "018f9c2a-3d5f-7a10-b2c4-9e8d7f6a5b4c" }
    }'
  ```
</RequestExample>

<ResponseExample>
  ```json theme={null}
  {
    "data": {
      "me": {
        "workspace": {
          "clients": [
            {
              "tasks": [
                {
                  "id": "018f9c2c-4a1b-7e20-8b33-7c4d5e6f7080",
                  "status": "RUNNING",
                  "startedAt": "2026-07-04T11:02:00.000Z",
                  "completedAt": null,
                  "errorMessage": null,
                  "subTasks": [
                    { "type": "BUILD_ADVISOR_MANIFEST", "status": "COMPLETED" },
                    { "type": "RUN_ADVISOR_AGENT", "status": "RUNNING" }
                  ]
                }
              ]
            }
          ]
        }
      }
    }
  }
  ```
</ResponseExample>

Poll on an interval (for example every few seconds) until `status` is no
longer `RUNNING`. `COMPLETED` means the run succeeded and `advisorPlan` is now
readable; `FAILED` means it did not, and `errorMessage` (plus
`subTasks[].errorMessage`) explains which stage failed.

## 3. Read the plan

Read the plan through `Client.advisorPlan`. There is no top-level `advisorPlan`
query; reach it through `me { ... on WorkspaceUser { workspace { clients(...) {
advisorPlan } } } }` (see [Clients](/apis/clients#the-client-type)). Call it
without a `runId` to read the client's current plan, or pass the `runId` from
the completed task to read that run's plan. This recipe passes `runId` to pin
the read to the run you just polled.

```graphql theme={null}
query ClientAdvisorPlan($clientId: ID!, $runId: ID) {
  me {
    ... on WorkspaceUser {
      workspace {
        clients(filters: { ids: [$clientId] }) {
          id
          advisorPlan(runId: $runId) {
            runId
            globalSummary
            strategies {
              id
              domain
              title
              summary
              estimatedSavingsCents
              savingsHorizon
              status
            }
          }
        }
      }
    }
  }
}
```

```json theme={null}
{
  "clientId": "018f9c2a-3d5f-7a10-b2c4-9e8d7f6a5b4c",
  "runId": "018f9c2c-4a1b-7e20-8b33-7c4d5e6f7080"
}
```

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST https://router.apps.filed.com/graphql \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer YOUR_WORKSPACE_TOKEN" \
    -d '{
      "query": "query ClientAdvisorPlan($clientId: ID!, $runId: ID) { me { ... on WorkspaceUser { workspace { clients(filters: { ids: [$clientId] }) { id advisorPlan(runId: $runId) { runId globalSummary strategies { id domain title summary estimatedSavingsCents savingsHorizon status } } } } } } }",
      "variables": {
        "clientId": "018f9c2a-3d5f-7a10-b2c4-9e8d7f6a5b4c",
        "runId": "018f9c2c-4a1b-7e20-8b33-7c4d5e6f7080"
      }
    }'
  ```
</RequestExample>

<ResponseExample>
  ```json theme={null}
  {
    "data": {
      "me": {
        "workspace": {
          "clients": [
            {
              "id": "018f9c2a-3d5f-7a10-b2c4-9e8d7f6a5b4c",
              "advisorPlan": {
                "runId": "018f9c2c-4a1b-7e20-8b33-7c4d5e6f7080",
                "globalSummary": "5 strategies surfaced across retirement, income shifting, and entity selection. Estimated 3-year savings of $18,400.",
                "strategies": [
                  {
                    "id": "019a1b2c-3d4e-7f10-aa12-1c2d3e4f5060",
                    "domain": "charitable",
                    "title": "Bunch charitable contributions into 2025",
                    "summary": "Combine two years of charitable giving into 2025 to exceed the standard deduction and itemize this year.",
                    "estimatedSavingsCents": 82000,
                    "savingsHorizon": "CURRENT_YEAR",
                    "status": "PROPOSED"
                  },
                  {
                    "id": "019a1b2c-3d4e-7f10-aa12-1c2d3e4f5061",
                    "domain": "retirement",
                    "title": "Roth convert up to the 24% bracket cap",
                    "summary": "Convert traditional IRA funds to Roth up to the top of the 24% bracket this year.",
                    "estimatedSavingsCents": null,
                    "savingsHorizon": "MULTI_YEAR",
                    "status": "PROPOSED"
                  }
                ]
              }
            }
          ]
        }
      }
    }
  }
  ```
</ResponseExample>

<Note>
  `advisorPlan` returns `null` while the run is still `RUNNING`, or when the
  client has no advisor run yet. Treat `null` as "no plan to show", and keep
  polling the task until `status` is `COMPLETED` before re-reading. For the full
  `AdvisorPlan` field list (including `taxYear`, `returnType`, `byDomain`,
  `bySavingsHorizon`, `estimatedSavingsCentsByHorizon`, and `skillsApplied`), see
  [Tax planning, read the plan](/apis/planning#read-the-plan).
</Note>

## 4. Update a strategy's status

Drive each strategy's workflow status with
[`setAdvisorStrategyStatus`](/apis/planning#update-a-strategys-status). It
requires a **`workspaceToken`** and identifies the strategy with `clientId`
plus the strategy's `domain` and `strategyId` (both from `AdvisorStrategy`),
plus the plan's `runId` to pin the change to the right run.

The real enum values are `PROPOSED`, `SELECTED`, and `DISMISSED`. Use
`SELECTED` for strategies the firm accepts, `DISMISSED` for those it rejects,
and `PROPOSED` to revert either back to the advisor's original state. There is
no `ACCEPTED` value; "accept" maps to `SELECTED`.

```graphql theme={null}
mutation SetAdvisorStrategyStatus($input: SetAdvisorStrategyStatusInput!) {
  setAdvisorStrategyStatus(input: $input) {
    id
    status
  }
}
```

```json theme={null}
{
  "input": {
    "clientId": "018f9c2a-3d5f-7a10-b2c4-9e8d7f6a5b4c",
    "domain": "charitable",
    "strategyId": "accelerate_charitable_contributions",
    "runId": "018f9c2c-4a1b-7e20-8b33-7c4d5e6f7080",
    "status": "SELECTED"
  }
}
```

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST https://router.apps.filed.com/graphql \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer YOUR_WORKSPACE_TOKEN" \
    -d '{
      "query": "mutation SetAdvisorStrategyStatus($input: SetAdvisorStrategyStatusInput!) { setAdvisorStrategyStatus(input: $input) { id status } }",
      "variables": {
        "input": {
          "clientId": "018f9c2a-3d5f-7a10-b2c4-9e8d7f6a5b4c",
          "domain": "charitable",
          "strategyId": "accelerate_charitable_contributions",
          "runId": "018f9c2c-4a1b-7e20-8b33-7c4d5e6f7080",
          "status": "SELECTED"
        }
      }
    }'
  ```
</RequestExample>

<ResponseExample>
  ```json theme={null}
  {
    "data": {
      "setAdvisorStrategyStatus": {
        "id": "019a1b2c-3d4e-7f10-aa12-1c2d3e4f5060",
        "status": "SELECTED"
      }
    }
  }
  ```
</ResponseExample>

<Tip>
  After a successful `setAdvisorStrategyStatus`, re-read `advisorPlan` (step 3) to
  get the refreshed `strategies[].status` values. The Filed web app does this by
  including `ClientAdvisorPlan` in the mutation's `refetchQueries`. See
  [Tax planning, update a strategy's status](/apis/planning#update-a-strategys-status).
</Tip>

## Next steps

Once you have accepted (`SELECTED`) the strategies you want to act on, the
natural next steps are to work through each strategy's `implementationPlan` and
to re-run tax prep so the accepted planning changes flow into the prepared
return. See [Run tax prep end to end](/guides/recipes/run-tax-prep) for that
recipe, and [Tax planning](/apis/planning) for the full reference.
