openapi: 3.2.0 info: title: Tale Platform Projects API version: 3.27.0 contact: name: Tale url: https://tale.dev description: 'REST access to a Tale deployment: knowledge resources, automations and their runs, chat threads, agents, and skills — plus an MCP endpoint exposing the same platform to MCP clients.' servers: - url: '{origin}' description: The deployment this document describes — `GET /openapi.json` on a running instance fills it in variables: origin: default: https://your-host.example.com description: Scheme and host of the Tale deployment security: - bearerAuth: [] tags: - name: Projects description: Projects, their folders, and their files — the machine door for external systems that mirror client work into the platform. Multi-org keys must send X-Organization-Slug on every call. paths: /api/v1/projects: get: tags: - Projects summary: List projects, or look one up by external item id description: 'Two doors in one. With `externalItemId`: a LOOKUP answering at most one project — the key is unique per organization and is compared after NFC normalization and trimming, the form the create stored it in; a project the key holder cannot see answers the same empty list as no match, and a lookup takes no `cursor`, `limit` or `archived` (400 `INVALID_QUERY`); it is one whole page and says so (`isDone: true`, an empty `continueCursor`). Without it: the LIST of every project the key holder can see, newest first, keyset-paged like every other list — `{projects, isDone, continueCursor}`, pass `continueCursor` back as `cursor` until `isDone`. While more pages remain the answer also carries `cursor`, the same token under its pre-1.5.0 name — deprecated, served for at least two more minor versions. Archived projects are excluded unless `archived` says `include` or `only`. Either way a row carries `archivedAt` when the project is archived, so a worker can detect an archived project holding its key. Shares the general REST bucket (120/min, keyed on the key holder).' operationId: listProjects security: - bearerAuth: [] parameters: - name: X-Organization-Slug in: header required: false schema: type: string description: The organization to operate on. STRICTLY REQUIRED on every call, reads included, when the key holder belongs to more than one organization — without it such requests are refused with 400 `ORG_SLUG_REQUIRED` (the dashboard’s last-active organization never steers a machine call). A slug that names no organization answers 404 `ORG_SLUG_INVALID`, one the key holder is no member of 403 `ORG_FORBIDDEN`. Single-organization keys may omit it; a blank or whitespace-only value reads as the header absent. Each of the three refusals lists the slugs the key holder may send under `data.organizations`; `GET /api/v1/me` answers them too. - name: externalItemId in: query required: false schema: type: string description: The caller-owned external key the project was created with — compared after NFC normalization and trimming; a value that is blank once trimmed is refused (400 `INVALID_QUERY`). Present, the answer is the lookup; absent, the list. - name: archived in: query required: false schema: type: string enum: - exclude - include - only default: exclude description: 'Which lifecycle the list covers (list only): `exclude` (the default) answers active projects, `include` both, `only` the archived ones' - name: cursor in: query required: false schema: type: string description: The previous page’s `continueCursor`, unchanged — opaque; omit for the first page. A value this list did not answer is refused with 400 `INVALID_CURSOR` rather than read as the first page - name: limit in: query required: false schema: type: integer minimum: 1 maximum: 100 default: 25 description: Page size, 1..100 (default 25; out-of-range numbers are clamped, a value that is not a number answers 400 `INVALID_LIMIT`) - name: X-Request-Id in: header required: false description: 'Your own correlation id, echoed back on the response and written to the platform’s log: up to 255 characters of letters, digits, `_`, `-` and `=`. Any other value is replaced by a fresh UUID (the response carries the id that was used), and uniqueness of an id you choose is yours to keep.' schema: type: string pattern: ^[A-Za-z0-9_=-]{1,255}$ responses: '200': description: The lookup’s zero or one matching project, or one page of the list content: application/json: schema: type: object x-tale-pagination: keyset required: - projects - isDone - continueCursor properties: projects: type: array items: $ref: '#/components/schemas/Project' isDone: type: boolean description: True when no more pages remain — a lookup is one whole page and says so continueCursor: type: string description: Pass back as `cursor` for the next page; empty when `isDone` (a lookup answers it empty) cursor: type: string deprecated: true description: The same token as `continueCursor`, present only while more pages remain — the pre-1.5.0 spelling, served for at least two more minor versions; read `continueCursor` headers: ETag: $ref: '#/components/headers/ETag' Cache-Control: $ref: '#/components/headers/CacheControl' X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '304': description: Not Modified — `If-None-Match` named the current `ETag`, so the body is not sent headers: ETag: $ref: '#/components/headers/ETag' Cache-Control: $ref: '#/components/headers/CacheControl' X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '400': description: 'Invalid request: a body the schema refuses — a wrong type, an unknown key, a missing field, a NUL or an unpaired surrogate, a whole number beyond 2^53 − 1 (`INVALID_BODY`, every problem under `data.issues`); a query parameter the operation does not take, given twice or left blank (`INVALID_QUERY`); on a list, a `limit` that is not a whole number (`INVALID_LIMIT`) or a `cursor` this list did not answer (`INVALID_CURSOR`)' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '401': description: Missing or invalid API key (`UNAUTHORIZED`) content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' WWW-Authenticate: $ref: '#/components/headers/WWWAuthenticate' '403': description: '`X-Organization-Slug` names an organization the key holder is no member of (`ORG_FORBIDDEN`, with the slugs the key holder may send under `data.organizations`)' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '404': description: '`X-Organization-Slug` names no organization (`ORG_SLUG_INVALID`, with the slugs the key holder may send under `data.organizations`)' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '405': description: The path exists, but not for this method (`METHOD_NOT_ALLOWED`); `Allow` names the methods it serves content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' Allow: $ref: '#/components/headers/Allow' '429': description: 'The key holder’s request budget is spent (`RATE_LIMITED`): `Retry-After` names the wait in whole seconds and `data.retryAfterMs` in milliseconds; the envelope carries a `requestId`' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' Retry-After: $ref: '#/components/headers/RetryAfter' '500': description: Internal error (`INTERNAL_ERROR`); the envelope carries a `requestId` to quote when reporting it content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '503': description: 'The platform’s database is restarting or cannot be reached (`DATABASE_UNAVAILABLE`): `Retry-After` names the wait in whole seconds — retry with backoff; the envelope carries a `requestId`' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' Retry-After: $ref: '#/components/headers/RetryAfter' post: tags: - Projects summary: Create a project description: 'Requires the org editor role. `externalItemId` is an opaque caller-owned key, stored in one canonical form — NFC-normalized and trimmed, the form every lookup and duplicate check compares — and unique per organization regardless of lifecycle: a conflict against an archived project is still a 409 (delete or restore it first). A value that is blank once trimmed, or over-long, is a 400 `INVALID_BODY`. `key` (the task-identifier prefix) is derived from the name when omitted — omitted, not blank: a key that is blank once trimmed is the same 400 `INVALID_BODY` a blank `name` is; a derived key that is already taken gets a numeric suffix, and a name no key can be derived from creates the project keyless — a name-only create never fails on a key the caller did not choose. Only an EXPLICIT `key` that is taken answers 409, and one outside the 2-6 letters-and-digits rule 400 `PROJECT_KEY_INVALID`.' operationId: createProject security: - bearerAuth: [] parameters: - name: X-Organization-Slug in: header required: false schema: type: string description: The organization to operate on. STRICTLY REQUIRED on every call, reads included, when the key holder belongs to more than one organization — without it such requests are refused with 400 `ORG_SLUG_REQUIRED` (the dashboard’s last-active organization never steers a machine call). A slug that names no organization answers 404 `ORG_SLUG_INVALID`, one the key holder is no member of 403 `ORG_FORBIDDEN`. Single-organization keys may omit it; a blank or whitespace-only value reads as the header absent. Each of the three refusals lists the slugs the key holder may send under `data.organizations`; `GET /api/v1/me` answers them too. - name: X-Request-Id in: header required: false description: 'Your own correlation id, echoed back on the response and written to the platform’s log: up to 255 characters of letters, digits, `_`, `-` and `=`. Any other value is replaced by a fresh UUID (the response carries the id that was used), and uniqueness of an id you choose is yours to keep.' schema: type: string pattern: ^[A-Za-z0-9_=-]{1,255}$ requestBody: required: true content: application/json: schema: type: object additionalProperties: false required: - name properties: name: type: string minLength: 1 maxLength: 80 description: Trimmed; whitespace alone is a missing name externalItemId: type: string minLength: 1 maxLength: 256 description: Stored and compared after NFC normalization and trimming key: type: string description: 'Immutable 2-6 char task-key prefix. Omitted: derived from the name (suffixed when taken), keyless when underivable. Trimmed; whitespace alone is a 400 `INVALID_BODY`, not an omitted key. Letters and digits only, starting with a letter (`PROJECT_KEY_INVALID` otherwise); normalized to uppercase, never truncated. An explicit key that is taken is a 409.' minLength: 2 maxLength: 6 pattern: ^[A-Za-z][A-Za-z0-9]{1,5}$ description: type: string maxLength: 500 teamIds: type: array items: type: string minLength: 1 maxLength: 128 maxItems: 21 description: 'The audience: the teams that may see the project, by id — teams of this organization (`GET /api/v1/teams` lists them), and for a key holder who is not an admin, teams they belong to (`TEAM_ACCESS_DENIED`). Omitted or empty = organization-wide. A repeated id collapses to one, first-seen order kept — the stored list is what the response echoes, as on documents and skills. Owners and admins see every project regardless.' responses: '201': description: Created project content: application/json: schema: type: object required: - project properties: project: $ref: '#/components/schemas/Project' headers: Location: $ref: '#/components/headers/Location' X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '400': description: A body the schema refuses (`INVALID_BODY` — a `teamIds` past its `maxItems` included), an explicit `key` outside the 2-6 letters-and-digits rule (`PROJECT_KEY_INVALID`), or a `teamIds` that names a team that is not this organization’s (`PROJECT_SHARING_INVALID`, the unknown ids in `data.unknownTeamIds`); a repeated team id collapses to one content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '401': description: Missing or invalid API key (`UNAUTHORIZED`) content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' WWW-Authenticate: $ref: '#/components/headers/WWWAuthenticate' '403': description: The key holder is not an org editor (`ROLE_FORBIDDEN`), or `teamIds` names a team a non-admin key holder is not in (`TEAM_ACCESS_DENIED`); or `X-Organization-Slug` names an organization the key holder is no member of (`ORG_FORBIDDEN`, with the slugs the key holder may send under `data.organizations`) content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '404': description: '`X-Organization-Slug` names no organization (`ORG_SLUG_INVALID`, with the slugs the key holder may send under `data.organizations`)' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '405': description: The path exists, but not for this method (`METHOD_NOT_ALLOWED`); `Allow` names the methods it serves content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' Allow: $ref: '#/components/headers/Allow' '409': description: Duplicate externalItemId (`PROJECT_DUPLICATE_EXTERNAL_ID`) or an explicit project key that is taken (`PROJECT_KEY_TAKEN`) content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '413': description: The body exceeds this operation’s byte cap — 1 MiB unless the description says otherwise (`BODY_TOO_LARGE`); the envelope carries a `requestId` content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '429': description: 'The key holder’s request budget is spent (`RATE_LIMITED`): `Retry-After` names the wait in whole seconds and `data.retryAfterMs` in milliseconds; the envelope carries a `requestId`' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' Retry-After: $ref: '#/components/headers/RetryAfter' '500': description: Internal error (`INTERNAL_ERROR`); the envelope carries a `requestId` to quote when reporting it content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '503': description: 'The platform’s database is restarting or cannot be reached (`DATABASE_UNAVAILABLE`): `Retry-After` names the wait in whole seconds — retry with backoff; the envelope carries a `requestId`' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' Retry-After: $ref: '#/components/headers/RetryAfter' /api/v1/projects/{id}: get: tags: - Projects summary: Get a project description: Answers an opaque 404 for a project that is cross-organization, invisible to the key holder, or does not exist — the three are indistinguishable. operationId: getProject security: - bearerAuth: [] parameters: - name: X-Organization-Slug in: header required: false schema: type: string description: The organization to operate on. STRICTLY REQUIRED on every call, reads included, when the key holder belongs to more than one organization — without it such requests are refused with 400 `ORG_SLUG_REQUIRED` (the dashboard’s last-active organization never steers a machine call). A slug that names no organization answers 404 `ORG_SLUG_INVALID`, one the key holder is no member of 403 `ORG_FORBIDDEN`. Single-organization keys may omit it; a blank or whitespace-only value reads as the header absent. Each of the three refusals lists the slugs the key holder may send under `data.organizations`; `GET /api/v1/me` answers them too. - name: id in: path required: true schema: type: string description: Project ID - name: X-Request-Id in: header required: false description: 'Your own correlation id, echoed back on the response and written to the platform’s log: up to 255 characters of letters, digits, `_`, `-` and `=`. Any other value is replaced by a fresh UUID (the response carries the id that was used), and uniqueness of an id you choose is yours to keep.' schema: type: string pattern: ^[A-Za-z0-9_=-]{1,255}$ responses: '200': description: The project content: application/json: schema: type: object required: - project properties: project: $ref: '#/components/schemas/Project' headers: ETag: $ref: '#/components/headers/ETag' Cache-Control: $ref: '#/components/headers/CacheControl' X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '304': description: Not Modified — `If-None-Match` named the current `ETag`, so the body is not sent headers: ETag: $ref: '#/components/headers/ETag' Cache-Control: $ref: '#/components/headers/CacheControl' X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '400': description: 'Invalid request: a body the schema refuses — a wrong type, an unknown key, a missing field, a NUL or an unpaired surrogate, a whole number beyond 2^53 − 1 (`INVALID_BODY`, every problem under `data.issues`); a query parameter the operation does not take, given twice or left blank (`INVALID_QUERY`); on a list, a `limit` that is not a whole number (`INVALID_LIMIT`) or a `cursor` this list did not answer (`INVALID_CURSOR`)' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '401': description: Missing or invalid API key (`UNAUTHORIZED`) content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' WWW-Authenticate: $ref: '#/components/headers/WWWAuthenticate' '403': description: '`X-Organization-Slug` names an organization the key holder is no member of (`ORG_FORBIDDEN`, with the slugs the key holder may send under `data.organizations`)' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '404': description: Project not found; or `X-Organization-Slug` names no organization (`ORG_SLUG_INVALID`, with the slugs the key holder may send under `data.organizations`) content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '405': description: The path exists, but not for this method (`METHOD_NOT_ALLOWED`); `Allow` names the methods it serves content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' Allow: $ref: '#/components/headers/Allow' '429': description: 'The key holder’s request budget is spent (`RATE_LIMITED`): `Retry-After` names the wait in whole seconds and `data.retryAfterMs` in milliseconds; the envelope carries a `requestId`' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' Retry-After: $ref: '#/components/headers/RetryAfter' '500': description: Internal error (`INTERNAL_ERROR`); the envelope carries a `requestId` to quote when reporting it content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '503': description: 'The platform’s database is restarting or cannot be reached (`DATABASE_UNAVAILABLE`): `Retry-After` names the wait in whole seconds — retry with backoff; the envelope carries a `requestId`' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' Retry-After: $ref: '#/components/headers/RetryAfter' patch: tags: - Projects summary: Update a project — archive, restore, rename, re-describe, re-attach its… description: 'The project’s mutable surface, every field optional and at least one required. `archived: true` archives the project, `false` restores it — the same action as the app, so it requires an organization admin (403 `ROLE_FORBIDDEN` otherwise); a project already in the requested state is left unchanged, and a body that changes nothing — every field already at its value — writes nothing and leaves `updatedAt` alone. Archiving keeps the project and everything in it readable through this door and refuses every write on it (403 `PROJECT_ARCHIVED`); its `externalItemId` stays taken until the project is deleted. `name`, `description` and `externalItemId` are the identity a mirror propagates when the source record changes: for the org editor role with project edit access (403 `ROLE_FORBIDDEN` / `RBAC_FORBIDDEN` otherwise) on an active project — a body that restores and edits applies the restore first, one that edits and archives applies the archive last, and identity edits on an archived project the body does not restore answer 403 `PROJECT_ARCHIVED`. `name` is trimmed and never blank; `description: null` clears it; `externalItemId` is stored canonical (NFC, trimmed), must be free within the organization (409 `PROJECT_DUPLICATE_EXTERNAL_ID`, the key in `data`), and `null` releases it. An invisible or absent project answers the opaque 404. Answers the project as it now stands.' operationId: setProjectArchived security: - bearerAuth: [] parameters: - name: X-Organization-Slug in: header required: false schema: type: string description: The organization to operate on. STRICTLY REQUIRED on every call, reads included, when the key holder belongs to more than one organization — without it such requests are refused with 400 `ORG_SLUG_REQUIRED` (the dashboard’s last-active organization never steers a machine call). A slug that names no organization answers 404 `ORG_SLUG_INVALID`, one the key holder is no member of 403 `ORG_FORBIDDEN`. Single-organization keys may omit it; a blank or whitespace-only value reads as the header absent. Each of the three refusals lists the slugs the key holder may send under `data.organizations`; `GET /api/v1/me` answers them too. - name: id in: path required: true schema: type: string description: Project ID - name: X-Request-Id in: header required: false description: 'Your own correlation id, echoed back on the response and written to the platform’s log: up to 255 characters of letters, digits, `_`, `-` and `=`. Any other value is replaced by a fresh UUID (the response carries the id that was used), and uniqueness of an id you choose is yours to keep.' schema: type: string pattern: ^[A-Za-z0-9_=-]{1,255}$ requestBody: required: true content: application/json: schema: type: object additionalProperties: false description: At least one of the fields; a body with none is refused (`INVALID_BODY`) properties: archived: type: boolean name: type: string minLength: 1 maxLength: 80 description: Trimmed, never blank description: type: - string - 'null' maxLength: 500 description: '`null` clears it' externalItemId: type: - string - 'null' minLength: 1 maxLength: 256 description: The caller-owned key, stored NFC-normalized and trimmed, unique per organization; `null` releases it teamIds: type: array items: type: string minLength: 1 maxLength: 128 maxItems: 21 description: 'The audience, replaced whole: the teams that may see the project, by id (teams of this organization — `GET /api/v1/teams` lists them); `[]` makes it organization-wide, a repeated id collapses to one, and the audience the project already carries is a no-op that leaves `updatedAt` alone. An organization admin verb, like `archived` — a lesser role answers 403 `RBAC_FORBIDDEN` — and, like every other write, refused on an archived project the body does not restore (403 `PROJECT_ARCHIVED`).' responses: '200': description: The project as it now stands content: application/json: schema: type: object required: - project properties: project: $ref: '#/components/schemas/Project' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '400': description: 'A body the schema refuses (`INVALID_BODY`): no field, an unknown key, a blank `name` or `externalItemId`, a value past its cap (`teamIds` past its `maxItems` included); or a `teamIds` that names a team that is not this organization’s (`PROJECT_SHARING_INVALID`, the unknown ids in `data.unknownTeamIds`)' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '401': description: Missing or invalid API key (`UNAUTHORIZED`) content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' WWW-Authenticate: $ref: '#/components/headers/WWWAuthenticate' '403': description: '`archived` or `teamIds` from a key holder who is not an organization admin, or an identity edit without the editor role (`ROLE_FORBIDDEN`) or project edit access (`RBAC_FORBIDDEN`, `PROJECT_FORBIDDEN`), or on an archived project the body does not restore (`PROJECT_ARCHIVED`); or `X-Organization-Slug` names an organization the key holder is no member of (`ORG_FORBIDDEN`, with the slugs the key holder may send under `data.organizations`)' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '404': description: Project not found; or `X-Organization-Slug` names no organization (`ORG_SLUG_INVALID`, with the slugs the key holder may send under `data.organizations`) content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '405': description: The path exists, but not for this method (`METHOD_NOT_ALLOWED`); `Allow` names the methods it serves content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' Allow: $ref: '#/components/headers/Allow' '409': description: '`externalItemId` is another project’s key in this organization (`PROJECT_DUPLICATE_EXTERNAL_ID`, the canonical key in `data`) — nothing was written' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '413': description: The body exceeds this operation’s byte cap — 1 MiB unless the description says otherwise (`BODY_TOO_LARGE`); the envelope carries a `requestId` content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '429': description: 'The key holder’s request budget is spent (`RATE_LIMITED`): `Retry-After` names the wait in whole seconds and `data.retryAfterMs` in milliseconds; the envelope carries a `requestId`' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' Retry-After: $ref: '#/components/headers/RetryAfter' '500': description: Internal error (`INTERNAL_ERROR`); the envelope carries a `requestId` to quote when reporting it content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '503': description: 'The platform’s database is restarting or cannot be reached (`DATABASE_UNAVAILABLE`): `Retry-After` names the wait in whole seconds — retry with backoff; the envelope carries a `requestId`' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' Retry-After: $ref: '#/components/headers/RetryAfter' delete: tags: - Projects summary: Delete a project description: 'The app’s project delete, for an organization admin (403 `ROLE_FORBIDDEN` otherwise). The optional body chooses what happens to the project’s content: `mode: "cascade"` (the default) destroys it — every document expires into the retention pipeline (purged after the organization’s grace window), the key holder’s own chats are trashed, every task is retired and its live runs cancelled; `mode: "detach"` releases the documents and chats into the organization instead. Agents, folders and the project’s run history — finished runs included, unlike an automation delete, which keeps its runs — go with the project either way, and its `externalItemId` becomes free again. The app’s confirmation phrase (the project name) is supplied by the door. A cascade is charged against the per-user `project:delete-cascade` budget the app charges too (5/min). Refused as a 409, before anything is written, while an automation is installed in the project (`PROJECT_HAS_BOUND_AUTOMATIONS` — `data.automations` names them; uninstall each with `DELETE /api/v1/projects/{id}/automations/{name}` first), while a cascade would destroy a controlled record that is in review, approved or retains an approved version (`PROJECT_HAS_PROTECTED_RECORDS` — `data.documents` names them), or while a legal hold covers one of its documents (`PROJECT_LEGAL_HOLD`). An invisible or absent project answers the opaque 404.' operationId: deleteProject security: - bearerAuth: [] parameters: - name: X-Organization-Slug in: header required: false schema: type: string description: The organization to operate on. STRICTLY REQUIRED on every call, reads included, when the key holder belongs to more than one organization — without it such requests are refused with 400 `ORG_SLUG_REQUIRED` (the dashboard’s last-active organization never steers a machine call). A slug that names no organization answers 404 `ORG_SLUG_INVALID`, one the key holder is no member of 403 `ORG_FORBIDDEN`. Single-organization keys may omit it; a blank or whitespace-only value reads as the header absent. Each of the three refusals lists the slugs the key holder may send under `data.organizations`; `GET /api/v1/me` answers them too. - name: id in: path required: true schema: type: string description: Project ID - name: X-Request-Id in: header required: false description: 'Your own correlation id, echoed back on the response and written to the platform’s log: up to 255 characters of letters, digits, `_`, `-` and `=`. Any other value is replaced by a fresh UUID (the response carries the id that was used), and uniqueness of an id you choose is yours to keep.' schema: type: string pattern: ^[A-Za-z0-9_=-]{1,255}$ requestBody: required: false content: application/json: schema: type: object additionalProperties: false properties: mode: type: string enum: - cascade - detach default: cascade description: 'What happens to the project’s documents and chats: destroyed (`cascade`) or released into the organization (`detach`)' responses: '204': description: Deleted headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '400': description: 'Invalid request: a body the schema refuses — a wrong type, an unknown key, a missing field, a NUL or an unpaired surrogate, a whole number beyond 2^53 − 1 (`INVALID_BODY`, every problem under `data.issues`); a query parameter the operation does not take, given twice or left blank (`INVALID_QUERY`); on a list, a `limit` that is not a whole number (`INVALID_LIMIT`) or a `cursor` this list did not answer (`INVALID_CURSOR`)' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '401': description: Missing or invalid API key (`UNAUTHORIZED`) content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' WWW-Authenticate: $ref: '#/components/headers/WWWAuthenticate' '403': description: The key holder is not an organization admin (`ROLE_FORBIDDEN`); or `X-Organization-Slug` names an organization the key holder is no member of (`ORG_FORBIDDEN`, with the slugs the key holder may send under `data.organizations`) content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '404': description: Project not found; or `X-Organization-Slug` names no organization (`ORG_SLUG_INVALID`, with the slugs the key holder may send under `data.organizations`) content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '405': description: The path exists, but not for this method (`METHOD_NOT_ALLOWED`); `Allow` names the methods it serves content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' Allow: $ref: '#/components/headers/Allow' '409': description: An automation is installed in the project (`PROJECT_HAS_BOUND_AUTOMATIONS`, `data.automations`), a cascade would destroy a protected controlled record (`PROJECT_HAS_PROTECTED_RECORDS`, `data.documents`), or a legal hold covers a document of the project (`PROJECT_LEGAL_HOLD`) content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '413': description: The body exceeds this operation’s byte cap — 1 MiB unless the description says otherwise (`BODY_TOO_LARGE`); the envelope carries a `requestId` content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '429': description: 'The key holder’s request budget is spent (`RATE_LIMITED`): `Retry-After` names the wait in whole seconds and `data.retryAfterMs` in milliseconds; the envelope carries a `requestId`' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' Retry-After: $ref: '#/components/headers/RetryAfter' '500': description: Internal error (`INTERNAL_ERROR`); the envelope carries a `requestId` to quote when reporting it content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '503': description: 'The platform’s database is restarting or cannot be reached (`DATABASE_UNAVAILABLE`): `Retry-After` names the wait in whole seconds — retry with backoff; the envelope carries a `requestId`' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' Retry-After: $ref: '#/components/headers/RetryAfter' /api/v1/projects/{id}/folders: get: tags: - Projects summary: List the project’s folders — the roots, or one folder’s children description: The root folders, or with `parentId` the children of that folder — one level at a time, NOT paginated (a project holds a handful of period folders per level, not an unbounded tree); walk the tree level by level, or resolve a file’s `folderId` with `GET …/folders/{folderId}`. Every folder carries its `parentId` (null at the root). A `parentId` that is not a folder of THIS project answers the opaque 404 (`FOLDER_NOT_FOUND`). operationId: listProjectFolders security: - bearerAuth: [] parameters: - name: X-Organization-Slug in: header required: false schema: type: string description: The organization to operate on. STRICTLY REQUIRED on every call, reads included, when the key holder belongs to more than one organization — without it such requests are refused with 400 `ORG_SLUG_REQUIRED` (the dashboard’s last-active organization never steers a machine call). A slug that names no organization answers 404 `ORG_SLUG_INVALID`, one the key holder is no member of 403 `ORG_FORBIDDEN`. Single-organization keys may omit it; a blank or whitespace-only value reads as the header absent. Each of the three refusals lists the slugs the key holder may send under `data.organizations`; `GET /api/v1/me` answers them too. - name: id in: path required: true schema: type: string description: Project ID - name: parentId in: query required: false schema: type: string description: List the children of this folder (a folder of this project) instead of the root folders - name: X-Request-Id in: header required: false description: 'Your own correlation id, echoed back on the response and written to the platform’s log: up to 255 characters of letters, digits, `_`, `-` and `=`. Any other value is replaced by a fresh UUID (the response carries the id that was used), and uniqueness of an id you choose is yours to keep.' schema: type: string pattern: ^[A-Za-z0-9_=-]{1,255}$ responses: '200': description: The folders of that level, each with its `parentId` content: application/json: schema: type: object description: 'The complete set under `folders` — never paginated: no `isDone`, no cursor to follow' x-tale-pagination: none required: - folders properties: folders: type: array items: $ref: '#/components/schemas/ProjectFolder' headers: ETag: $ref: '#/components/headers/ETag' Cache-Control: $ref: '#/components/headers/CacheControl' X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '304': description: Not Modified — `If-None-Match` named the current `ETag`, so the body is not sent headers: ETag: $ref: '#/components/headers/ETag' Cache-Control: $ref: '#/components/headers/CacheControl' X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '400': description: 'Invalid request: a body the schema refuses — a wrong type, an unknown key, a missing field, a NUL or an unpaired surrogate, a whole number beyond 2^53 − 1 (`INVALID_BODY`, every problem under `data.issues`); a query parameter the operation does not take, given twice or left blank (`INVALID_QUERY`); on a list, a `limit` that is not a whole number (`INVALID_LIMIT`) or a `cursor` this list did not answer (`INVALID_CURSOR`)' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '401': description: Missing or invalid API key (`UNAUTHORIZED`) content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' WWW-Authenticate: $ref: '#/components/headers/WWWAuthenticate' '403': description: '`X-Organization-Slug` names an organization the key holder is no member of (`ORG_FORBIDDEN`, with the slugs the key holder may send under `data.organizations`)' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '404': description: Project not found, or `parentId` is not a folder of this project (`FOLDER_NOT_FOUND`); or `X-Organization-Slug` names no organization (`ORG_SLUG_INVALID`, with the slugs the key holder may send under `data.organizations`) content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '405': description: The path exists, but not for this method (`METHOD_NOT_ALLOWED`); `Allow` names the methods it serves content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' Allow: $ref: '#/components/headers/Allow' '429': description: 'The key holder’s request budget is spent (`RATE_LIMITED`): `Retry-After` names the wait in whole seconds and `data.retryAfterMs` in milliseconds; the envelope carries a `requestId`' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' Retry-After: $ref: '#/components/headers/RetryAfter' '500': description: Internal error (`INTERNAL_ERROR`); the envelope carries a `requestId` to quote when reporting it content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '503': description: 'The platform’s database is restarting or cannot be reached (`DATABASE_UNAVAILABLE`): `Retry-After` names the wait in whole seconds — retry with backoff; the envelope carries a `requestId`' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' Retry-After: $ref: '#/components/headers/RetryAfter' post: tags: - Projects summary: Get or create a folder description: 'GET-OR-CREATE semantics: a folder of the same name under the same parent — compared without regard to case, the way sibling names are unique — answers 200 with `created: false` and the STORED spelling; otherwise the folder is created and answered 201 with `created: true`. Two concurrent creates of one name yield one folder (the sibling rule is database-enforced). `name` is trimmed and NFC-normalized; a name that is a path (`a/b`, `a\b`, `.`, `..`) or carries a control character is refused (`FOLDER_NAME_INVALID` — the sentence names the rule broken and `data.issues` names `name`) — the rule a file name and a WebDAV segment follow — and a parent 20 levels deep refuses a child (`FOLDER_DEPTH_EXCEEDED`). `parentId` must name a folder of THIS project (an opaque 404 otherwise); omit it for a root folder — a blank id is a 400 `INVALID_BODY`. Requires the org editor role and project edit access. Charged against the general REST bucket (120/min) and the per-org `folder:mutate` budget the in-app folder actions share.' operationId: getOrCreateProjectFolder security: - bearerAuth: [] parameters: - name: X-Organization-Slug in: header required: false schema: type: string description: The organization to operate on. STRICTLY REQUIRED on every call, reads included, when the key holder belongs to more than one organization — without it such requests are refused with 400 `ORG_SLUG_REQUIRED` (the dashboard’s last-active organization never steers a machine call). A slug that names no organization answers 404 `ORG_SLUG_INVALID`, one the key holder is no member of 403 `ORG_FORBIDDEN`. Single-organization keys may omit it; a blank or whitespace-only value reads as the header absent. Each of the three refusals lists the slugs the key holder may send under `data.organizations`; `GET /api/v1/me` answers them too. - name: id in: path required: true schema: type: string description: Project ID - name: X-Request-Id in: header required: false description: 'Your own correlation id, echoed back on the response and written to the platform’s log: up to 255 characters of letters, digits, `_`, `-` and `=`. Any other value is replaced by a fresh UUID (the response carries the id that was used), and uniqueness of an id you choose is yours to keep.' schema: type: string pattern: ^[A-Za-z0-9_=-]{1,255}$ requestBody: required: true content: application/json: schema: type: object additionalProperties: false required: - name properties: name: type: string minLength: 1 maxLength: 128 description: Trimmed and NFC-normalized; matched against siblings without regard to case. No path separators (`/`, `\`), no control characters, not `.` or `..`. parentId: type: string minLength: 1 maxLength: 64 description: Parent folder id (a folder of this project) — present with a value, or omitted for a root folder; blank is refused responses: '200': description: The existing folder content: application/json: schema: $ref: '#/components/schemas/ProjectFolderResult' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '201': description: The created folder content: application/json: schema: $ref: '#/components/schemas/ProjectFolderResult' headers: Location: $ref: '#/components/headers/Location' X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '400': description: A body the schema refuses (`INVALID_BODY`), a name that is a path or carries a control character (`FOLDER_NAME_INVALID`, the sentence naming the rule and `data.issues` naming `name`), or a parent at the depth cap (`FOLDER_DEPTH_EXCEEDED`) content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '401': description: Missing or invalid API key (`UNAUTHORIZED`) content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' WWW-Authenticate: $ref: '#/components/headers/WWWAuthenticate' '403': description: No write access to an active project; or `X-Organization-Slug` names an organization the key holder is no member of (`ORG_FORBIDDEN`, with the slugs the key holder may send under `data.organizations`) content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '404': description: Project or parent folder not found; or `X-Organization-Slug` names no organization (`ORG_SLUG_INVALID`, with the slugs the key holder may send under `data.organizations`) content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '405': description: The path exists, but not for this method (`METHOD_NOT_ALLOWED`); `Allow` names the methods it serves content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' Allow: $ref: '#/components/headers/Allow' '409': description: A same-name sibling appeared while the folder was being created and could not be re-read (`FOLDER_NAME_TAKEN`) — retry the call, it answers the existing folder content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '413': description: The body exceeds this operation’s byte cap — 1 MiB unless the description says otherwise (`BODY_TOO_LARGE`); the envelope carries a `requestId` content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '429': description: 'The key holder’s request budget is spent (`RATE_LIMITED`): `Retry-After` names the wait in whole seconds and `data.retryAfterMs` in milliseconds; the envelope carries a `requestId`' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' Retry-After: $ref: '#/components/headers/RetryAfter' '500': description: Internal error (`INTERNAL_ERROR`); the envelope carries a `requestId` to quote when reporting it content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '503': description: 'The platform’s database is restarting or cannot be reached (`DATABASE_UNAVAILABLE`): `Retry-After` names the wait in whole seconds — retry with backoff; the envelope carries a `requestId`' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' Retry-After: $ref: '#/components/headers/RetryAfter' /api/v1/projects/{id}/folders/{folderId}: get: tags: - Projects summary: Get a project folder description: One folder of this project with its `parentId` (null at the root) — what a file’s `folderId` resolves to, and, parent by parent, its path. A folder of another project or organization, or an absent id, answers the same opaque 404 (`FOLDER_NOT_FOUND`). operationId: getProjectFolder security: - bearerAuth: [] parameters: - name: X-Organization-Slug in: header required: false schema: type: string description: The organization to operate on. STRICTLY REQUIRED on every call, reads included, when the key holder belongs to more than one organization — without it such requests are refused with 400 `ORG_SLUG_REQUIRED` (the dashboard’s last-active organization never steers a machine call). A slug that names no organization answers 404 `ORG_SLUG_INVALID`, one the key holder is no member of 403 `ORG_FORBIDDEN`. Single-organization keys may omit it; a blank or whitespace-only value reads as the header absent. Each of the three refusals lists the slugs the key holder may send under `data.organizations`; `GET /api/v1/me` answers them too. - name: id in: path required: true schema: type: string description: Project ID - name: folderId in: path required: true schema: type: string description: Folder ID - name: X-Request-Id in: header required: false description: 'Your own correlation id, echoed back on the response and written to the platform’s log: up to 255 characters of letters, digits, `_`, `-` and `=`. Any other value is replaced by a fresh UUID (the response carries the id that was used), and uniqueness of an id you choose is yours to keep.' schema: type: string pattern: ^[A-Za-z0-9_=-]{1,255}$ responses: '200': description: The folder content: application/json: schema: type: object required: - folder properties: folder: $ref: '#/components/schemas/ProjectFolder' headers: ETag: $ref: '#/components/headers/ETag' Cache-Control: $ref: '#/components/headers/CacheControl' X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '304': description: Not Modified — `If-None-Match` named the current `ETag`, so the body is not sent headers: ETag: $ref: '#/components/headers/ETag' Cache-Control: $ref: '#/components/headers/CacheControl' X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '400': description: 'Invalid request: a body the schema refuses — a wrong type, an unknown key, a missing field, a NUL or an unpaired surrogate, a whole number beyond 2^53 − 1 (`INVALID_BODY`, every problem under `data.issues`); a query parameter the operation does not take, given twice or left blank (`INVALID_QUERY`); on a list, a `limit` that is not a whole number (`INVALID_LIMIT`) or a `cursor` this list did not answer (`INVALID_CURSOR`)' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '401': description: Missing or invalid API key (`UNAUTHORIZED`) content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' WWW-Authenticate: $ref: '#/components/headers/WWWAuthenticate' '403': description: '`X-Organization-Slug` names an organization the key holder is no member of (`ORG_FORBIDDEN`, with the slugs the key holder may send under `data.organizations`)' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '404': description: Project or folder not found (`FOLDER_NOT_FOUND`); or `X-Organization-Slug` names no organization (`ORG_SLUG_INVALID`, with the slugs the key holder may send under `data.organizations`) content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '405': description: The path exists, but not for this method (`METHOD_NOT_ALLOWED`); `Allow` names the methods it serves content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' Allow: $ref: '#/components/headers/Allow' '429': description: 'The key holder’s request budget is spent (`RATE_LIMITED`): `Retry-After` names the wait in whole seconds and `data.retryAfterMs` in milliseconds; the envelope carries a `requestId`' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' Retry-After: $ref: '#/components/headers/RetryAfter' '500': description: Internal error (`INTERNAL_ERROR`); the envelope carries a `requestId` to quote when reporting it content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '503': description: 'The platform’s database is restarting or cannot be reached (`DATABASE_UNAVAILABLE`): `Retry-After` names the wait in whole seconds — retry with backoff; the envelope carries a `requestId`' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' Retry-After: $ref: '#/components/headers/RetryAfter' delete: tags: - Projects summary: Delete a project folder and everything beneath it description: 'Permanent, cascading: every file under the folder (subfolders included) is purged — its document row, its search-corpus rows (released at once, so project search stops matching them) and its blob — then the subtree goes. A protected controlled record or a legal hold anywhere beneath refuses the whole delete before anything is removed. Requires the org editor role and project edit access on an active project; charged against the general REST bucket and the per-org `folder:mutate` budget the in-app folder actions share. A folder of another project or organization answers the same opaque 404.' operationId: deleteProjectFolder security: - bearerAuth: [] parameters: - name: X-Organization-Slug in: header required: false schema: type: string description: The organization to operate on. STRICTLY REQUIRED on every call, reads included, when the key holder belongs to more than one organization — without it such requests are refused with 400 `ORG_SLUG_REQUIRED` (the dashboard’s last-active organization never steers a machine call). A slug that names no organization answers 404 `ORG_SLUG_INVALID`, one the key holder is no member of 403 `ORG_FORBIDDEN`. Single-organization keys may omit it; a blank or whitespace-only value reads as the header absent. Each of the three refusals lists the slugs the key holder may send under `data.organizations`; `GET /api/v1/me` answers them too. - name: id in: path required: true schema: type: string description: Project ID - name: folderId in: path required: true schema: type: string description: Folder ID - name: X-Request-Id in: header required: false description: 'Your own correlation id, echoed back on the response and written to the platform’s log: up to 255 characters of letters, digits, `_`, `-` and `=`. Any other value is replaced by a fresh UUID (the response carries the id that was used), and uniqueness of an id you choose is yours to keep.' schema: type: string pattern: ^[A-Za-z0-9_=-]{1,255}$ responses: '204': description: Deleted, with everything beneath it headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '400': description: 'Invalid request: a body the schema refuses — a wrong type, an unknown key, a missing field, a NUL or an unpaired surrogate, a whole number beyond 2^53 − 1 (`INVALID_BODY`, every problem under `data.issues`); a query parameter the operation does not take, given twice or left blank (`INVALID_QUERY`); on a list, a `limit` that is not a whole number (`INVALID_LIMIT`) or a `cursor` this list did not answer (`INVALID_CURSOR`)' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '401': description: Missing or invalid API key (`UNAUTHORIZED`) content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' WWW-Authenticate: $ref: '#/components/headers/WWWAuthenticate' '403': description: No write access to an active project; or `X-Organization-Slug` names an organization the key holder is no member of (`ORG_FORBIDDEN`, with the slugs the key holder may send under `data.organizations`) content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '404': description: Project or folder not found (`FOLDER_NOT_FOUND`); or `X-Organization-Slug` names no organization (`ORG_SLUG_INVALID`, with the slugs the key holder may send under `data.organizations`) content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '405': description: The path exists, but not for this method (`METHOD_NOT_ALLOWED`); `Allow` names the methods it serves content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' Allow: $ref: '#/components/headers/Allow' '409': description: A protected record beneath the folder (`DOCUMENT_RECORD_PROTECTED`) or a legal hold (`LEGAL_HOLD_ACTIVE`) content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '429': description: 'The key holder’s request budget is spent (`RATE_LIMITED`): `Retry-After` names the wait in whole seconds and `data.retryAfterMs` in milliseconds; the envelope carries a `requestId`' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' Retry-After: $ref: '#/components/headers/RetryAfter' '500': description: Internal error (`INTERNAL_ERROR`); the envelope carries a `requestId` to quote when reporting it content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '503': description: 'The purge could not remove every dead surface (`PURGE_INCOMPLETE`): nothing was removed, retry the delete; or the platform’s database is restarting or cannot be reached (`DATABASE_UNAVAILABLE`): `Retry-After` names the wait in whole seconds — retry with backoff; the envelope carries a `requestId`' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' Retry-After: $ref: '#/components/headers/RetryAfter' /api/v1/projects/{id}/uploads: post: tags: - Projects summary: Mint an upload handoff description: 'Answers where to send the bytes: a presigned `PUT` URL for the organization’s bucket (every 0.5 blob is S3-backed) plus the `s3Ref` to bind afterwards as `fileId` — a declared `contentType` is signed into the URL, so the PUT must carry that exact `Content-Type` header (omit `contentType` and the PUT has no header requirement). The `uploadId` is a single-use intent valid until `expiresAt` (30 minutes); complete the upload with `POST /api/v1/projects/{id}/files`. The signed URL is valid for exactly as long as the intent — `expiresAt` is the one deadline for both. Send `fileName` to have the bind’s type rules run here too: the organization’s extension and MIME allowlists and the platform’s format allowlist refuse a name they would refuse at the bind — a name without an extension included, whatever `contentType` says — 400 `UPLOAD_POLICY_REJECTED` / `UNSUPPORTED_FILE_TYPE` before anything is presigned, so the bytes never travel. The answer names `maxBytes`, the largest file this organization accepts for the declared type (the platform ceiling, 100 MiB, or the organization’s lower cap); declare `size` and the size and volume caps are judged here too — 400 `FILE_TOO_LARGE` / `UPLOAD_POLICY_REJECTED` with `data.limitBytes` — where they were discoverable only by uploading past them (the bind judges the landed bytes against the same caps; the declared size is a pre-check, never compared with what landed, so understating it buys nothing). A handoff never bound — or bound and refused — is reclaimed lazily: 24 hours after its 30-minute expiry, on the next mint into the organization, the blob is deleted with the intent, so a crashed worker leaves no permanent orphan and needs no delete call. Requires the org editor role and project edit access. Uses the upload lane bucket (240/min — one logical upload is several calls; keyed on the key holder like every REST budget).' operationId: createProjectUpload security: - bearerAuth: [] parameters: - name: X-Organization-Slug in: header required: false schema: type: string description: The organization to operate on. STRICTLY REQUIRED on every call, reads included, when the key holder belongs to more than one organization — without it such requests are refused with 400 `ORG_SLUG_REQUIRED` (the dashboard’s last-active organization never steers a machine call). A slug that names no organization answers 404 `ORG_SLUG_INVALID`, one the key holder is no member of 403 `ORG_FORBIDDEN`. Single-organization keys may omit it; a blank or whitespace-only value reads as the header absent. Each of the three refusals lists the slugs the key holder may send under `data.organizations`; `GET /api/v1/me` answers them too. - name: id in: path required: true schema: type: string description: Project ID - name: X-Request-Id in: header required: false description: 'Your own correlation id, echoed back on the response and written to the platform’s log: up to 255 characters of letters, digits, `_`, `-` and `=`. Any other value is replaced by a fresh UUID (the response carries the id that was used), and uniqueness of an id you choose is yours to keep.' schema: type: string pattern: ^[A-Za-z0-9_=-]{1,255}$ requestBody: required: false content: application/json: schema: type: object additionalProperties: false properties: fileName: type: string minLength: 1 maxLength: 1024 description: Optional. Checked against the bind’s own rules — a file name (no path separators, no `..`, no control characters; trimmed and NFC-normalized first) ending in an extension the upload policy and the format allowlist accept (a name without one is refused whatever `contentType` says) — so a bad name fails here, before the bytes are uploaded; the bind’s `fileName` is what is stored. contentType: type: string maxLength: 255 size: type: integer minimum: 0 description: 'Optional. The bytes about to be uploaded: a size the bind would refuse — over the platform ceiling (`FILE_TOO_LARGE`), over the organization’s cap for the type, or past the key holder’s volume quota (`UPLOAD_POLICY_REJECTED`) — fails here, before anything is presigned. The bind judges the landed size regardless.' responses: '200': description: The upload handoff content: application/json: schema: $ref: '#/components/schemas/ProjectUploadHandoff' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '400': description: Malformed body (`INVALID_BODY`); `fileName` names a type the organization’s upload policy (`UPLOAD_POLICY_REJECTED`) or the platform’s format allowlist (`UNSUPPORTED_FILE_TYPE`) refuses; or `size` is over the platform ceiling (`FILE_TOO_LARGE`, `data.limitBytes`), the organization’s cap or the volume quota (`UPLOAD_POLICY_REJECTED`, `data.reasonCode` `file_too_large` / `volume_exceeded`) content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '401': description: Missing or invalid API key (`UNAUTHORIZED`) content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' WWW-Authenticate: $ref: '#/components/headers/WWWAuthenticate' '403': description: No write access to an active project; or `X-Organization-Slug` names an organization the key holder is no member of (`ORG_FORBIDDEN`, with the slugs the key holder may send under `data.organizations`) content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '404': description: Project not found; or `X-Organization-Slug` names no organization (`ORG_SLUG_INVALID`, with the slugs the key holder may send under `data.organizations`) content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '405': description: The path exists, but not for this method (`METHOD_NOT_ALLOWED`); `Allow` names the methods it serves content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' Allow: $ref: '#/components/headers/Allow' '413': description: The body exceeds this operation’s byte cap — 1 MiB unless the description says otherwise (`BODY_TOO_LARGE`); the envelope carries a `requestId` content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '429': description: 'The key holder’s request budget is spent (`RATE_LIMITED`): `Retry-After` names the wait in whole seconds and `data.retryAfterMs` in milliseconds; the envelope carries a `requestId`' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' Retry-After: $ref: '#/components/headers/RetryAfter' '500': description: Internal error (`INTERNAL_ERROR`); the envelope carries a `requestId` to quote when reporting it content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '503': description: 'No object store is configured for this deployment (`OBJECT_STORE_UNCONFIGURED`) — nothing can be uploaded until an operator connects one; or the platform’s database is restarting or cannot be reached (`DATABASE_UNAVAILABLE`): `Retry-After` names the wait in whole seconds — retry with backoff; the envelope carries a `requestId`' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' Retry-After: $ref: '#/components/headers/RetryAfter' /api/v1/projects/{id}/files/{documentId}: get: tags: - Projects summary: Read one project file description: The row `GET /api/v1/projects/{id}/files` lists — `fileName`, `folderId`, `mimeType`, `size` and where the file stands in the search corpus (`indexing`) — for one file, so a poller waiting for ONE upload’s `indexing` (after a bind, after `retry-indexing`) reads that file instead of walking the whole listing. Answers an `ETag` and 304 like every JSON read, so an `If-None-Match` poll costs no body while nothing has changed. Requires read access to the project. A file of another project or organization, a document without a file, and a trashed or absent one answer the same opaque 404 `FILE_NOT_FOUND`. Project files never appear in `GET /api/v1/documents/{id}` — that is the knowledge hub’s surface. operationId: getProjectFile security: - bearerAuth: [] parameters: - name: X-Organization-Slug in: header required: false schema: type: string description: The organization to operate on. STRICTLY REQUIRED on every call, reads included, when the key holder belongs to more than one organization — without it such requests are refused with 400 `ORG_SLUG_REQUIRED` (the dashboard’s last-active organization never steers a machine call). A slug that names no organization answers 404 `ORG_SLUG_INVALID`, one the key holder is no member of 403 `ORG_FORBIDDEN`. Single-organization keys may omit it; a blank or whitespace-only value reads as the header absent. Each of the three refusals lists the slugs the key holder may send under `data.organizations`; `GET /api/v1/me` answers them too. - name: id in: path required: true schema: type: string description: Project ID - name: documentId in: path required: true schema: type: string description: The file’s document id - name: X-Request-Id in: header required: false description: 'Your own correlation id, echoed back on the response and written to the platform’s log: up to 255 characters of letters, digits, `_`, `-` and `=`. Any other value is replaced by a fresh UUID (the response carries the id that was used), and uniqueness of an id you choose is yours to keep.' schema: type: string pattern: ^[A-Za-z0-9_=-]{1,255}$ responses: '200': description: The file content: application/json: schema: type: object required: - file properties: file: $ref: '#/components/schemas/ProjectFile' headers: ETag: $ref: '#/components/headers/ETag' Cache-Control: $ref: '#/components/headers/CacheControl' X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '304': description: Not Modified — `If-None-Match` named the current `ETag`, so the body is not sent headers: ETag: $ref: '#/components/headers/ETag' Cache-Control: $ref: '#/components/headers/CacheControl' X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '400': description: 'Invalid request: a body the schema refuses — a wrong type, an unknown key, a missing field, a NUL or an unpaired surrogate, a whole number beyond 2^53 − 1 (`INVALID_BODY`, every problem under `data.issues`); a query parameter the operation does not take, given twice or left blank (`INVALID_QUERY`); on a list, a `limit` that is not a whole number (`INVALID_LIMIT`) or a `cursor` this list did not answer (`INVALID_CURSOR`)' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '401': description: Missing or invalid API key (`UNAUTHORIZED`) content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' WWW-Authenticate: $ref: '#/components/headers/WWWAuthenticate' '403': description: '`X-Organization-Slug` names an organization the key holder is no member of (`ORG_FORBIDDEN`, with the slugs the key holder may send under `data.organizations`)' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '404': description: Project not found (`PROJECT_NOT_FOUND`), or the file is not a live file of this project (`FILE_NOT_FOUND`); or `X-Organization-Slug` names no organization (`ORG_SLUG_INVALID`, with the slugs the key holder may send under `data.organizations`) content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '405': description: The path exists, but not for this method (`METHOD_NOT_ALLOWED`); `Allow` names the methods it serves content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' Allow: $ref: '#/components/headers/Allow' '429': description: 'The key holder’s request budget is spent (`RATE_LIMITED`): `Retry-After` names the wait in whole seconds and `data.retryAfterMs` in milliseconds; the envelope carries a `requestId`' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' Retry-After: $ref: '#/components/headers/RetryAfter' '500': description: Internal error (`INTERNAL_ERROR`); the envelope carries a `requestId` to quote when reporting it content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '503': description: 'The platform’s database is restarting or cannot be reached (`DATABASE_UNAVAILABLE`): `Retry-After` names the wait in whole seconds — retry with backoff; the envelope carries a `requestId`' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' Retry-After: $ref: '#/components/headers/RetryAfter' delete: tags: - Projects summary: Delete a project file description: 'Permanent: the file’s document row, its search-corpus rows and its blob are purged through the same lane every hard delete uses (controlled-record protection, legal holds, the audit row). Requires the org editor role and project edit access on an active project. A file of another project or organization, a document without a file, and a trashed or absent one answer the same opaque 404.' operationId: deleteProjectFile security: - bearerAuth: [] parameters: - name: X-Organization-Slug in: header required: false schema: type: string description: The organization to operate on. STRICTLY REQUIRED on every call, reads included, when the key holder belongs to more than one organization — without it such requests are refused with 400 `ORG_SLUG_REQUIRED` (the dashboard’s last-active organization never steers a machine call). A slug that names no organization answers 404 `ORG_SLUG_INVALID`, one the key holder is no member of 403 `ORG_FORBIDDEN`. Single-organization keys may omit it; a blank or whitespace-only value reads as the header absent. Each of the three refusals lists the slugs the key holder may send under `data.organizations`; `GET /api/v1/me` answers them too. - name: id in: path required: true schema: type: string description: Project ID - name: documentId in: path required: true schema: type: string description: File (document) ID - name: X-Request-Id in: header required: false description: 'Your own correlation id, echoed back on the response and written to the platform’s log: up to 255 characters of letters, digits, `_`, `-` and `=`. Any other value is replaced by a fresh UUID (the response carries the id that was used), and uniqueness of an id you choose is yours to keep.' schema: type: string pattern: ^[A-Za-z0-9_=-]{1,255}$ responses: '204': description: Deleted headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '400': description: 'Invalid request: a body the schema refuses — a wrong type, an unknown key, a missing field, a NUL or an unpaired surrogate, a whole number beyond 2^53 − 1 (`INVALID_BODY`, every problem under `data.issues`); a query parameter the operation does not take, given twice or left blank (`INVALID_QUERY`); on a list, a `limit` that is not a whole number (`INVALID_LIMIT`) or a `cursor` this list did not answer (`INVALID_CURSOR`)' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '401': description: Missing or invalid API key (`UNAUTHORIZED`) content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' WWW-Authenticate: $ref: '#/components/headers/WWWAuthenticate' '403': description: No write access to an active project; or `X-Organization-Slug` names an organization the key holder is no member of (`ORG_FORBIDDEN`, with the slugs the key holder may send under `data.organizations`) content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '404': description: Project or file not found (`FILE_NOT_FOUND`); or `X-Organization-Slug` names no organization (`ORG_SLUG_INVALID`, with the slugs the key holder may send under `data.organizations`) content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '405': description: The path exists, but not for this method (`METHOD_NOT_ALLOWED`); `Allow` names the methods it serves content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' Allow: $ref: '#/components/headers/Allow' '409': description: A protected record (`DOCUMENT_RECORD_PROTECTED`) or a legal hold (`LEGAL_HOLD_ACTIVE`) content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '429': description: 'The key holder’s request budget is spent (`RATE_LIMITED`): `Retry-After` names the wait in whole seconds and `data.retryAfterMs` in milliseconds; the envelope carries a `requestId`' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' Retry-After: $ref: '#/components/headers/RetryAfter' '500': description: Internal error (`INTERNAL_ERROR`); the envelope carries a `requestId` to quote when reporting it content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '503': description: 'The purge could not remove every dead surface (`PURGE_INCOMPLETE`): the row was kept so the delete can be retried; or the platform’s database is restarting or cannot be reached (`DATABASE_UNAVAILABLE`): `Retry-After` names the wait in whole seconds — retry with backoff; the envelope carries a `requestId`' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' Retry-After: $ref: '#/components/headers/RetryAfter' /api/v1/projects/{id}/files/{documentId}/content: get: tags: - Projects summary: Download a project file description: 'The result lane: fetch the bytes of a file in the project — including what an automation filed back (a prepared return, a report). The bytes are streamed in the response itself (**200**, no redirect to follow), with the stored name in an RFC 6266 `Content-Disposition` (`filename` and `filename*`), the stored `Content-Type`, `Content-Length`, `ETag` and `Last-Modified`; `Range` is honoured (206, or a bodiless 416 for a range the file cannot satisfy — judged against the stored size before any byte is fetched) and a HEAD request answers the headers alone — `Content-Length`, `ETag`, `Last-Modified`, `Accept-Ranges` — ignoring `Range`. Visibility is the minting user’s; a cross-project, cross-organization, trashed, or absent file answers the same opaque 404, and a file whose bytes the store no longer holds does too. A store that does not answer is a 503 with `Retry-After`.' operationId: downloadProjectFile security: - bearerAuth: [] parameters: - name: X-Organization-Slug in: header required: false schema: type: string description: The organization to operate on. STRICTLY REQUIRED on every call, reads included, when the key holder belongs to more than one organization — without it such requests are refused with 400 `ORG_SLUG_REQUIRED` (the dashboard’s last-active organization never steers a machine call). A slug that names no organization answers 404 `ORG_SLUG_INVALID`, one the key holder is no member of 403 `ORG_FORBIDDEN`. Single-organization keys may omit it; a blank or whitespace-only value reads as the header absent. Each of the three refusals lists the slugs the key holder may send under `data.organizations`; `GET /api/v1/me` answers them too. - name: id in: path required: true schema: type: string description: Project ID - name: documentId in: path required: true schema: type: string description: File (document) ID - name: If-None-Match in: header required: false schema: type: string description: 'The `ETag` a previous answer carried (as received — the compressing edge suffixes the tag of a text file it compressed, and that form matches too): unchanged bytes answer 304 with no body. Takes precedence over `If-Modified-Since`.' - name: If-Modified-Since in: header required: false schema: type: string description: 'The `Last-Modified` a previous answer carried: bytes not modified since answer 304 with no body, judged at the whole-second precision an HTTP date carries. Ignored when `If-None-Match` is present. On a content-only Hub document the date is the document’s own `updatedAt`, which a title or metadata patch moves too — `If-None-Match` follows the bytes alone.' - name: Range in: header required: false schema: type: string description: 'A single byte range (`bytes=0-1023`, `bytes=1024-`, `bytes=-512`): 206 with `Content-Range`, or a bodiless 416 for a range starting at or past the end of the file. Several ranges, another unit or a spec that does not parse are ignored and the whole file answers 200; a HEAD ignores it.' - name: If-Range in: header required: false schema: type: string description: 'With `Range`, the `ETag` the partial download started from: the range is served only while the file is still that representation, otherwise the whole file answers 200.' - name: X-Request-Id in: header required: false description: 'Your own correlation id, echoed back on the response and written to the platform’s log: up to 255 characters of letters, digits, `_`, `-` and `=`. Any other value is replaced by a fresh UUID (the response carries the id that was used), and uniqueness of an id you choose is yours to keep.' schema: type: string pattern: ^[A-Za-z0-9_=-]{1,255}$ responses: '200': description: The file bytes, named by `Content-Disposition` headers: Content-Disposition: $ref: '#/components/headers/ContentDisposition' Content-Length: $ref: '#/components/headers/ContentLength' ETag: $ref: '#/components/headers/ETag' Last-Modified: $ref: '#/components/headers/LastModified' Accept-Ranges: $ref: '#/components/headers/AcceptRanges' X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' content: '*/*': schema: type: string format: binary '206': description: The bytes of a satisfied `Range`; `Content-Range` names the slice and the file’s size. Ranges are clamped to the file (`bytes=0-99999999` on a shorter file answers what exists); a `Range` the server cannot read (`bytes=abc`, several ranges) is ignored and the whole file answers 200 headers: Content-Range: $ref: '#/components/headers/ContentRange' Content-Disposition: $ref: '#/components/headers/ContentDisposition' Content-Length: $ref: '#/components/headers/ContentLength' ETag: $ref: '#/components/headers/ETag' Last-Modified: $ref: '#/components/headers/LastModified' Accept-Ranges: $ref: '#/components/headers/AcceptRanges' X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' content: '*/*': schema: type: string format: binary '304': description: Not Modified — `If-None-Match` named the current `ETag` (or `If-Modified-Since` the current `Last-Modified`), so no bytes are sent; `ETag`, `Last-Modified` and `Accept-Ranges` ride along headers: ETag: $ref: '#/components/headers/ETag' Last-Modified: $ref: '#/components/headers/LastModified' Cache-Control: $ref: '#/components/headers/CacheControl' X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '400': description: 'Invalid request: a body the schema refuses — a wrong type, an unknown key, a missing field, a NUL or an unpaired surrogate, a whole number beyond 2^53 − 1 (`INVALID_BODY`, every problem under `data.issues`); a query parameter the operation does not take, given twice or left blank (`INVALID_QUERY`); on a list, a `limit` that is not a whole number (`INVALID_LIMIT`) or a `cursor` this list did not answer (`INVALID_CURSOR`)' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '401': description: Missing or invalid API key (`UNAUTHORIZED`) content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' WWW-Authenticate: $ref: '#/components/headers/WWWAuthenticate' '403': description: '`X-Organization-Slug` names an organization the key holder is no member of (`ORG_FORBIDDEN`, with the slugs the key holder may send under `data.organizations`)' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '404': description: File not found (`FILE_NOT_FOUND`); or `X-Organization-Slug` names no organization (`ORG_SLUG_INVALID`, with the slugs the key holder may send under `data.organizations`) content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '405': description: The path exists, but not for this method (`METHOD_NOT_ALLOWED`); `Allow` names the methods it serves content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' Allow: $ref: '#/components/headers/Allow' '416': description: 'The range starts at or past the end of the file (`bytes=-` — what a resumed download sends once its copy is complete): an empty body (`Content-Length: 0`), no envelope, `Content-Range: bytes */` naming the size, no byte fetched from the store' headers: Content-Range: $ref: '#/components/headers/ContentRange' Accept-Ranges: $ref: '#/components/headers/AcceptRanges' Content-Length: $ref: '#/components/headers/ContentLength' X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '429': description: 'The key holder’s request budget is spent (`RATE_LIMITED`): `Retry-After` names the wait in whole seconds and `data.retryAfterMs` in milliseconds; the envelope carries a `requestId`' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' Retry-After: $ref: '#/components/headers/RetryAfter' '500': description: Internal error (`INTERNAL_ERROR`); the envelope carries a `requestId` to quote when reporting it content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '503': description: 'The object store did not serve the file (`OBJECT_STORE_UNAVAILABLE`, with `Retry-After`) or none is configured (`OBJECT_STORE_UNCONFIGURED`); or the platform’s database is restarting or cannot be reached (`DATABASE_UNAVAILABLE`): `Retry-After` names the wait in whole seconds — retry with backoff; the envelope carries a `requestId`' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' Retry-After: $ref: '#/components/headers/RetryAfter' /api/v1/projects/{id}/files/{documentId}/retry-indexing: post: tags: - Projects summary: Index a project file now description: 'The REST twin of **Index now** on the project’s Knowledge tab: re-queues the file’s blob for indexing through the core behind `POST /api/v1/documents/{id}/retry-indexing` (which answers 404 for a project file) and answers the same `{status, reason}`. A file bound with the default `skipRagIndexing: true` is opted back in by this request — the explicit ask is the opt-in — and answers `indexing`; poll its `indexing` on `GET /api/v1/projects/{id}/files/{documentId}` (304 while unchanged) until `completed`, from when `POST …/knowledge/search` finds it, or `unsupported` — terminal, `indexing.errorCode` says why. `skipped` names its `reason`: a blob the platform does not track (`untracked-blob`), a file type no extractor can read (`unsupported` — terminal; the row’s `indexing.error` says why), or a fresh index job already queued or running (`in-progress` — poll instead); `content-only` cannot occur here, a project file always has a blob. Requires the org editor role and project edit access on an active project — the gate `DELETE …/files/{documentId}` runs — plus a role that can write documents (403 `RBAC_FORBIDDEN`); the same budget as the Hub door, 10 retries per user per minute, answers 429 `RATE_LIMITED` with `Retry-After`. A file of another project or organization, a document without a file, and a trashed or absent one answer the same opaque 404 (`FILE_NOT_FOUND`).' operationId: retryProjectFileIndexing security: - bearerAuth: [] parameters: - name: X-Organization-Slug in: header required: false schema: type: string description: The organization to operate on. STRICTLY REQUIRED on every call, reads included, when the key holder belongs to more than one organization — without it such requests are refused with 400 `ORG_SLUG_REQUIRED` (the dashboard’s last-active organization never steers a machine call). A slug that names no organization answers 404 `ORG_SLUG_INVALID`, one the key holder is no member of 403 `ORG_FORBIDDEN`. Single-organization keys may omit it; a blank or whitespace-only value reads as the header absent. Each of the three refusals lists the slugs the key holder may send under `data.organizations`; `GET /api/v1/me` answers them too. - name: id in: path required: true schema: type: string description: Project ID - name: documentId in: path required: true schema: type: string description: File (document) ID - name: X-Request-Id in: header required: false description: 'Your own correlation id, echoed back on the response and written to the platform’s log: up to 255 characters of letters, digits, `_`, `-` and `=`. Any other value is replaced by a fresh UUID (the response carries the id that was used), and uniqueness of an id you choose is yours to keep.' schema: type: string pattern: ^[A-Za-z0-9_=-]{1,255}$ responses: '200': description: Whether indexing was queued content: application/json: schema: $ref: '#/components/schemas/RetryIndexingResult' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '400': description: 'Invalid request: a body the schema refuses — a wrong type, an unknown key, a missing field, a NUL or an unpaired surrogate, a whole number beyond 2^53 − 1 (`INVALID_BODY`, every problem under `data.issues`); a query parameter the operation does not take, given twice or left blank (`INVALID_QUERY`); on a list, a `limit` that is not a whole number (`INVALID_LIMIT`) or a `cursor` this list did not answer (`INVALID_CURSOR`)' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '401': description: Missing or invalid API key (`UNAUTHORIZED`) content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' WWW-Authenticate: $ref: '#/components/headers/WWWAuthenticate' '403': description: No write access to an active project, or a role that cannot write documents (`RBAC_FORBIDDEN`); or `X-Organization-Slug` names an organization the key holder is no member of (`ORG_FORBIDDEN`, with the slugs the key holder may send under `data.organizations`) content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '404': description: Project or file not found (`FILE_NOT_FOUND`); or `X-Organization-Slug` names no organization (`ORG_SLUG_INVALID`, with the slugs the key holder may send under `data.organizations`) content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '405': description: The path exists, but not for this method (`METHOD_NOT_ALLOWED`); `Allow` names the methods it serves content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' Allow: $ref: '#/components/headers/Allow' '429': description: 'The key holder’s request budget is spent (`RATE_LIMITED`): `Retry-After` names the wait in whole seconds and `data.retryAfterMs` in milliseconds; the envelope carries a `requestId`' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' Retry-After: $ref: '#/components/headers/RetryAfter' '500': description: Internal error (`INTERNAL_ERROR`); the envelope carries a `requestId` to quote when reporting it content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '503': description: 'The platform’s database is restarting or cannot be reached (`DATABASE_UNAVAILABLE`): `Retry-After` names the wait in whole seconds — retry with backoff; the envelope carries a `requestId`' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' Retry-After: $ref: '#/components/headers/RetryAfter' /api/v1/projects/{id}/files: get: tags: - Projects summary: List the project’s files description: Keyset-paginated (`cursor` + `limit`, answering `{files, isDone, continueCursor}` — pass `continueCursor` back as `cursor` until `isDone`; while more pages remain the answer also carries `cursor`, the same token under its pre-1.5.0 name — deprecated, served for at least two more minor versions). `folderId` narrows to one folder of this project. Project files never appear in `GET /api/v1/documents` — that is the knowledge hub’s surface. Newest first — ordered by `createdAt`, then `id`, both descending; the cursor is that pair, so a file bound while you page lands ahead of the cursor and is not served by that pass, and a deleted file simply drops out. operationId: listProjectFiles security: - bearerAuth: [] parameters: - name: X-Organization-Slug in: header required: false schema: type: string description: The organization to operate on. STRICTLY REQUIRED on every call, reads included, when the key holder belongs to more than one organization — without it such requests are refused with 400 `ORG_SLUG_REQUIRED` (the dashboard’s last-active organization never steers a machine call). A slug that names no organization answers 404 `ORG_SLUG_INVALID`, one the key holder is no member of 403 `ORG_FORBIDDEN`. Single-organization keys may omit it; a blank or whitespace-only value reads as the header absent. Each of the three refusals lists the slugs the key holder may send under `data.organizations`; `GET /api/v1/me` answers them too. - name: id in: path required: true schema: type: string description: Project ID - name: folderId in: query required: false schema: type: string description: Only files inside this project folder — a folder id of this project (any other id is 404 `FOLDER_NOT_FOUND`), or `root` for the files in no folder. Omit it to list every file of the project. - name: cursor in: query required: false schema: type: string description: The previous page’s `continueCursor`, unchanged — opaque; omit for the first page. A value this list did not answer is refused with 400 `INVALID_CURSOR` rather than read as the first page - name: limit in: query required: false schema: type: integer minimum: 1 maximum: 100 default: 25 description: Page size, 1..100 (default 25; out-of-range numbers are clamped, a value that is not a number answers 400 `INVALID_LIMIT`) - name: X-Request-Id in: header required: false description: 'Your own correlation id, echoed back on the response and written to the platform’s log: up to 255 characters of letters, digits, `_`, `-` and `=`. Any other value is replaced by a fresh UUID (the response carries the id that was used), and uniqueness of an id you choose is yours to keep.' schema: type: string pattern: ^[A-Za-z0-9_=-]{1,255}$ responses: '200': description: The files content: application/json: schema: type: object x-tale-pagination: keyset required: - files - isDone - continueCursor properties: files: type: array items: $ref: '#/components/schemas/ProjectFile' isDone: type: boolean description: True when no more pages remain continueCursor: type: string description: Pass back as `cursor` for the next page; empty when `isDone` cursor: type: string deprecated: true description: The same token as `continueCursor`, present only while more pages remain — the pre-1.5.0 spelling, served for at least two more minor versions; read `continueCursor` headers: ETag: $ref: '#/components/headers/ETag' Cache-Control: $ref: '#/components/headers/CacheControl' X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '304': description: Not Modified — `If-None-Match` named the current `ETag`, so the body is not sent headers: ETag: $ref: '#/components/headers/ETag' Cache-Control: $ref: '#/components/headers/CacheControl' X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '400': description: 'Invalid request: a body the schema refuses — a wrong type, an unknown key, a missing field, a NUL or an unpaired surrogate, a whole number beyond 2^53 − 1 (`INVALID_BODY`, every problem under `data.issues`); a query parameter the operation does not take, given twice or left blank (`INVALID_QUERY`); on a list, a `limit` that is not a whole number (`INVALID_LIMIT`) or a `cursor` this list did not answer (`INVALID_CURSOR`)' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '401': description: Missing or invalid API key (`UNAUTHORIZED`) content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' WWW-Authenticate: $ref: '#/components/headers/WWWAuthenticate' '403': description: '`X-Organization-Slug` names an organization the key holder is no member of (`ORG_FORBIDDEN`, with the slugs the key holder may send under `data.organizations`)' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '404': description: Project or folder not found; or `X-Organization-Slug` names no organization (`ORG_SLUG_INVALID`, with the slugs the key holder may send under `data.organizations`) content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '405': description: The path exists, but not for this method (`METHOD_NOT_ALLOWED`); `Allow` names the methods it serves content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' Allow: $ref: '#/components/headers/Allow' '429': description: 'The key holder’s request budget is spent (`RATE_LIMITED`): `Retry-After` names the wait in whole seconds and `data.retryAfterMs` in milliseconds; the envelope carries a `requestId`' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' Retry-After: $ref: '#/components/headers/RetryAfter' '500': description: Internal error (`INTERNAL_ERROR`); the envelope carries a `requestId` to quote when reporting it content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '503': description: 'The platform’s database is restarting or cannot be reached (`DATABASE_UNAVAILABLE`): `Retry-After` names the wait in whole seconds — retry with backoff; the envelope carries a `requestId`' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' Retry-After: $ref: '#/components/headers/RetryAfter' post: tags: - Projects summary: Bind an uploaded blob as a project file description: 'Consumes the single-use `uploadId` minted by `POST …/uploads` and creates the document. `fileId` is the `s3Ref` the handoff answered. `folderId` is REQUIRED and must belong to this project. `skipRagIndexing` DEFAULTS TO TRUE — project ledger files are working material, not organization knowledge; pass `false` explicitly to index the file into the knowledge corpus. The organization’s upload policy applies to the landed bytes (authoritative size): a size cap, extension allowlist, or per-user volume refusal answers 400 with code `UPLOAD_POLICY_REJECTED` / `FILE_TOO_LARGE` / `UNSUPPORTED_FILE_TYPE` (the allowlist keys on the file name’s extension, which the name must carry; a declared `contentType` is a hint resolved against it, never a substitute), and the organization’s `file:upload` budget answers 429. `fileName` must be a file name — no path separators, no `..`, no control characters — and is stored trimmed and NFC-normalized, the rule a folder name follows. A refusal rolls the intent consume back, so the handshake survives a corrected retry. Uses the upload lane bucket (240/min, keyed on the key holder).' operationId: bindProjectFile security: - bearerAuth: [] parameters: - name: X-Organization-Slug in: header required: false schema: type: string description: The organization to operate on. STRICTLY REQUIRED on every call, reads included, when the key holder belongs to more than one organization — without it such requests are refused with 400 `ORG_SLUG_REQUIRED` (the dashboard’s last-active organization never steers a machine call). A slug that names no organization answers 404 `ORG_SLUG_INVALID`, one the key holder is no member of 403 `ORG_FORBIDDEN`. Single-organization keys may omit it; a blank or whitespace-only value reads as the header absent. Each of the three refusals lists the slugs the key holder may send under `data.organizations`; `GET /api/v1/me` answers them too. - name: id in: path required: true schema: type: string description: Project ID - name: X-Request-Id in: header required: false description: 'Your own correlation id, echoed back on the response and written to the platform’s log: up to 255 characters of letters, digits, `_`, `-` and `=`. Any other value is replaced by a fresh UUID (the response carries the id that was used), and uniqueness of an id you choose is yours to keep.' schema: type: string pattern: ^[A-Za-z0-9_=-]{1,255}$ requestBody: required: true content: application/json: schema: type: object additionalProperties: false required: - uploadId - fileId - folderId - fileName properties: uploadId: type: string minLength: 1 maxLength: 64 fileId: type: string minLength: 1 maxLength: 2048 folderId: type: string minLength: 1 maxLength: 64 fileName: type: string minLength: 1 maxLength: 1024 contentType: type: string maxLength: 255 skipRagIndexing: type: boolean default: true responses: '201': description: The bound file content: application/json: schema: type: object required: - file properties: file: type: object required: - id - fileName - folderId - projectId - size - mimeType properties: id: type: string fileName: type: string folderId: type: string projectId: type: string size: type: integer description: The landed bytes — the authoritative size the upload policy judged mimeType: type: string description: The type the gate resolved from the file name (a declared `contentType` is a hint), as the row was written headers: Location: $ref: '#/components/headers/Location' X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '400': description: Malformed body, or the upload policy refused the file (`UPLOAD_POLICY_REJECTED`, `FILE_TOO_LARGE`, `UNSUPPORTED_FILE_TYPE`) content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '401': description: Missing or invalid API key (`UNAUTHORIZED`) content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' WWW-Authenticate: $ref: '#/components/headers/WWWAuthenticate' '403': description: No write access to an active project; or `X-Organization-Slug` names an organization the key holder is no member of (`ORG_FORBIDDEN`, with the slugs the key holder may send under `data.organizations`) content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '404': description: Project or folder not found (`PROJECT_NOT_FOUND`, `FOLDER_NOT_FOUND`), or the bytes never landed at the presigned URL (`BLOB_NOT_FOUND` — the intent is rolled back, PUT the file and bind again); or `X-Organization-Slug` names no organization (`ORG_SLUG_INVALID`, with the slugs the key holder may send under `data.organizations`) content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '405': description: The path exists, but not for this method (`METHOD_NOT_ALLOWED`); `Allow` names the methods it serves content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' Allow: $ref: '#/components/headers/Allow' '409': description: The upload intent is unknown, expired or already consumed — mint a new handoff (`UPLOAD_INTENT_INVALID`) — or `fileId` is not the `s3Ref` it was minted for (`UPLOAD_FILE_MISMATCH`) content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '413': description: The body exceeds this operation’s byte cap — 1 MiB unless the description says otherwise (`BODY_TOO_LARGE`); the envelope carries a `requestId` content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '429': description: 'The key holder’s request budget is spent (`RATE_LIMITED`): `Retry-After` names the wait in whole seconds and `data.retryAfterMs` in milliseconds; the envelope carries a `requestId`' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' Retry-After: $ref: '#/components/headers/RetryAfter' '500': description: Internal error (`INTERNAL_ERROR`); the envelope carries a `requestId` to quote when reporting it content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' '503': description: 'No object store is configured for this deployment (`OBJECT_STORE_UNCONFIGURED`); or the platform’s database is restarting or cannot be reached (`DATABASE_UNAVAILABLE`): `Retry-After` names the wait in whole seconds — retry with backoff; the envelope carries a `requestId`' content: application/json: schema: $ref: '#/components/schemas/Error' headers: X-Request-Id: $ref: '#/components/headers/XRequestId' X-Tale-Api-Version: $ref: '#/components/headers/XTaleApiVersion' Retry-After: $ref: '#/components/headers/RetryAfter' components: headers: XTaleApiVersion: description: The version of this contract (`info.version`) the serving instance implements — semver for the wire schema: type: string example: 3.27.0 ContentDisposition: description: The stored file name as an RFC 6266 attachment (`filename` and `filename*`) schema: type: string Location: description: The created resource’s own path, relative to the request URL — follow it with GET whatever shape the 201 body has schema: type: string Allow: description: The methods this path serves, comma-separated schema: type: string example: GET, POST, HEAD, OPTIONS AcceptRanges: description: '`bytes` — `Range` requests are honoured' schema: type: string example: bytes RetryAfter: description: How long to wait before retrying, in whole seconds schema: type: integer minimum: 1 WWWAuthenticate: description: 'The challenge RFC 9110 §11.6.1 requires of a 401: `Bearer`, or `Bearer error="invalid_token"` when a key was presented and refused' schema: type: string ContentLength: description: The byte length of the response body schema: type: integer minimum: 0 ContentRange: description: The satisfied range on a 206 (`bytes -/`); on a 416, `bytes */` names the file’s size schema: type: string CacheControl: description: '`private, no-cache` on a validated read and on a download: keep the answer, revalidate it before reuse, and let no shared cache keep it. Every other answer says `no-store`.' schema: type: string ETag: description: 'A validator: over the answer’s bytes on a JSON read, the stored object’s tag on a download. Send it back as `If-None-Match` — as received; the weak and edge-suffixed forms match too — to be told 304 when nothing changed.' schema: type: string XRequestId: description: The request id the platform logged this call under — quote it when reporting a problem. Echoes a sane `X-Request-Id` the caller sent. schema: type: string LastModified: description: When the stored object was last written (HTTP date) schema: type: string schemas: RetryIndexingResult: type: object required: - status description: What a retry-indexing request did — the one answer the Hub document door and the project-file door share. properties: status: type: string enum: - indexing - skipped description: '`indexing` — the blob was queued (an opt-out lifted); `skipped` — nothing was queued, `reason` says why' reason: type: string enum: - content-only - untracked-blob - unsupported - in-progress description: 'Why indexing was skipped; absent when `status` is `indexing`. `content-only`: inline text never enters the search corpus (a Hub document without a file — never a project file); `untracked-blob`: the platform does not track the blob; `unsupported`: terminal — the platform cannot index these bytes and a retry reproduces the answer; the row’s `indexing.errorCode` names the cause (`unsupported_type`, `image_no_vision`, `empty`, `not_text`, `malformed`); `in-progress`: a fresh index job is already queued or running — poll `indexing` instead.' ProjectUploadHandoff: type: object required: - uploadId - url - method - s3Ref - expiresAt - maxBytes properties: maxBytes: type: integer description: The largest file this organization accepts for the declared type, in bytes — the platform ceiling (104,857,600, 100 MiB) or the organization’s lower cap; the bind refuses more (`FILE_TOO_LARGE` / `UPLOAD_POLICY_REJECTED`, `data.limitBytes`) uploadId: type: string description: Single-use intent to present at the bind step url: type: string description: Where to send the bytes method: type: string enum: - PUT s3Ref: type: string description: The blob reference to bind as `fileId` expiresAt: type: integer minimum: 0 description: Epoch ms; the intent AND the signed `url` both die of old age after this (30 minutes from the mint) ProjectFolder: type: object required: - id - name - parentId properties: id: type: string name: type: string parentId: type: - string - 'null' description: The folder this one sits in; null for a root folder ProjectFile: type: object required: - id - createdAt - size properties: id: type: string fileName: type: - string - 'null' folderId: type: - string - 'null' mimeType: type: - string - 'null' description: Resolved from the file name’s extension at the bind — a declared `contentType` never stands in for it — and the `Content-Type` the download carries size: type: - integer - 'null' description: The landed bytes, from the blob’s metadata row — the size the upload policy judged; null when the platform does not track the blob indexing: type: object required: - status additionalProperties: false description: 'Where the file stands in the search corpus — the vocabulary `Document.indexing` speaks. A file bound with the default `skipRagIndexing: true` reads `skipped` until someone indexes it (**Index now** on the project’s Knowledge tab, `POST …/files/{documentId}/retry-indexing`, or a bind with `skipRagIndexing: false`) and is not found by `POST …/knowledge/search` until then; absent when the platform does not track the blob.' properties: status: type: string enum: - pending - queued - running - completed - failed - unsupported - skipped description: '`pending` — never queued; `skipped` — the file opts out of indexing; `unsupported` — TERMINAL: the platform cannot index these bytes and a retry reproduces the answer, `errorCode` says why (`unsupported_type`, `image_no_vision`, `empty`, `not_text`, `malformed`); `failed` — see `error` / `errorCode`: the job retries `embedding_upstream`, `indexer_error` and `index_rebuilding` by itself, `usage_limit` resumes by itself once the usage limit allows it, the rest wait for an admin (a provider account, the organization’s policy) and a `retry-indexing`; a `failed` with no `errorCode` is a failure the platform settled without classifying it, usually an indexing run that stopped before it finished (a lost job, a stopped worker) — request a `retry-indexing` rather than wait for one' indexedAt: type: integer minimum: 0 description: Epoch ms of the last completed indexing error: type: string description: A sentence for a person, present with `failed` and `unsupported`; never a storage-engine diagnostic — the raw cause stays in the platform log. errorCode: type: string enum: - unsupported_type - image_no_vision - empty - not_text - malformed - secret_detected - pii_blocked - embedding_not_configured - embedding_provider_refused - embedding_upstream - index_rebuilding - index_repair_failed - usage_limit - indexer_error description: 'The stable cause to branch on, present with `unsupported`, and with `failed` whenever the platform classified the cause — a `failed` without one was settled unclassified, usually after its indexing run stopped before it finished; request a `retry-indexing` for it. Terminal (`unsupported`): `unsupported_type` — no extractor for the type; `image_no_vision` — an image and no OCR lane; `empty` — no text to index; `not_text` — binary bytes behind a text extension, re-export as UTF-8; `malformed` — the bytes do not parse as the format the extension claims. Retried by the job (`failed`): `embedding_upstream` — the provider was unreachable, rate-limited or 5xx; `indexer_error` — a platform-side store fault; `index_rebuilding` — the search index is being rebuilt. Waits for a usage limit (`failed`): `usage_limit` — a limit that binds whoever the file is indexed for (its uploader, a synced drive’s owner, the organization for an emailed attachment) has too little room for its embeddings; indexing resumes by itself within the hour after the limit resets or is raised, after what it already embedded. Waits for an admin (`failed`): `embedding_not_configured`, `embedding_provider_refused` (the provider refused the account or credential, the model answers vectors of another width than the settings state, or the platform cannot use the embedding credential — none configured, deleted, disabled or unreadable), `index_repair_failed`, `secret_detected`, `pii_blocked`. Saving corrected embedding settings, or adding or repairing the credential the embedding model uses, re-queues every document that failed on the embedding model.' createdAt: type: integer minimum: 0 description: Epoch milliseconds Project: type: object required: - id - name - teamIds - createdAt - updatedAt properties: id: type: string name: type: string key: type: string description: Immutable task-identifier prefix description: type: string externalItemId: type: string description: Caller-owned external key, unique per organization — stored NFC-normalized and trimmed archivedAt: type: integer minimum: 0 description: Epoch ms — present when the project is archived — the archived marker for lookups and the list createdAt: type: integer minimum: 0 description: Epoch ms — the list is ordered on it, newest first updatedAt: type: integer minimum: 0 description: Epoch ms of the last change to the project itself teamIds: type: array items: type: string description: 'The audience: the teams that may see the project, by id; empty = organization-wide (every member). Owners and admins see every project regardless.' ProjectFolderResult: type: object required: - folder - created properties: folder: $ref: '#/components/schemas/ProjectFolder' created: type: boolean description: false when an exact-name sibling already existed (200) Error: type: object required: - error - code properties: error: type: string description: What went wrong code: type: string enum: - ACTOR_AMBIGUOUS - ACTOR_DISABLED - ACTOR_FORBIDDEN - ACTOR_NOT_FOUND - ACTOR_REBOUND - ACTOR_UNVERIFIED - API_KEY_SCOPE_FORBIDDEN - ATTACHMENT_NOT_FOUND - ATTACHMENT_NOT_OWNED - ATTACHMENT_NOT_STAGED - ATTACHMENT_SIZE_MISMATCH - AUTHOR_UNVERIFIED - AUTOMATION_DELIVERY_SCOPE_MISMATCH - AUTOMATION_HAS_ACTIVE_RUNS - AUTOMATION_INPUT_INVALID - AUTOMATION_NOT_DEPLOYED - AUTOMATION_NOT_FOUND - AUTOMATION_NOT_INSTALLED - AUTOMATION_PROJECT_FORBIDDEN - AUTOMATION_PROJECT_SCOPE_REQUIRED - AUTOMATION_TRIGGER_INVALID - AUTOMATION_VERSION_NOT_DEPLOYED - AUTOMATION_VERSION_UNKNOWN - BLOB_NOT_FOUND - BODY_CHUNK_MALFORMED - BODY_LENGTH_MISMATCH - BODY_TOO_LARGE - BROWSER_SESSION_NOT_FOUND - BUDGET_EXCEEDED - CHAT_MODEL_AMBIGUOUS - CHAT_MODEL_NOT_ON_PROVIDER - CHAT_MODEL_UNKNOWN - CHAT_PROVIDER_UNKNOWN - CHAT_THREAD_ARCHIVED - CHAT_THREAD_NOT_DIRECT - CHAT_TURN_IN_PROGRESS - CHAT_TURN_NOT_RUNNING - CONTACT_AMBIGUOUS - CONTACT_DUPLICATE_EMAIL - CONTACT_DUPLICATE_EXTERNAL_ID - CONTACT_IDENTITY_REQUIRED - CONTACT_NOT_FOUND - CONTACT_STALE - CONVERSATION_CLOSED - CONVERSATION_CONTACT_CONFLICT - CONVERSATION_CONTACT_TRASHED - CONVERSATION_NOT_FOUND - CONVERSATION_SNAPSHOT_CONFLICT - CONVERSATION_SOURCE_NOT_FOUND - DATABASE_UNAVAILABLE - DELIVERY_NOT_FOUND - DELIVERY_RECEIPT_CONFLICT - DELIVERY_RETRY_UNAVAILABLE - DELIVERY_UNACKNOWLEDGED - DOCUMENT_HAS_KNOWLEDGE_ENTRY - DOCUMENT_NOT_FOUND - DOCUMENT_RECORD_FROZEN - DOCUMENT_RECORD_PROTECTED - DOCUMENT_RECORD_REPLACEMENT_REQUIRED - DOCUMENT_STALE - DUPLICATE_PRODUCT_EXTERNAL_ID - DUPLICATE_PRODUCT_NAME - EMBEDDING_CREDENTIAL_REJECTED - EMBEDDING_CREDIT_EXHAUSTED - EMBEDDING_NOT_CONFIGURED - EMBEDDING_UPSTREAM_ERROR - EMPTY_ANSWER - FILE_NOT_FOUND - FILE_SIZE_INVALID - FILE_TOO_LARGE - FOLDER_DEPTH_EXCEEDED - FOLDER_NAME_INVALID - FOLDER_NAME_TAKEN - FOLDER_NOT_ACCESSIBLE - FOLDER_NOT_FOUND - FORBIDDEN_DEPLOYMENT_EDITOR - FORBIDDEN_INSTANCE_ADMIN - HTTP_ERROR - HUMAN_ASK_EXPIRED - HUMAN_ASK_NOT_FOUND - HUMAN_ASK_NOT_PENDING - IDEMPOTENCY_KEY_REUSED - INTEGRATION_NOT_OWNED - INTERNAL_ERROR - INVALID_BODY - INVALID_CURSOR - INVALID_HEADER - INVALID_LIMIT - INVALID_QUERY - INVALID_SESSION - INVALID_SKILL - INVALID_SKILL_SLUG - INVALID_URL - KNOWLEDGE_ENTRY_DUPLICATE - KNOWLEDGE_ENTRY_FORBIDDEN - KNOWLEDGE_ENTRY_NOT_FOUND - KNOWLEDGE_ENTRY_STORE_TIMEOUT - KNOWLEDGE_ENTRY_SUPERSEDED - LEGAL_HOLD_ACTIVE - MENTION_DIRECTORY_UNAVAILABLE - MESSAGE_NOT_FOUND - METHOD_NOT_ALLOWED - MODEL_API_CONCURRENCY_EXCEEDED - MODEL_API_DISABLED - MODEL_API_FORBIDDEN - MODEL_API_GUARDRAIL_BLOCKED - MODEL_API_GUARDRAIL_UNAVAILABLE - MODEL_API_GUARDRAIL_UNSUPPORTED - MODEL_API_MODEL_FORBIDDEN - MODEL_API_MODEL_UNKNOWN - MODEL_API_TEXT_TOO_LARGE - MODEL_API_TOOLS_UNSUPPORTED - MODEL_API_UNAVAILABLE - MODEL_API_UPSTREAM_ERROR - MODEL_API_VENDOR_TOOL_UNSUPPORTED - MODEL_API_VISION_UNSUPPORTED - NOT_FOUND - OBJECT_STORE_UNAVAILABLE - OBJECT_STORE_UNCONFIGURED - ORG_FORBIDDEN - ORG_SLUG_INVALID - ORG_SLUG_REQUIRED - ORIGIN_FORBIDDEN - PRECONDITION_FAILED - PRODUCT_NOT_FOUND - PRODUCT_STALE - PROJECT_AGENT_CONNECTOR_UNKNOWN - PROJECT_AGENT_HARNESS_INVALID - PROJECT_AGENT_LIMIT - PROJECT_AGENT_MANAGED - PROJECT_AGENT_MODEL_INVALID - PROJECT_AGENT_NAME_TAKEN - PROJECT_AGENT_NOT_FOUND - PROJECT_AGENT_PROVIDER_UNKNOWN - PROJECT_AGENT_SECRETS_FORBIDDEN - PROJECT_AGENT_SECRET_UNKNOWN - PROJECT_AGENT_SKILL_UNKNOWN - PROJECT_AGENT_STALE - PROJECT_AGENT_TOOL_UNKNOWN - PROJECT_ARCHIVED - PROJECT_DUPLICATE_EXTERNAL_ID - PROJECT_FORBIDDEN - PROJECT_HAS_BOUND_AUTOMATIONS - PROJECT_HAS_PROTECTED_RECORDS - PROJECT_KEY_INVALID - PROJECT_KEY_TAKEN - PROJECT_LEGAL_HOLD - PROJECT_NOT_FOUND - PROJECT_SHARING_INVALID - PURGE_INCOMPLETE - RATE_LIMITED - RBAC_FORBIDDEN - REQUEST_TIMEOUT - REVIEW_COMPETENCE_REQUIRED - REVIEW_INDEPENDENT_REVIEWER_REQUIRED - ROLE_FORBIDDEN - RUN_ACTIVE - RUN_NOT_FOUND - RUN_QUARANTINED - RUN_QUARANTINE_CHANGED - SETUP_FOLDER_MISSING - SKILL_EXISTS - SKILL_FILE_NOT_FOUND - SKILL_FORBIDDEN - SKILL_MALFORMED - SKILL_NOT_FOUND - SKILL_PUBLISH_FORBIDDEN - SKILL_STALE - SKILL_TEAM_UNKNOWN - STANDARD_AGENT_OFF - STANDARD_AGENT_UNAVAILABLE - TASK_AGENT_REVIEW_REQUIRED - TASK_ARCHIVED - TASK_AUTOMATION_DISABLED - TASK_AUTOMATION_UNAVAILABLE - TASK_EXTERNAL_REF_INVALID - TASK_EXTERNAL_STATUS_STALE - TASK_HAS_LIVE_RUN - TASK_HAS_OPEN_SUBTASKS - TASK_LABEL_UNKNOWN - TASK_NOT_FOUND - TASK_NOT_IN_REVIEW - TASK_REVIEW_POLICY_UNAVAILABLE - TASK_STATUS_CONFLICT - TEAM_ACCESS_DENIED - TEAM_INHERITED_FROM_FOLDER - TEAM_NOT_IN_ORG - THREAD_NOT_FOUND - UNAUTHORIZED - UNSUPPORTED_FILE_TYPE - UPLOAD_FILE_MISMATCH - UPLOAD_INTENT_INVALID - UPLOAD_POLICY_REJECTED - UPSTREAM_UNAVAILABLE - URI_TOO_LONG - WEBSITE_DOMAIN_IMMUTABLE - WEBSITE_DOMAIN_INVALID - WEBSITE_DOMAIN_NOT_CRAWLABLE - WEBSITE_DUPLICATE_DOMAIN - WEBSITE_INVALID_LIST_URL - WEBSITE_NOT_FOUND description: 'A stable refusal code — every refusal the door itself makes carries one, and so does every domain refusal it lets through. The set is additive: a new code is a minor change, so treat a value you do not know as a generic refusal of the status you got.' requestId: type: string description: The response’s `X-Request-Id`, repeated on a 429, on a 500, on a body-size 413 and on a URL-size 414 so a caller can quote it data: type: object properties: retryAfterMs: type: number description: Wait in milliseconds for RATE_LIMITED providers: type: array items: type: string description: For CHAT_MODEL_AMBIGUOUS, the provider slugs that serve the model — name one as `providerSlug` lastMessageId: type: string description: 'For CHAT_TURN_NOT_RUNNING, the newest assistant message on the thread (once it has one) — the idle poll’s own disambiguation: equal to the `messageId` a 202 named means that turn already settled' lastStatus: type: string enum: - pending - complete - failed - cancelled description: For CHAT_TURN_NOT_RUNNING, how `lastMessageId` settled (present with it) organizations: type: array description: For ORG_SLUG_REQUIRED, ORG_SLUG_INVALID and ORG_FORBIDDEN, the organizations the key holder belongs to (a disabled membership is left out; the list is empty when none remains) — send one of the slugs as `X-Organization-Slug` items: type: object required: - slug - name properties: slug: type: string name: type: string issues: type: array description: For INVALID_BODY and INVALID_QUERY, every problem the schema found in the body or the query string; for INVALID_LIMIT and INVALID_CURSOR, the one parameter refused (`limit`, `cursor`); for AUTOMATION_INPUT_INVALID, every problem the automation’s inputs schema found in the run input (at most 20 either way), each naming the field and the reason; `error` repeats the first one items: type: object required: - path - message properties: path: type: string description: The field, dotted for nesting (`address.city`, `contacts.2.email`); empty for a problem with the body as a whole message: type: string scope: type: string enum: - user - team - project - org - apiKey description: 'For BUDGET_EXCEEDED, whose cap is reached: the key holder’s own (`user`), one of their teams’ (`team`), that of the project the work belongs to (`project`), the organization’s (`org`) or this API key’s (`apiKey`)' period: type: string enum: - daily - weekly - monthly description: For BUDGET_EXCEEDED, what the cap counts over — a calendar day, ISO week or calendar month in UTC limitCode: type: string enum: - TOKEN_LIMIT - COST_LIMIT - REQUEST_LIMIT description: 'For BUDGET_EXCEEDED, which cap is reached: tokens, cost in cents, or requests' used: type: number description: For BUDGET_EXCEEDED, the usage the cap measured this period, in its unit (tokens, cents or requests) limit: type: number description: For BUDGET_EXCEEDED, the cap, in the same unit resetsAt: type: number description: For BUDGET_EXCEEDED, when the period resets, in epoch milliseconds — `Retry-After` names the same wait in whole seconds securitySchemes: bearerAuth: type: http scheme: bearer description: An organization API key. Create one in Settings → API. Pasted into the explorer on /docs it stays in the page’s memory only and is forgotten on reload — it is never stored.