> ## 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.

# Quickstart

> Go from zero to a processed client in five steps: authenticate, upload, create, and poll

This quickstart walks a new integrator through the golden path: from an API key
to a client whose documents have been ingested into its binder. Each step links to
the detailed reference page for full type detail rather than duplicating it.

All requests go to:

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

Uploads (step 3) go to a separate host:

```
https://web.apps.filed.com/api/uploads
```

<Steps>
  ## Get a token

  Create a workspace-scoped API key in the Filed web app and exchange it for a
  short-lived `workspaceToken`. This is the only call you make without a Bearer
  token.

  ```bash cURL theme={null}
  curl -X POST https://router.apps.filed.com/graphql \
    -H "Content-Type: application/json" \
    -d '{
      "query": "mutation ExchangeApiKey($apiKey: String!) { exchangeSurfaceRefreshTokenForAccessTokens(refreshToken: $apiKey) { userToken workspaceToken } }",
      "variables": { "apiKey": "YOUR_API_KEY" }
    }'
  ```

  Save the returned `workspaceToken`; you send it as `Authorization: Bearer
    YOUR_WORKSPACE_TOKEN` on every later request. Full details, including access
  levels and key expiry, are in [Authentication](/guides/authentication).

  ## Confirm it works

  Run [`me`](/apis/me) with the `workspaceToken` to confirm the token works and to
  see which workspace it is scoped to. `me` returns the `Me` union, which resolves
  to `WorkspaceUser` for a `workspaceToken`, so select fields with an inline
  fragment:

  ```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 Me { me { __typename ... on WorkspaceUser { id role user { id name email } workspace { id name } } } }" }'
  ```

  A successful response shows your workspace id and name. If you get an
  `UNAUTHENTICATED` error, re-exchange your API key for a fresh token (they last
  about 30 minutes).

  ## Upload a document

  Filed does not accept file bytes over GraphQL. Stage each file with the resumable
  [tus](https://tus.io) upload endpoint first, then pass the returned upload ID to
  `createClient` (next step). A minimal upload is two requests: a `POST` that
  creates the upload and returns its location, then a `PATCH` that sends the bytes.

  ```bash cURL theme={null}
  # 1. Create the upload. Metadata values are base64-encoded.
  #    filename=w2_1040.pdf  filetype=application/pdf  intent=client-document
  LOCATION=$(curl -sS -D - -o /dev/null -X POST https://web.apps.filed.com/api/uploads \
    -H "Authorization: Bearer YOUR_WORKSPACE_TOKEN" \
    -H "Tus-Resumable: 1.0.0" \
    -H "Upload-Length: $(wc -c < w2_1040.pdf)" \
    -H "Upload-Metadata: filename dzJfMTA0MC5wZGY=,filetype YXBwbGljYXRpb24vcGRm,intent Y2xpZW50LWRvY3VtZW50" \
    | tr -d '\r' | awk '/^Location:/ {print $2}')

  UPLOAD_ID="${LOCATION##*/}"

  # 2. Send the bytes.
  curl -sS -X PATCH "$LOCATION" \
    -H "Authorization: Bearer YOUR_WORKSPACE_TOKEN" \
    -H "Tus-Resumable: 1.0.0" \
    -H "Upload-Offset: 0" \
    -H "Content-Type: application/offset+octet-stream" \
    --data-binary @w2_1040.pdf
  ```

  The **upload ID is the last path segment** of the upload's `Location` URL. Keep
  it for the next step. Full tus details, including chunking and retries, are in
  [Uploading documents](/guides/uploading-documents).

  ## Create a client with the document

  Pass the upload ID to [`createClient`](/apis/clients#create-a-client) to create a
  client and kick off binder ingestion in one call. The mutation returns
  `{ client { id }, taskId }`: the `client.id` identifies the new client, and the
  `taskId` is the binder ingestion task you poll next.

  ```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 CreateClient($input: CreateClientInput!) { createClient(input: $input) { client { id name externalId status returnType taxYear } taskId } }",
      "variables": {
        "input": {
          "name": "Jane Taxpayer",
          "externalId": "PMS-10432",
          "returnType": "F1040",
          "taxYear": 2025,
          "uploadIds": ["YOUR_UPLOAD_ID"]
        }
      }
    }'
  ```

  ```json theme={null}
  {
    "data": {
      "createClient": {
        "client": {
          "id": "018f9c2a-3d5f-7a10-b2c4-9e8d7f6a5b4c",
          "name": "Jane Taxpayer",
          "externalId": "PMS-10432",
          "status": "active",
          "returnType": "F1040",
          "taxYear": 2025
        },
        "taskId": "018f9c2b-1a2b-7c3d-8e4f-5a6b7c8d9e0f"
      }
    }
  }
  ```

  Save both `client.id` and `taskId`. If you created the client without documents,
  `taskId` is `null` and there is nothing to poll. Full input and return type
  details are in [Clients](/apis/clients).

  ## Poll the binder task

  Filed ingests the staged documents into the client's binder as a background task
  of type `BINDER`. Read the task through the client's `tasks(type: BINDER)` field
  and poll until `status` is `COMPLETED` (or `FAILED`).

  ```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 ClientTaskStatus($clientId: ID!, $type: TaskType) { me { ... on WorkspaceUser { workspace { clients(filters: { ids: [$clientId] }) { tasks(type: $type) { id status startedAt completedAt errorMessage subTasks { type status } } } } } } }",
      "variables": { "clientId": "YOUR_CLIENT_ID", "type": "BINDER" }
    }'
  ```

  ```json theme={null}
  {
    "data": {
      "me": {
        "workspace": {
          "clients": [
            {
              "tasks": [
                {
                  "id": "018f9c2b-1a2b-7c3d-8e4f-5a6b7c8d9e0f",
                  "status": "COMPLETED",
                  "startedAt": "2026-07-04T09:15:00.000Z",
                  "completedAt": "2026-07-04T09:17:42.000Z",
                  "errorMessage": null,
                  "subTasks": [
                    { "type": "CONVERT_DOCUMENTS", "status": "COMPLETED" },
                    { "type": "CLASSIFY_SUBDOCS", "status": "COMPLETED" },
                    { "type": "EXTRACT_SUBDOCS", "status": "COMPLETED" },
                    { "type": "EXPORT_AND_INDEX", "status": "COMPLETED" }
                  ]
                }
              ]
            }
          ]
        }
      }
    }
  }
  ```

  <Tip>
    Poll on an interval (for example every few seconds) until `status` is no longer
    `RUNNING`. `COMPLETED` means the documents are filed in the binder; `FAILED`
    means ingestion did not succeed, and `errorMessage` explains why.
  </Tip>

  There is no `task(id:)` query; you read a task through its client. Full task,
  filter, and result type details are in [Tasks](/apis/tasks).
</Steps>

## Next steps

* Learn the [request anatomy and error model](/guides/making-requests).
* Add more documents to an existing client with
  [`addClientDocuments`](/apis/clients#add-documents-to-a-client).
* Manage clients (rename, archive, delete, assign) in the
  [Clients](/apis/clients#manage-clients) reference.
* Trigger and follow other task types (`TAX_PREP`, `TAX_REVIEW`, `TAX_ADVISOR`)
  in the [Tasks](/apis/tasks) reference.
