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

# Create clients from tax software

> List a connected firm's UltraTax or CCH Axcess clients and create a Filed client for each, this year and next

Once a firm has [connected over OAuth](https://developers.apps.filed.com/guides/connect-with-oauth.md), you can
read the client list from its tax software and create a Filed client for each
client you work on. This guide covers UltraTax CS and CCH Axcess.

```mermaid theme={null}
---
config:
  theme: base
  look: classic
  layout: elk
  fontFamily: "Geist, ui-sans-serif, system-ui, sans-serif"
  flowchart:
    curve: linear
  themeVariables:
    fontSize: 14px
    primaryColor: "#ffffff"
    primaryBorderColor: "#d4cabc"
    primaryTextColor: "#1e150e"
    lineColor: "#a89a88"
    edgeLabelBackground: "#ffffff"
---
flowchart TD
  A["1. connections<br/>find the tax-software connection"] --> B["2. clientList<br/>the firm's tax-software clients"]
  B --> C["3. clientImportIndex<br/>which already exist in Filed"]
  C -->|"new"| D["4. createClient<br/>sourceExternalId<br/>+ taxSoftwareClient"]
  C -->|"exists"| E["Use that client"]
  D --> F["5. binder checklist<br/>built from last year's return"]
  classDef decision fill:#ebe3d5,stroke:#704b30,color:#1e150e
  classDef ok fill:#e9f6e9,stroke:#257035,color:#1e150e
  class C decision
  class D,F ok
```

Every call goes to `https://router.apps.filed.com/graphql` with the access token
from your OAuth grant.

## Before you start

* Request `clients:read` and `clients:write`. A grant without a write scope is
  read-only, and `createClient` fails with a `FORBIDDEN` error.
* The customer who approved the grant needs a full seat in the workspace.
  Otherwise `createClient` fails with `A full seat is required.`
* The firm connects its tax software in Filed itself, under **Plugins**.
  UltraTax runs through [Prowork Desktop](https://developers.apps.filed.com/guides/install-prowork-on-shared-windows.md)
  on a machine that can reach the firm's UltraTax data folder.
* Your token reaches one workspace. Workspaces share nothing: clients,
  connections, and protocols are separate in each. A firm with several Filed
  workspaces grants you access to each one separately.
* The refresh token lapses only after 90 days without a refresh, so a firm
  you keep refreshing for stays connected. See
  [Refreshing](https://developers.apps.filed.com/guides/connect-with-oauth.md#refreshing).

## 1. Find the tax-software connection

List the workspace's tax-software connections. A workspace can have more than
one, for example an UltraTax connection per office. Filed keeps offices apart
only when each uses its own UltraTax data location.

```graphql theme={null}
query TaxSoftwareConnections {
  me {
    ... on WorkspaceUser {
      workspace {
        connections(categories: ["tax_software"]) {
          id
          providerKey
          name
          status
        }
      }
    }
  }
}
```

```json theme={null}
{
  "data": {
    "me": {
      "workspace": {
        "connections": [
          {
            "id": "019a4c2e-5b1f-7d20-9c3e-4a8b2d6f1e07",
            "providerKey": "ultratax",
            "name": "UltraTax CS",
            "status": "active"
          }
        ]
      }
    }
  }
}
```

| `providerKey` | Tax software |
| - | - |
| `ultratax` | UltraTax CS |
| `cch-axcess` | CCH Axcess, connected with a user seat |
| `cch-api` | CCH Axcess, connected through the CCH API |

The list holds the workspace's shared connections and the approving customer's
own personal ones. For CCH Axcess, Filed recommends the user seat: in Filed's
tests it produces significantly more accurate returns, and the API is only
slightly faster.

## 2. List the firm's clients

`clientList` returns up to 100 clients per page, sorted by name. `search`
matches part of a name or ID, ignoring case.

```graphql theme={null}
query TaxSoftwareClients($connectionId: ID!, $search: String, $offset: Int, $limit: Int) {
  me {
    ... on WorkspaceUser {
      workspace {
        connections(connectionId: $connectionId) {
          id
          clientList(search: $search, offset: $offset, limit: $limit) {
            externalId
            name
            returnType
            taxYear
          }
        }
      }
    }
  }
}
```

```json theme={null}
{
  "connectionId": "019a4c2e-5b1f-7d20-9c3e-4a8b2d6f1e07",
  "offset": 0,
  "limit": 100
}
```

```json theme={null}
{
  "data": {
    "me": {
      "workspace": {
        "connections": [
          {
            "id": "019a4c2e-5b1f-7d20-9c3e-4a8b2d6f1e07",
            "clientList": [
              {
                "externalId": "DOEJ01",
                "name": "John Doe",
                "returnType": "F1040",
                "taxYear": 2025
              }
            ]
          }
        ]
      }
    }
  }
}
```

To read every client, add 100 to `offset` until a page comes back with fewer
than 100 rows.

`externalId` is the tax software's own ID for the client, and the value you
pass on in the next steps:

| Tax software | `externalId` | Across tax years |
| - | - | - |
| UltraTax CS | The UltraTax client ID, such as `DOEJ01` | Stays the same |
| CCH Axcess | The return ID: year, return type, client ID and version, such as `2025I:DOEJ01:V1` | Changes with the year |

`returnType` and `taxYear` are `null` when the tax software does not report
them. Supply them yourself when you create the client.

### Refresh the list

`clientList` reads Filed's copy of the firm's client list, not the tax
software directly. After the firm adds clients or rolls them over to a new
year, refresh the copy:

```graphql theme={null}
mutation SyncTaxSoftwareClients($input: SyncConnectionClientsInput!) {
  syncConnectionClients(input: $input)
}
```

```json theme={null}
{
  "input": { "connectionId": "019a4c2e-5b1f-7d20-9c3e-4a8b2d6f1e07" }
}
```

It returns `true` once the refresh has started. Poll the connection's
`clientRoster` until `syncing` is `false` and `syncedAt` has moved past the
time you started:

```graphql theme={null}
query TaxSoftwareClientsSync($connectionId: ID!) {
  me {
    ... on WorkspaceUser {
      workspace {
        connections(connectionId: $connectionId) {
          clientRoster {
            syncedAt
            syncing
          }
        }
      }
    }
  }
}
```

UltraTax reads the list through Prowork Desktop, so the refresh needs it
running on the firm's machine.

## 3. Find clients that already exist

The firm may already have created some of these clients in Filed. Read the
tax year's clients once, with the tax-software IDs they are linked to:

```graphql theme={null}
query ExistingClients($taxYear: Int!) {
  me {
    ... on WorkspaceUser {
      workspace {
        clientImportIndex(taxYear: $taxYear) {
          id
          name
          returnType
          archived
          links {
            connectionId
            externalId
          }
        }
      }
    }
  }
}
```

A Filed client already exists for a tax-software client when one of its
`links` has the tax-software connection's `id` as `connectionId` and the
client's `externalId`, with the same `returnType`. Use that client's `id`
instead of creating another.

## 4. Create a Filed client

```graphql theme={null}
mutation CreateClient($input: CreateClientInput!) {
  createClient(input: $input) {
    client {
      id
      name
      returnType
      taxYear
      status
    }
    taskId
  }
}
```

```json theme={null}
{
  "input": {
    "name": "John Doe",
    "returnType": "F1040",
    "taxYear": 2025,
    "sourceExternalId": "DOEJ01",
    "taxSoftwareClient": {
      "connectionId": "019a4c2e-5b1f-7d20-9c3e-4a8b2d6f1e07",
      "clientId": "DOEJ01",
      "displayName": "John Doe"
    }
  }
}
```

```json theme={null}
{
  "data": {
    "createClient": {
      "client": {
        "id": "019a4c31-0e7a-7b45-8f12-6c3d9e2a4b10",
        "name": "John Doe",
        "returnType": "F1040",
        "taxYear": 2025,
        "status": "active"
      },
      "taskId": null
    }
  }
}
```

`sourceExternalId` and `taxSoftwareClient` both take the tax-software
`externalId`; `taxSoftwareClient.connectionId` is the connection from step 1.
See [`CreateClientInput`](https://developers.apps.filed.com/apis/clients#input-createclientinput) for what each
field does.

### Repeat calls return the same client

Filed identifies the client by your connection, `sourceExternalId`, `taxYear`
and `returnType`. Calling `createClient` again with the same four values returns
the client the first call created, even when the calls arrive at the same time:

```json theme={null}
{
  "data": {
    "createClient": {
      "client": {
        "id": "019a4c31-0e7a-7b45-8f12-6c3d9e2a4b10",
        "name": "John Doe",
        "returnType": "F1040",
        "taxYear": 2025,
        "status": "active"
      },
      "taskId": null
    }
  }
}
```

A different `taxYear` or `returnType` creates a different client. The first
call's `name` is kept. A call for an archived client fails.

Store the returned `client.id` and call `createClient` again only to retry a
call whose outcome you did not see: each call without `uploadIds` also starts a
new checklist build.

## 5. Read the checklist

The checklist is the list of documents the client sent last year that are
missing this year. Filed builds it without anyone clicking anything:

| Tax software | Built from |
| - | - |
| CCH Axcess | Last year's return in CCH Axcess, read when the client is created with `taxSoftwareClient` and no `uploadIds` |
| UltraTax CS | Last year's return uploaded as a PDF. Reading it from UltraTax is not available yet. |

Upload last year's return in `uploadIds` when you create the client, or later
with [`addClientDocuments`](https://developers.apps.filed.com/apis/clients#add-documents-to-a-client).

```graphql theme={null}
query ClientChecklist($clientId: ID!) {
  me {
    ... on WorkspaceUser {
      workspace {
        clients(filters: { ids: [$clientId] }) {
          binder {
            checklistBuild {
              source
              status
              returnId
              error
            }
            missingItemsAssessment {
              status
              count
              items {
                id
                item
                formType
                issuer
                severity
                status
              }
            }
          }
        }
      }
    }
  }
}
```

`checklistBuild` is `null` until a build starts. Its `status` is `running`,
`completed` or `failed`; `source` is `TAX_SOFTWARE` or `DOCUMENTS`, and
`returnId` names the tax-software return it read. When a tax-software build
fails, `error` says why. The next binder run tries the tax software again; a
prior-year PDF uploaded since is used only if that try fails too, or the tax
software has no return to read. See [Browse a client's
binder](https://developers.apps.filed.com/guides/recipes/browse-the-binder.md#2-check-the-missing-item-checklist)
for working with the items.

## A new tax year

Filed does not create, roll over or remove clients in the tax software. The
firm rolls its clients over in UltraTax or CCH Axcess as it does today.

In Filed, a client belongs to one tax year, so the 2026 return is a new Filed
client. Once the firm has rolled a client over:

1. [Refresh the client list](#refresh-the-list).
2. Call `createClient` with `taxYear: 2026` and the client's ID for 2026. In
   UltraTax that is the same client ID as in 2025. In CCH Axcess it is the
   2026 return ID, such as `2026I:DOEJ01:V1`, which appears in the list after
   the firm rolls the return over.
3. Filed builds the 2026 checklist from the 2025 return, as in
   [step 5](#5-read-the-checklist). CCH Axcess reads the previous year's
   return, `2025I:DOEJ01:V1`. For UltraTax, for now, upload the 2025 return as
   a PDF.

Filed does not link a client's Filed records across tax years yet: the 2025 and
2026 clients are separate, and the mapping between them is yours to keep.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.