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

# Making requests

> Shape a GraphQL request, pass variables, and read error responses

Every Filed API call is a single HTTP `POST` to the same endpoint, with a JSON
body and a `Bearer` token. This page covers the request shape, a first request,
variables, and the error model, so you can call any operation in
the rest of these docs once you know how the wire looks.

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

## Request anatomy

A request is one HTTP `POST` with a JSON body of two keys, `query` and
`variables`, and two headers, `Content-Type: application/json` and
`Authorization: Bearer YOUR_WORKSPACE_TOKEN`.

```http theme={null}
POST /graphql HTTP/1.1
Host: router.apps.filed.com
Content-Type: application/json
Authorization: Bearer YOUR_WORKSPACE_TOKEN

{
  "query": "<GraphQL document>",
  "variables": { }
}
```

<ParamField path="query" type="String!" required>
  A GraphQL document: one (or more) `query` / `mutation` / `fragment` definitions.
  For a single operation you usually send one operation and omit the operation
  name. When you send several operations, give each a name and pass
  `"operationName"` alongside `query` and `variables`.
</ParamField>

<ParamField path="variables" type="Object">
  A JSON object of values for the operation's `$variables`. Pass complex or
  user-supplied values here rather than string-interpolating them into `query`,
  so the GraphQL server validates their types and you avoid injection mistakes.
  Omit it (or send `{}`) for operations that take no arguments.
</ParamField>

<ParamField path="Authorization" type="String" required>
  `Bearer ` followed by a `workspaceToken` from
  [Authentication](/guides/authentication). Omit only for `@public` operations such
  as [`health`](/apis/health) and the token exchange.
</ParamField>

<ParamField path="Content-Type" type="String" required>
  `application/json`. The body is always JSON, never form-encoded.
</ParamField>

A successful response is JSON with a `data` object (and, on partial failure, an
`errors` array, see [Error responses](#error-responses)):

```json theme={null}
{
  "data": { },
  "errors": [ ]
}
```

## A first request

Start with [`health`](/apis/health). It is `@public`, so it needs no token, and
it confirms the endpoint is reachable.

```bash cURL theme={null}
curl -X POST https://router.apps.filed.com/graphql \
  -H "Content-Type: application/json" \
  -d '{ "query": "query Health { health { id ai platform } }" }'
```

```json theme={null}
{
  "data": {
    "health": {
      "id": "health",
      "ai": "ok",
      "platform": "ok"
    }
  }
}
```

Now add a token and run [`me`](/apis/me) to confirm the token works and see who
you are in the workspace:

```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 workspace { id name } } } }" }'
```

`me` returns the `Me` union; with a `workspaceToken` it resolves to
`WorkspaceUser`, which carries your `role` and the `workspace` you are scoped to
(see [`me`](/apis/me) for the full type and `User` member).

## Variables

Pass `$variables` for any operation that takes arguments, so values are
type-checked by the server instead of string-interpolated. The
[`workspace.clients`](/apis/clients#list-clients) field takes a `ClientFilters`
input, an `offset`, a `limit`, and a `SortBy`. Send the document once with
`$variables` placeholders, then send the values in the `variables` JSON object.

```graphql theme={null}
query ListClients($filters: ClientFilters, $limit: Int, $sortBy: SortBy) {
  me {
    ... on WorkspaceUser {
      workspace {
        clients(filters: $filters, limit: $limit, sortBy: $sortBy) {
          id
          name
          externalId
          status
        }
      }
    }
  }
}
```

```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 ListClients($filters: ClientFilters, $limit: Int, $sortBy: SortBy) { me { ... on WorkspaceUser { workspace { clients(filters: $filters, limit: $limit, sortBy: $sortBy) { id name externalId status } } } } }",
    "variables": {
      "filters": { "status": ["active"], "search": "jane" },
      "limit": 20,
      "sortBy": { "field": "createdAt", "order": "DESC" }
    }
  }'
```

<Tip>
  Always send user-supplied or dynamic values through `variables`, never by
  splicing them into the `query` string. The server coerces and validates the
  types, and you avoid quoting bugs and injection risk.
</Tip>

See [`clients`](/apis/clients) for the full `ClientFilters` input and
[`tasks`](/apis/tasks) for `TaskFilters`. The same `$variable` pattern applies
to every mutation in the API, for example `createClient(input: $input)`.

## Error responses

On failure the response is JSON with an `errors` array. Each entry has at least
a `message`, and most also carry an `extensions` object with a `code`. When the
operation failed before any data could be produced, `data` is `null` (or
omitted); when a field resolver fails mid-operation, that field is `null` in
`data` and the matching entry is in `errors` with a `path` pointing at it.

The two error shapes you will see most often:

### Validation error (malformed query)

A query that references a field the type does not have fails GraphQL validation
before any resolver runs. `data` is omitted and `errors` carries one entry per
invalid field, with `extensions.code` of `GRAPHQL_VALIDATION_FAILED`.

```bash cURL theme={null}
curl -X POST http://localhost:7020/graphql \
  -H "Content-Type: application/json" \
  -d '{ "query": "query { health { id ai platform thisFieldDoesNotExist } }" }'
```

```json theme={null}
{
  "errors": [
    {
      "message": "Cannot query field \"thisFieldDoesNotExist\" on type \"Health\".",
      "locations": [
        { "line": 1, "column": 33 }
      ],
      "extensions": {
        "code": "GRAPHQL_VALIDATION_FAILED"
      }
    }
  ]
}
```

### Authentication error (no token)

A resolver that requires a token (everything except `@public` operations like
`health` and the token exchange) returns `UNAUTHENTICATED` for that field. The
field is `null` in `data` and `errors` carries the entry with a `path` naming
the field.

```bash cURL theme={null}
curl -X POST http://localhost:7020/graphql \
  -H "Content-Type: application/json" \
  -d '{ "query": "query { me { __typename } }" }'
```

```json theme={null}
{
  "errors": [
    {
      "message": "UNAUTHENTICATED",
      "path": ["me"],
      "extensions": {
        "code": "UNAUTHENTICATED",
        "serviceName": "platform"
      }
    }
  ],
  "data": {
    "me": null
  }
}
```

<Note>
  The live router may also include a `stacktrace` in `extensions` for server-side
  errors. Treat it as debugging detail, not a stable contract: log it for
  troubleshooting, but key your retry / surface-error logic off
  `extensions.code` and the `path`.
</Note>

### Reading errors in a client

Handle errors defensively:

1. Check `errors` first. If present, at least part of the operation failed.
2. When `data` is `null`, the whole operation failed; surface the first
   `errors[0].message`.
3. When `data` is present but a field is `null`, look up the matching `path` in
   `errors` to know which field failed and why.
4. Branch on `extensions.code` for the common cases:
   * `GRAPHQL_VALIDATION_FAILED` - your query is wrong; do not retry, fix the
     document.
   * `UNAUTHENTICATED` - re-exchange your API key for a fresh `workspaceToken`
     (see [Authentication](/guides/authentication)) and retry once.
   * Other codes (for example `INTERNAL_SERVER_ERROR`) - safe to retry with
     backoff.

<Warning>
  Always inspect `errors` before `data`. A non-empty `errors` array means the
  operation did not fully succeed, even when `data` looks populated, because
  individual fields can fail while the rest resolve.
</Warning>

## Next steps

* [Authentication](/guides/authentication) - get the `workspaceToken` you send
  as `Bearer`.
* [`health`](/apis/health) and [`me`](/apis/me) - the two queries from the
  first-request example above, documented in full.
* [`clients`](/apis/clients) and [`tasks`](/apis/tasks) - the workspace-scoped
  resources you reach through `me { ... on WorkspaceUser { workspace { ... } } }`.
* [Uploading documents](/guides/uploading-documents) - the other wire (tus) you
  use to stage files before attaching them to a client.
