---
title: "Say the next thing to a drop"
method: POST
path: "/v1/engine/chat"
tags: ["Drops"]
---

# Say the next thing to a drop

`POST /v1/engine/chat`

## Request

- Base URL: `https://aicontentdrop.com`
- URL: `https://aicontentdrop.com/v1/engine/chat`
- Auth: HTTP bearer

Say the next thing to a drop and get its answer with everything it has made. This is the only verb the drop needs: research, concepts, a storyboard, a board, a render or a scheduled post are all asked for in words, and the engine picks the phase itself. Without a `run_id` a new drop is started from the message. Asking costs no credits; a step that would charge stops for POST /v1/engine/runs/{id}/approve and shows up as `pending_approval`. Send `Accept: application/json` for one settled turn; the default is a stream (see the tag description). A turn that outlives the wait comes back 202 as still working and keeps going; send `X-ACD-Wait-Ms` to be answered inside your own client's timeout, then follow the drop with GET /v1/engine/runs/{id}.

## Headers

- `X-ACD-Wait-Ms` integer

## Request body

- object
  - `run_id` string, uuid: The drop to continue. Omit to start a new one from this message.
  - `message` string, required: What you want next, in words.
  - `thinking_mode` 'normal' | 'deep': How much effort to spend planning the turn. Depth only — it changes no capability.
  - `approval_mode` 'confirm_each': Sets the mode when this call starts a new drop; ignored on an existing one. confirm_each is the only mode today, so omit it.
  - `budget_credits` integer: Sets the credit ceiling when this call starts a new drop. A step that would pass it stops, and approving cannot lift it.
  - `attachments` string[]: Public https URLs of images or clips to work from, at most 8.
  - `brief` DropBrief: What is being made, for what, how long, on which recipe. Every field is optional: a brief is filled in as it is settled.
    - `type` 'image' | 'video'
    - `goal` 'viral' | 'sales' | 'social'
    - `duration_s` 5 | 10 | 15 | 20 | 30 | 60: Running time in seconds. A still image has none.
    - `lane` 'cinematic' | 'ugc' | 'marketing'
    - `reference` object: Where the material comes from. `upload` carries its url and `template` its template id; a reference missing either is not one.
      - `kind` 'none' | 'upload' | 'template', required
      - `url` string, uri
      - `template_id` string
    - `model` object: A pinned model. Absent means the recipe's own default, revealed before anything is charged.
  - `mode` 'proposal' | 'produce': Recorded when the drop is created and never read from the wire again.
  - `source` 'url' | 'embed:video-studio' | 'embed:images' | 'embed:marketplace': Which surface opened the drop. Provenance only; nothing branches on it.

## Response `200`

The turn settled. `next_step` says what to do with what came back.

- DropTurn: One settled turn: what the drop said, everything it has made, and the decision it is now waiting on, if any.
  - `run_id` string, uuid, required
  - `phase` 'intake' | 'research' | 'concept' | 'plan' | 'produce' | 'assemble' | 'distribute' | 'done', required
  - `status` 'idle' | 'running' | 'waiting_input' | 'waiting_approval' | 'failed' | 'done' | 'cancelled', required
  - `answer` string, required: What the drop said back. Empty when the turn outlived its window and was answered 202.
  - `artifacts` DropEntities, required: Everything a drop has made, keyed by kind and then by id. An artifact enters once and is referenced afterwards, so ids stay stable for the life of the drop. Instructions sent to a producer, internal model ids and job references are not part of this projection and never appear.
    - `brief` object: The settled brief: what is being made, for what, how long, on which recipe.
    - `videos` object
    - `images` object
    - `ads` object
    - `research` object: Competitor rows read for this drop. Each carries `demo` and `analysed` so a sample row is never shown as an observed one.
    - `concepts` object: Angles, each bound to the research ids it rests on.
    - `storyboards` object
    - `boards` object: The board this drop builds on, with its link.
    - `quotes` object
  - `pending_approval` DropPendingApproval: The decision a drop is waiting on. Pass `task_id` and `quote_id` back exactly as given to POST /v1/engine/runs/{id}/approve. The quote fixes the credit ceiling for that one step and lapses at `expires_at`; once it lapses the engine prices the step again by itself, and `quote_id` is null until the new price lands.
    - `task_id` string, uuid, required
    - `quote_id` string, nullable, required: The signed quote for that exact step. Null while a price that ran out is being refreshed: read the drop again in about a minute for the new one.
    - `credits` number, nullable, required: The credit ceiling this decision fixes; null while the price is being refreshed.
    - `what` string, required: What the step makes, in plain words.
    - `expires_at` string, date-time, nullable, required: When this price lapses.
  - `questions` DropQuestion[]: Present when the drop needs the brief settled before it plans.
    - `id` string, required
    - `question` string, required
    - `options` string[]
    - `allow_free_text` boolean
  - `next_step` string, required: What to do next, in plain words, so a caller never has to infer it from the status.

## Other responses

- `202`: Still running, read the run again. The turn outlived the wait (110 seconds, or the `X-ACD-Wait-Ms` the caller sent), so the route answers with the drop as it stands — `answer` is empty and `status` is `running`. The work was not cancelled: poll GET /v1/engine/runs/{id} until `messages` carries the drop's reply to this message. The run's own `status` is only rewritten when the turn settles, so until then it reads what it read before the message.
- `400`: The body was rejected. `code` names the class of problem and `details` names the fields.
- `401`: Missing, malformed, revoked, or budget-exhausted API key
- `404`: No drop with that id on this account — or the whole surface is absent, which is what an account that is not enabled for drops sees. Drops are available to accounts enabled for them; on those accounts every path below answers `{"error":{"code":"not_found"}}` and nothing else, so an unlisted caller cannot tell an enabled surface from an absent one. On an enabled account, this is a drop id that does not exist or is not yours.
- `409`: The drop is not in a state that allows this. `code` says which: `RUN_BUSY` (it is working on the last message), `RUN_FINISHED`, `REVISION_CONFLICT` (it changed while your request was in flight — read it again and repeat), `TASK_NOT_AWAITING_APPROVAL`, `QUOTE_UNKNOWN` (that step has no priced quote yet), or `QUOTE_EXPIRED` (the price lapsed, so nothing was released).
- `429`: Rate limited. Read Retry-After (header) or error.retry_after_seconds (body) — they carry the same number — and wait that long. Retrying sooner extends the window rather than shortening it.
- `500`: Unexpected failure on our side. Retry once; nothing was charged.
- `503`: The engine could not finish that step. Retry in a few seconds; nothing was charged.

---

[API](https://skmtc.dev/aicontentdrop/apis/ai-content-drop-api.md) · [All operations](https://skmtc.dev/aicontentdrop/apis/ai-content-drop-api/llms.txt) · [OpenAPI document](https://skmtc.dev/aicontentdrop/apis/ai-content-drop-api/revisions/75a13a8043ae?raw)
