{"openapi":"3.1.1","info":{"title":"Amy API","version":"1.0.0","description":"The Amy API for API-key callers. Send your key in the X-Api-Key header on every request."},"security":[{"ApiKey":[]}],"tags":[{"name":"InferencePrivacyEndpoints"},{"name":"RunEndpoints"},{"name":"ClientConversationEndpoints"},{"name":"AmyFileEndpoints"}],"paths":{"/v1/organisation/inference-privacy":{"get":{"tags":["InferencePrivacyEndpoints"],"summary":"The calling API key's organisation's inference privacy verification.","description":"What this organisation's inference privacy actually is right now. `policy` is null when none is set. `capabilities` lists chat, vision and embeddings, each exactly once, bound or defaulted. Vision has no default: unbound, no call can be made on it, so its `provider` and `model` are `none`, its `evidenceState` is `unbound`, and its labels say what the policy promises every call on it (a binding is then judged like any other). `enforced` is true only when a policy exists and every listed capability is admitted under it: under `requireVerifiedZdr` that means a routing-constrainable provider, fresh ZDR evidence and at least one ZDR endpoint for the exact model; under a deny-only policy only that the constraint can be carried. Read it with the policy flags, never alone. Closed tokens: `retention` is `zdr-verified` or `not-enforced`; `trainingUse` is `verified-none` or `not-enforced`; `credentialScope` is `platform`, `org` or `none` (no key resolves; never null). `evidenceState` is `verified`, the condition a verified-ZDR requirement would refuse it under (`provider-unconstrainable`, `no-evidence`, `evidence-stale`, `model-no-zdr-endpoint`), or `unbound`. `evidence.observedAt` is null only before any ZDR list has ever been read. OCR, speech and image generation are gated at call time but are not listed here, so `enforced` says nothing about them. Route attribution is an alarm, never a control: `lastRouteViolationAt` and `routeAttribution` never change `enforced`; every policy `PUT`, an unchanged one included, stamps `policy.updatedAt`, which is how an admin acknowledges a violation.","operationId":"GetOrganisationInferencePrivacy","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InferencePrivacyVerificationDto"}}}},"401":{"description":"Unauthorized","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/agents/{agentId}/runs":{"post":{"tags":["RunEndpoints"],"summary":"Start an Amy run; streams tokens, tool calls/results, usage, and a terminal event over SSE.","description":"Optional `endUserRef` must be a stable PSEUDONYMOUS handle for the caller's end user (their own user id / opaque token) — never PII (no email, name, phone, government id). It is retained in the append-only audit log and webhook delivery records, and it is the handle an erasure of that end user's data is made against. Erasure is an organisation-admin operation: an API key cannot call it. Validated for shape only (≤200 chars, printable ASCII), and erasure takes every ref this rule admits, so a ref that can be stored can always be erased. Omitting it leaves the run UNATTRIBUTED, which disables the run-history tools for it (`search_runs` / `get_run_messages` / `get_run_iterations` refuse rather than read a bucket shared with every other unattributed run in the tenant). Pass a stable ref per end user, or one constant ref if a single shared history is genuinely wanted.\n\nA `conversationId` belongs to the client and `endUserRef` that first use it: starting a run under one that already holds a run of another client, another `endUserRef`, a Console member, or a run started before clients were recorded is a 404 — before any run is created or spend recorded. Mint a fresh id per chat.\n\nRuns are stateless by default: the agent sees only the new `message`. Set `threadConversationHistory: true` to have it replayed the earlier turns of this conversation — the user and assistant text of this client's earlier runs with the same `conversationId` and `endUserRef`, oldest first. Another client's runs, another `endUserRef`'s and a Console member's are never replayed. At most the newest 20 runs and 48,000 characters are replayed; older runs are dropped whole, never cut mid-turn. A retry of such a run, and its resume after an approval, a question or its background tasks, replay the same way. `threadConversationHistory: true` requires both `conversationId` and `endUserRef`, and only a JSON boolean is accepted — anything else is a 400 before any run is created.\n\nOptional `Idempotency-Key` header (a UUID is recommended; any string works; a blank header is no key): send a start again with the same key and no second run starts and nothing is billed twice. Keys belong to the calling API client — any of its keys, rotations included — and the three start routes share them; another client's identical key is unrelated. A repeat of the same request (same route, agent and body; `mcpAuth` is left out, so a freshly minted MCP token is still a repeat) answers `202` with the first run's `runId`, current `status` and `toolWarnings` on `/runs/submit`, and `409` `code: run_already_started` with `runId`, `runStatus` and a `Location` on the stream and `/runs/await` — no stream opens; read the run with GET /v1/runs/{runId}. The same key with another request is `409` `code: idempotency_key_reused`; a first request still starting is `409` `code: idempotency_in_progress` with `Retry-After: 1`. A start refused before its run exists (400, 403, 404, 422, 503) frees its key for the corrected request. With a key, `/runs/await` lets its run finish when the caller hangs up instead of cancelling it. A key lasts 24 hours from first use; nothing is stored but the request's hash and the run id.","operationId":"StartAmyRun","parameters":[{"name":"agentId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StartRunRequest"}}},"required":true},"responses":{"200":{"description":"A Server-Sent Events stream. The event contract is in the Streaming guide of the developer docs.","content":{"text/event-stream":{"schema":{"type":"string"}}}},"400":{"description":"Bad Request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/HttpValidationProblemDetails"}}}},"401":{"description":"Unauthorized","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not Found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"Conflict","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/agents/{agentId}/runs/await":{"post":{"tags":["RunEndpoints"],"summary":"Start an Amy run and return one JSON result when it completes.","description":"Optional `endUserRef` must be a stable PSEUDONYMOUS handle for the caller's end user (their own user id / opaque token) — never PII (no email, name, phone, government id). It is retained in the append-only audit log and webhook delivery records, and it is the handle an erasure of that end user's data is made against. Erasure is an organisation-admin operation: an API key cannot call it. Validated for shape only (≤200 chars, printable ASCII), and erasure takes every ref this rule admits, so a ref that can be stored can always be erased. Omitting it leaves the run UNATTRIBUTED, which disables the run-history tools for it (`search_runs` / `get_run_messages` / `get_run_iterations` refuse rather than read a bucket shared with every other unattributed run in the tenant). Pass a stable ref per end user, or one constant ref if a single shared history is genuinely wanted.\n\nA `conversationId` belongs to the client and `endUserRef` that first use it: starting a run under one that already holds a run of another client, another `endUserRef`, a Console member, or a run started before clients were recorded is a 404 — before any run is created or spend recorded. Mint a fresh id per chat.\n\nRuns are stateless by default: the agent sees only the new `message`. Set `threadConversationHistory: true` to have it replayed the earlier turns of this conversation — the user and assistant text of this client's earlier runs with the same `conversationId` and `endUserRef`, oldest first. Another client's runs, another `endUserRef`'s and a Console member's are never replayed. At most the newest 20 runs and 48,000 characters are replayed; older runs are dropped whole, never cut mid-turn. A retry of such a run, and its resume after an approval, a question or its background tasks, replay the same way. `threadConversationHistory: true` requires both `conversationId` and `endUserRef`, and only a JSON boolean is accepted — anything else is a 400 before any run is created.\n\nOptional `Idempotency-Key` header (a UUID is recommended; any string works; a blank header is no key): send a start again with the same key and no second run starts and nothing is billed twice. Keys belong to the calling API client — any of its keys, rotations included — and the three start routes share them; another client's identical key is unrelated. A repeat of the same request (same route, agent and body; `mcpAuth` is left out, so a freshly minted MCP token is still a repeat) answers `202` with the first run's `runId`, current `status` and `toolWarnings` on `/runs/submit`, and `409` `code: run_already_started` with `runId`, `runStatus` and a `Location` on the stream and `/runs/await` — no stream opens; read the run with GET /v1/runs/{runId}. The same key with another request is `409` `code: idempotency_key_reused`; a first request still starting is `409` `code: idempotency_in_progress` with `Retry-After: 1`. A start refused before its run exists (400, 403, 404, 422, 503) frees its key for the corrected request. With a key, `/runs/await` lets its run finish when the caller hangs up instead of cancelling it. A key lasts 24 hours from first use; nothing is stored but the request's hash and the run id.","operationId":"StartAmyRunAwaited","parameters":[{"name":"agentId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StartRunRequest"}}},"required":true},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AmyRunResult"}}}},"400":{"description":"Bad Request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/HttpValidationProblemDetails"}}}},"401":{"description":"Unauthorized","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not Found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"Conflict","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/agents/{agentId}/runs/submit":{"post":{"tags":["RunEndpoints"],"summary":"Submit an Amy run for asynchronous execution; returns 202 + a run id to poll via GET /v1/runs/{runId}.","description":"Optional `endUserRef` must be a stable PSEUDONYMOUS handle for the caller's end user (their own user id / opaque token) — never PII (no email, name, phone, government id). It is retained in the append-only audit log and webhook delivery records, and it is the handle an erasure of that end user's data is made against. Erasure is an organisation-admin operation: an API key cannot call it. Validated for shape only (≤200 chars, printable ASCII), and erasure takes every ref this rule admits, so a ref that can be stored can always be erased. Omitting it leaves the run UNATTRIBUTED, which disables the run-history tools for it (`search_runs` / `get_run_messages` / `get_run_iterations` refuse rather than read a bucket shared with every other unattributed run in the tenant). Pass a stable ref per end user, or one constant ref if a single shared history is genuinely wanted.\n\nA `conversationId` belongs to the client and `endUserRef` that first use it: starting a run under one that already holds a run of another client, another `endUserRef`, a Console member, or a run started before clients were recorded is a 404 — before any run is created or spend recorded. Mint a fresh id per chat.\n\nRuns are stateless by default: the agent sees only the new `message`. Set `threadConversationHistory: true` to have it replayed the earlier turns of this conversation — the user and assistant text of this client's earlier runs with the same `conversationId` and `endUserRef`, oldest first. Another client's runs, another `endUserRef`'s and a Console member's are never replayed. At most the newest 20 runs and 48,000 characters are replayed; older runs are dropped whole, never cut mid-turn. A retry of such a run, and its resume after an approval, a question or its background tasks, replay the same way. `threadConversationHistory: true` requires both `conversationId` and `endUserRef`, and only a JSON boolean is accepted — anything else is a 400 before any run is created.\n\nOptional `Idempotency-Key` header (a UUID is recommended; any string works; a blank header is no key): send a start again with the same key and no second run starts and nothing is billed twice. Keys belong to the calling API client — any of its keys, rotations included — and the three start routes share them; another client's identical key is unrelated. A repeat of the same request (same route, agent and body; `mcpAuth` is left out, so a freshly minted MCP token is still a repeat) answers `202` with the first run's `runId`, current `status` and `toolWarnings` on `/runs/submit`, and `409` `code: run_already_started` with `runId`, `runStatus` and a `Location` on the stream and `/runs/await` — no stream opens; read the run with GET /v1/runs/{runId}. The same key with another request is `409` `code: idempotency_key_reused`; a first request still starting is `409` `code: idempotency_in_progress` with `Retry-After: 1`. A start refused before its run exists (400, 403, 404, 422, 503) frees its key for the corrected request. With a key, `/runs/await` lets its run finish when the caller hangs up instead of cancelling it. A key lasts 24 hours from first use; nothing is stored but the request's hash and the run id.","operationId":"SubmitAmyRun","parameters":[{"name":"agentId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StartRunRequest"}}},"required":true},"responses":{"202":{"description":"Accepted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmitRunResponse"}}}},"400":{"description":"Bad Request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/HttpValidationProblemDetails"}}}},"401":{"description":"Unauthorized","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not Found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"Conflict","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/runs/{runId}":{"get":{"tags":["RunEndpoints"],"summary":"The full persisted run (status, messages, usage); 404 covers cross-tenant ids and another client's runs.","description":"**Scoped to the calling API-key client:** only a run this client started is reachable — any of its keys, rotations included. Another client's run in the same tenant, a run started before clients were recorded, and another tenant's run are all the same 404 as an unknown id.","operationId":"GetAmyRun","parameters":[{"name":"runId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunDetailDto"}}}},"401":{"description":"Unauthorized","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not Found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/runs/{runId}/approvals/{approvalId}":{"post":{"tags":["RunEndpoints"],"summary":"Resume an approval-paused run with approve/deny; streams the continuation over SSE.","description":"**Scoped to the calling API-key client:** only a run this client started is reachable — any of its keys, rotations included. Another client's run in the same tenant, a run started before clients were recorded, and another tenant's run are all the same 404 as an unknown id. A team run's assignment approval is a `404` here too: a team run is never started by a client, and its approvals are decided in the Console only.","operationId":"ResumeAmyRunApproval","parameters":[{"name":"runId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"approvalId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResumeApprovalRequest"}}},"required":true},"responses":{"200":{"description":"A Server-Sent Events stream. The event contract is in the Streaming guide of the developer docs.","content":{"text/event-stream":{"schema":{"type":"string"}}}},"400":{"description":"Bad Request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/HttpValidationProblemDetails"}}}},"401":{"description":"Unauthorized","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not Found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"Conflict","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/runs/{runId}/retry":{"post":{"tags":["RunEndpoints"],"summary":"Retry a failed run from its committed trace — no tool is re-executed; streams the re-drive over SSE.","description":"Re-drives a run that ended `failed` from the messages it already persisted. **No tool is re-executed** — a committed `tool_result` is replayed, which is the point: the work the tool did is paid for once. A tool call the failed segment left unanswered is replayed as an interrupted result, so the model is told what happened rather than handed a history its provider rejects. The run is REOPENED (`status` returns to `running`, `completedAt` and the failure fields are cleared) rather than copied to a successor, and an `amy.run.retried` webhook says so. Only a `failed` run whose failure code is retryable may be retried, at most `retryCap` times, only while it is the latest turn of its conversation for the client that started it (another client's later run under the same `conversationId` does not count), only while its agent is active, only while every file it was given is still stored (a failed run whose files were discarded cannot be retried), and never as part of a team run, an agent task or a Test Lab case — `GET /v1/runs/{runId}` answers all of it as `retryable`. Every such refusal is a 409 carrying `code: retry_refused` and a `reason` before the stream opens, never an SSE frame. A retry is a new model call, so it also passes the same token-allowance, credential and pricing checks as a new run, answered as a new run's are; `retryable` does not predict those. **Scoped to the calling API-key client:** only a run this client started is reachable — any of its keys, rotations included. Another client's run in the same tenant, a run started before clients were recorded, and another tenant's run are all the same 404 as an unknown id.","operationId":"RetryAmyRun","parameters":[{"name":"runId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/RetryRunRequest"}]}}}},"responses":{"200":{"description":"A Server-Sent Events stream. The event contract is in the Streaming guide of the developer docs.","content":{"text/event-stream":{"schema":{"type":"string"}}}},"400":{"description":"Bad Request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/HttpValidationProblemDetails"}}}},"401":{"description":"Unauthorized","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not Found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"409":{"description":"Conflict","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/runs/{runId}/cancel":{"post":{"tags":["RunEndpoints"],"summary":"Cancel a run this client started; 404 covers cross-tenant ids and another client's runs.","description":"Stops a run by its id. Only the API client whose key started the run may cancel it — the client, not the key, so a rotated key cancels what its predecessor started. Every other caller gets 404. A running run is told to stop and ends itself as cancelled with what it had produced committed, so `status` in the answer is the status BEFORE the stop took effect: poll GET /v1/runs/{runId} for `cancelled`. A run still queued by /runs/submit is cancelled on the spot. A run started by /runs/submit reads `pending` until it ends, also while it is being worked on, so `cancelled: true` with `status: pending` is such a run told to stop. `cancelled: false` means this call stopped nothing — the run had already ended, or is paused on an approval (decide it instead) or on its tasks, or is a task another run dispatched — and is not an error. If `status` is not a final one the run is still going: a cancel sent while Amy is being updated can miss a live run, so ask again. Every call is audit-logged.","operationId":"CancelAmyRun","parameters":[{"name":"runId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelRunResponse"}}}},"401":{"description":"Unauthorized","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not Found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/conversations":{"get":{"tags":["ClientConversationEndpoints"],"summary":"One end user's conversations for the calling API-key client, most recently active first.","description":"Scoped to the calling API-key CLIENT (any of its keys — a rotated key reads what its predecessor wrote) and to the one `endUserRef` named, which is required. Only runs started through the /v1 run routes after this surface shipped carry a client, so earlier runs are not returned. A run another client started under the same `conversationId` is never included.","operationId":"ListAmyClientConversations","parameters":[{"name":"endUserRef","in":"query","required":true,"schema":{"type":"string"}},{"name":"page","in":"query","schema":{"type":"integer","format":"int32","default":1}},{"name":"pageSize","in":"query","schema":{"type":"integer","format":"int32","default":50}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PagedResultOfClientConversationSummaryDto"}}}},"400":{"description":"Bad Request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/HttpValidationProblemDetails"}}}},"401":{"description":"Unauthorized","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/conversations/{conversationId}":{"get":{"tags":["ClientConversationEndpoints"],"summary":"One conversation's user and assistant text turns, in order; 404 unless it holds a run of this client for this end user.","description":"Scoped to the calling API-key CLIENT (any of its keys — a rotated key reads what its predecessor wrote) and to the one `endUserRef` named, which is required. Only runs started through the /v1 run routes after this surface shipped carry a client, so earlier runs are not returned. A run another client started under the same `conversationId` is never included. A user turn is what was sent into the chat: a run's message as the agent received it (a run that carried files ends with an `[Attached files: …]` line naming them), or a note sent with an approval decision. Tool calls, tool results, delegated specialists' rows and the messages Amy hands the agent itself (such as background-task results) are not part of the transcript.","operationId":"GetAmyClientConversation","parameters":[{"name":"conversationId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"endUserRef","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientConversationTranscriptDto"}}}},"400":{"description":"Bad Request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/HttpValidationProblemDetails"}}}},"401":{"description":"Unauthorized","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not Found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/files":{"post":{"tags":["AmyFileEndpoints"],"summary":"Upload a file in one multipart request; returns a file id usable as attachmentFileIds.","description":"Uploads a file in one multipart request and returns its id, ready to pass as `attachmentFileIds` on a run. Unlike the Console flow there is no presign/confirm round trip: the server holds the bytes, so the file is `Stored` the moment this returns.\n\nLimits are the platform storage bounds — 25 MB, and the storage content-type allowlist (PNG, JPEG, WebP, GIF, PDF, plain text, CSV, and audio as MP3, MP4, WAV, WebM, Ogg or FLAC). **Note these are wider than what a run can consume:** `attachmentFileIds` accepts images (PNG/JPEG/WebP/GIF, 20 MB), audio (MP3/MP4/WAV/WebM/Ogg/FLAC, 20 MB) and PDF documents (20 pages, 20 MB) — at most 8 attachments per run, a PDF counting as one however many pages it has. A file outside those bounds uploads successfully here and is refused, with a message naming what was wrong, if it is later attached to a run.\n\nAn attached PDF is read by the agent's `read_document` tool, which rasterizes every page and reads them in one call; it needs an active **Vision** capability binding, and a scanned image-only PDF works as well as one with a text layer.\n\n**Retention:** delete a file you no longer need with `DELETE /v1/files/{id}` — your client can delete any file it uploaded here, attached to a run or not. Otherwise a file is erased along with the run it is attached to when that run's end user is erased by an organisation admin; a file that is never attached to a run is not reachable by end-user erasure, so track its id and delete it.\n\nIf your organisation turned **Keep files sent by API keys** off (Console → API keys) before the upload, Amy deletes the file itself as soon as every run it is attached to has ended — completed or failed — or an hour after upload if no run uses it. The run still lists the attachment, but its content answers `404`, a later turn of the same conversation cannot read it again, and a failed run whose files were discarded cannot be retried (its `retryable` is `false`). The setting at upload time decides; changing it later does not affect files already uploaded.","operationId":"UploadAmyFile","responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadApiFileResponse"}}}},"400":{"description":"Bad Request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"401":{"description":"Unauthorized","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"403":{"description":"Forbidden","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"415":{"description":"Unsupported Media Type","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"502":{"description":"Bad Gateway","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Service Unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/files/{id}":{"delete":{"tags":["AmyFileEndpoints"],"summary":"Delete a file your API client uploaded; 404 covers every file that is not.","description":"Deletes a file **your API client** uploaded through `POST /v1/files` — its bytes, then its record — and releases the storage it counted against the tenant's quota. Any key of the same client may delete it, so a rotated key can clean up after its predecessor.\n\nEvery other id is a 404: an unknown id, another client's upload, another tenant's file, a Console upload, a file a run generated, and a file uploaded before this endpoint existed (those have no client recorded; end-user erasure removes them).\n\n**An attached file may be deleted.** Every run that links it keeps the attachment's metadata in `GET /v1/runs/{runId}`, but its content route answers 404 — and a run still in flight that reads the file no longer finds it. A failed run whose files were deleted cannot be retried (its `retryable` is `false`). Delete once you are done with the run.\n\nA 502 means storage refused the delete: nothing was removed, and retrying is safe.","operationId":"DeleteAmyFile","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"No Content"},"401":{"description":"Unauthorized","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not Found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"502":{"description":"Bad Gateway","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"503":{"description":"Service Unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/runs/{runId}/files/{fileId}/content":{"get":{"tags":["AmyFileEndpoints"],"summary":"Stream the bytes of a file attached to this run; 404 covers cross-tenant, other-client and unlinked ids.","description":"Streams the bytes of one file attached to this run — an image the run generated, or one that was attached to it. List them first with `GET /v1/runs/{runId}`, which returns each attachment's `fileId`.\n\nThe file must be attached to *this* run: a file id that belongs to the tenant but not to this run returns 404, as do an unknown run, an unknown file, and another tenant's run.\n\n**Scoped to the calling API-key client:** only a run this client started is reachable — any of its keys, rotations included. Another client's run in the same tenant, a run started before clients were recorded, and another tenant's run are all the same 404 as an unknown id.","operationId":"GetAmyRunFileContent","parameters":[{"name":"runId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"fileId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK"},"401":{"description":"Unauthorized","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not Found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"502":{"description":"Bad Gateway","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/v1/runs/{runId}/workspace-files/{interactionId}/content":{"get":{"tags":["AmyFileEndpoints"],"summary":"Stream a file this run wrote with its file tools; 404 covers cross-tenant, other-client, unknown and non-produced ids.","description":"Streams one file this run **wrote** with its file tools — not an uploaded or generated attachment (that is `GET /v1/runs/{runId}/files/{fileId}/content`).\n\nThe id is a **file-interaction row id**, listed by the run's own file interactions. Only rows the run produced are servable: `write`/`append`, succeeded, and a file rather than a directory. A path the model merely mentioned has no row and cannot be requested.\n\nServed as an attachment with the app's user-content hardening, so a `.html` artifact downloads rather than rendering. Files over 100 MB are refused with 413.\n\n**Not used on hosted Amy:** a run has no such files there, so every id here is a 404.\n\n**Scoped to the calling API-key client:** only a run this client started is reachable — any of its keys, rotations included. Another client's run in the same tenant, a run started before clients were recorded, and another tenant's run are all the same 404 as an unknown id.","operationId":"GetAmyRunWorkspaceFileContent","parameters":[{"name":"runId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"interactionId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"OK"},"401":{"description":"Unauthorized","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"404":{"description":"Not Found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"413":{"description":"Payload Too Large","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"502":{"description":"Bad Gateway","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}}},"components":{"schemas":{"AmyRunResult":{"required":["runId","status","message","usage"],"type":"object","properties":{"runId":{"type":"string","format":"uuid"},"status":{"type":"string"},"message":{"type":["null","string"]},"usage":{"$ref":"#/components/schemas/AmyUsage"},"errorCode":{"type":["null","string"]},"failureReason":{"type":["null","string"]},"traceId":{"type":["null","string"]},"toolWarnings":{"type":"array","items":{"$ref":"#/components/schemas/ToolWarning"}}}},"AmyUsage":{"required":["inputTokens","outputTokens","totalTokens"],"type":"object","properties":{"inputTokens":{"type":"integer","format":"int64"},"outputTokens":{"type":"integer","format":"int64"},"totalTokens":{"type":"integer","format":"int64"},"reportedCost":{"type":["null","number"],"format":"double"},"reportedCostCurrency":{"type":["null","string"]},"cachedInputTokens":{"type":["null","integer"],"format":"int64"},"cacheWriteTokens":{"type":["null","integer"],"format":"int64"},"reasoningTokens":{"type":["null","integer"],"format":"int64"},"uncachedInputTokens":{"type":"integer","format":"int64"}}},"CancelRunResponse":{"required":["cancelled","status"],"type":"object","properties":{"cancelled":{"type":"boolean"},"status":{"type":"string"}}},"ClientConversationSummaryDto":{"required":["conversationId","agentSlug","title","status","startedAt","lastActivityAt","runCount"],"type":"object","properties":{"conversationId":{"type":"string","format":"uuid"},"agentSlug":{"type":"string"},"title":{"type":["null","string"]},"status":{"type":"string"},"startedAt":{"type":"string","format":"date-time"},"lastActivityAt":{"type":"string","format":"date-time"},"runCount":{"type":"integer","format":"int32"}}},"ClientConversationTranscriptDto":{"required":["conversationId","agentSlug","title","status","latestRunId","startedAt","lastActivityAt","runCount","turns"],"type":"object","properties":{"conversationId":{"type":"string","format":"uuid"},"agentSlug":{"type":"string"},"title":{"type":["null","string"]},"status":{"type":"string"},"latestRunId":{"type":"string","format":"uuid"},"startedAt":{"type":"string","format":"date-time"},"lastActivityAt":{"type":"string","format":"date-time"},"runCount":{"type":"integer","format":"int32"},"turns":{"type":"array","items":{"$ref":"#/components/schemas/ClientConversationTurnDto"}}}},"ClientConversationTurnDto":{"required":["runId","role","text","createdAt"],"type":"object","properties":{"runId":{"type":"string","format":"uuid"},"role":{"type":"string"},"text":{"type":"string"},"createdAt":{"type":"string","format":"date-time"}}},"HttpValidationProblemDetails":{"type":"object","properties":{"type":{"type":["null","string"]},"title":{"type":["null","string"]},"status":{"type":["null","integer"],"format":"int32"},"detail":{"type":["null","string"]},"instance":{"type":["null","string"]},"errors":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}}},"InferencePrivacyCapabilityDto":{"required":["capability","provider","model","credentialScope","retention","trainingUse","evidenceObservedAt","evidenceState"],"type":"object","properties":{"capability":{"type":"string"},"provider":{"type":"string"},"model":{"type":"string"},"credentialScope":{"type":"string"},"retention":{"type":"string"},"trainingUse":{"type":"string"},"evidenceObservedAt":{"type":["null","string"],"format":"date-time"},"evidenceState":{"type":"string"}}},"InferencePrivacyEvidenceDto":{"required":["source","observedAt","stale"],"type":"object","properties":{"source":{"type":"string"},"observedAt":{"type":["null","string"],"format":"date-time"},"stale":{"type":"boolean"}}},"InferencePrivacyPolicyDto":{"required":["requireVerifiedZdr","denyDataCollection","updatedAt"],"type":"object","properties":{"requireVerifiedZdr":{"type":"boolean"},"denyDataCollection":{"type":"boolean"},"updatedAt":{"type":"string","format":"date-time"}}},"InferencePrivacyVerificationDto":{"required":["policy","enforced","evidence","capabilities","lastRouteViolationAt","routeAttribution","generatedAt"],"type":"object","properties":{"policy":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/InferencePrivacyPolicyDto"}]},"enforced":{"type":"boolean"},"evidence":{"$ref":"#/components/schemas/InferencePrivacyEvidenceDto"},"capabilities":{"type":"array","items":{"$ref":"#/components/schemas/InferencePrivacyCapabilityDto"}},"lastRouteViolationAt":{"type":["null","string"],"format":"date-time"},"routeAttribution":{"$ref":"#/components/schemas/InferenceRouteAttributionDto"},"generatedAt":{"type":"string","format":"date-time"}}},"InferenceRouteAttributionDto":{"required":["lastObservedAt","lastUnattributedAt"],"type":"object","properties":{"lastObservedAt":{"type":["null","string"],"format":"date-time"},"lastUnattributedAt":{"type":["null","string"],"format":"date-time"}}},"McpServerCredential":{"required":["serverSlug","token"],"type":"object","properties":{"serverSlug":{"type":"string"},"token":{"type":"string"}}},"McpServerDeclaration":{"required":["slug","url","authHeaderName","tools"],"type":"object","properties":{"slug":{"type":"string"},"url":{"type":"string"},"authHeaderName":{"type":["null","string"]},"tools":{"type":"array","items":{"$ref":"#/components/schemas/McpToolBinding"}}}},"McpToolBinding":{"required":["handle","remoteName"],"type":"object","properties":{"handle":{"type":"string"},"remoteName":{"type":"string"}}},"PagedResultOfClientConversationSummaryDto":{"required":["items","page","pageSize","total"],"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/ClientConversationSummaryDto"}},"page":{"type":"integer","format":"int32"},"pageSize":{"type":"integer","format":"int32"},"total":{"type":"integer","format":"int32"}}},"PendingApprovalDto":{"required":["approvalId","toolName","argsJson","requestedAt","expiresAt","approvableScopes"],"type":"object","properties":{"approvalId":{"type":"string","format":"uuid"},"toolName":{"type":"string"},"argsJson":{"type":["null","string"]},"requestedAt":{"type":"string","format":"date-time"},"expiresAt":{"type":["null","string"],"format":"date-time"},"approvableScopes":{"type":"array","items":{"type":"string"}},"requestingAgentSlug":{"type":["null","string"]},"delegatedGatedTools":{"type":["null","array"],"items":{"type":"string"}}}},"PendingQuestionDto":{"required":["questionId","askingAgentSlug","question","options","recommendedIndex","allowMultiple","allowFreeText","askedAt"],"type":"object","properties":{"questionId":{"type":"string","format":"uuid"},"askingAgentSlug":{"type":"string"},"question":{"type":"string"},"options":{"type":"array","items":{"$ref":"#/components/schemas/QuestionOptionDto"}},"recommendedIndex":{"type":["null","integer"],"format":"int32"},"allowMultiple":{"type":"boolean"},"allowFreeText":{"type":"boolean"},"askedAt":{"type":"string","format":"date-time"},"answer":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/QuestionAnswerDto"}]}}},"ProblemDetails":{"type":"object","properties":{"type":{"type":["null","string"]},"title":{"type":["null","string"]},"status":{"type":["null","integer"],"format":"int32"},"detail":{"type":["null","string"]},"instance":{"type":["null","string"]}}},"QuestionAnswerDto":{"required":["chosenIndices","freeText","declined","answeredAt"],"type":"object","properties":{"chosenIndices":{"type":"array","items":{"type":"integer","format":"int32"}},"freeText":{"type":["null","string"]},"declined":{"type":"boolean"},"answeredAt":{"type":"string","format":"date-time"}}},"QuestionOptionDto":{"required":["label","reason"],"type":"object","properties":{"label":{"type":"string"},"reason":{"type":"string"}}},"ResumeApprovalRequest":{"required":["decision"],"type":"object","properties":{"decision":{"type":"string"},"note":{"type":["null","string"]},"mcpAuth":{"type":["null","array"],"items":{"$ref":"#/components/schemas/McpServerCredential"}},"mcpServers":{"type":["null","array"],"items":{"$ref":"#/components/schemas/McpServerDeclaration"}}}},"RetryRunRequest":{"type":"object","properties":{"mcpAuth":{"type":["null","array"],"items":{"$ref":"#/components/schemas/McpServerCredential"}},"mcpServers":{"type":["null","array"],"items":{"$ref":"#/components/schemas/McpServerDeclaration"}}}},"RunAgentUsageDto":{"required":["agentSlug","inputTokens","outputTokens","totalTokens"],"type":"object","properties":{"agentSlug":{"type":"string"},"inputTokens":{"type":"integer","format":"int64"},"outputTokens":{"type":"integer","format":"int64"},"totalTokens":{"type":"integer","format":"int64"}}},"RunAttachmentDto":{"required":["runId","id","fileId","kind","filename","contentType","sizeBytes"],"type":"object","properties":{"runId":{"type":"string","format":"uuid"},"id":{"type":"string","format":"uuid"},"fileId":{"type":"string","format":"uuid"},"kind":{"type":"string"},"filename":{"type":"string"},"contentType":{"type":"string"},"sizeBytes":{"type":"integer","format":"int64"},"sourceUrl":{"type":["null","string"]}}},"RunConversationContextDto":{"required":["conversationId","title","position","runCount","previousRunId","nextRunId"],"type":"object","properties":{"conversationId":{"type":"string","format":"uuid"},"title":{"type":["null","string"]},"position":{"type":"integer","format":"int32"},"runCount":{"type":"integer","format":"int32"},"previousRunId":{"type":["null","string"],"format":"uuid"},"nextRunId":{"type":["null","string"],"format":"uuid"}}},"RunDetailDto":{"required":["id","status","agentSlug","endUserRef","conversationId","createdAt","startedAt","completedAt","usage","credentialScopeUsed","messages","iterations","pendingApprovals"],"type":"object","properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string"},"agentSlug":{"type":"string"},"endUserRef":{"type":["null","string"]},"conversationId":{"type":["null","string"],"format":"uuid"},"createdAt":{"type":"string","format":"date-time"},"startedAt":{"type":["null","string"],"format":"date-time"},"completedAt":{"type":["null","string"],"format":"date-time"},"usage":{"$ref":"#/components/schemas/AmyUsage"},"credentialScopeUsed":{"type":["null","string"]},"messages":{"type":"array","items":{"$ref":"#/components/schemas/RunMessageDto"}},"iterations":{"type":"array","items":{"$ref":"#/components/schemas/RunIterationDto"}},"pendingApprovals":{"type":"array","items":{"$ref":"#/components/schemas/PendingApprovalDto"}},"conversation":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/RunConversationContextDto"}]},"agentUsage":{"type":["null","array"],"items":{"$ref":"#/components/schemas/RunAgentUsageDto"}},"attachments":{"type":["null","array"],"items":{"$ref":"#/components/schemas/RunAttachmentDto"}},"errorCode":{"type":["null","string"]},"failureReason":{"type":["null","string"]},"workspace":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/RunWorkspaceDto"}]},"tasks":{"type":["null","array"],"items":{"$ref":"#/components/schemas/RunTaskDto"}},"taskWait":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/RunTaskWaitDto"}]},"taskWakesUsed":{"type":"integer","format":"int32","default":0},"cappedToolFamilies":{"type":["null","array"],"items":{"type":"string"}},"pendingQuestion":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/PendingQuestionDto"}]},"retriesUsed":{"type":"integer","format":"int32","default":0},"retryCap":{"type":"integer","format":"int32","default":0},"retryable":{"type":"boolean","default":false},"toolWarnings":{"type":"array","items":{"$ref":"#/components/schemas/ToolWarning"}}}},"RunIterationDto":{"required":["id","sequence","modelId","finishReason","inputTokens","outputTokens","durationMs"],"type":"object","properties":{"id":{"type":"string","format":"uuid"},"sequence":{"type":"integer","format":"int32"},"modelId":{"type":"string"},"finishReason":{"type":["null","string"]},"inputTokens":{"type":"integer","format":"int64"},"outputTokens":{"type":"integer","format":"int64"},"durationMs":{"type":"integer","format":"int32"},"agentSlug":{"type":["null","string"]},"parentToolCallId":{"type":["null","string"]}}},"RunMessageDto":{"required":["sequence","iterationId","role","kind","text","toolName","toolCallId","argumentsJson","resultJson","isError","createdAt"],"type":"object","properties":{"sequence":{"type":"integer","format":"int32"},"iterationId":{"type":["null","string"],"format":"uuid"},"role":{"type":"string"},"kind":{"type":"string"},"text":{"type":["null","string"]},"toolName":{"type":["null","string"]},"toolCallId":{"type":["null","string"]},"argumentsJson":{"type":["null","string"]},"resultJson":{"type":["null","string"]},"isError":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"},"agentSlug":{"type":["null","string"]},"parentToolCallId":{"type":["null","string"]}}},"RunTaskDto":{"required":["id","label","agentSlug","status","runId","deadline","resultDigest","errorCode","failureReason","createdAt","startedAt","completedAt"],"type":"object","properties":{"id":{"type":"string","format":"uuid"},"label":{"type":"string"},"agentSlug":{"type":"string"},"status":{"type":"string"},"runId":{"type":["null","string"],"format":"uuid"},"deadline":{"type":"string","format":"date-time"},"resultDigest":{"type":["null","string"]},"errorCode":{"type":["null","string"]},"failureReason":{"type":["null","string"]},"createdAt":{"type":"string","format":"date-time"},"startedAt":{"type":["null","string"],"format":"date-time"},"completedAt":{"type":["null","string"],"format":"date-time"}}},"RunTaskWaitDto":{"required":["mode","startedAt","deadline"],"type":"object","properties":{"mode":{"type":"string"},"startedAt":{"type":"string","format":"date-time"},"deadline":{"type":"string","format":"date-time"}}},"RunWorkspaceDto":{"required":["id","slug","name"],"type":"object","properties":{"id":{"type":"string","format":"uuid"},"slug":{"type":"string"},"name":{"type":"string"}}},"StartRunRequest":{"required":["message"],"type":"object","properties":{"message":{"type":"string"},"endUserRef":{"type":["null","string"]},"conversationId":{"type":["null","string"],"format":"uuid"},"model":{"type":["null","string"]},"tools":{"type":["null","array"],"items":{"type":"string"}},"mcpAuth":{"type":["null","array"],"items":{"$ref":"#/components/schemas/McpServerCredential"}},"mcpServers":{"type":["null","array"],"items":{"$ref":"#/components/schemas/McpServerDeclaration"}},"userTimeZone":{"type":["null","string"]},"attachmentFileIds":{"type":["null","array"],"items":{"type":"string","format":"uuid"}},"workspaceId":{"type":["null","string"],"format":"uuid"},"threadConversationHistory":{"type":"boolean","default":false}}},"SubmitRunResponse":{"required":["runId","status"],"type":"object","properties":{"runId":{"type":"string","format":"uuid"},"status":{"type":"string"},"toolWarnings":{"type":"array","items":{"$ref":"#/components/schemas/ToolWarning"}}}},"ToolWarning":{"required":["code","tools","message"],"type":"object","properties":{"code":{"type":"string"},"tools":{"type":"array","items":{"type":"string"}},"message":{"type":"string"},"server":{"type":["null","string"]}}},"UploadApiFileResponse":{"required":["id","filename","contentType","sizeBytes"],"type":"object","properties":{"id":{"type":"string","format":"uuid"},"filename":{"type":"string"},"contentType":{"type":"string"},"sizeBytes":{"type":"integer","format":"int64"}}}},"securitySchemes":{"ApiKey":{"type":"apiKey","in":"header","name":"X-Api-Key","description":"Your Amy API key, in the form amy_live_<keyId>.<secret>."}}},"servers":[{"url":"https://api.askamy.co.za"}]}