---
title: "One conversational turn. The chat surface uses this."
method: POST
path: "/v1/chat"
tags: ["chat"]
---

# One conversational turn. The chat surface uses this.

`POST /v1/chat`

## Request

- Base URL: `https://api.hookdetector.com` (the document declares no server; this is the origin it was published from)
- URL: `https://api.hookdetector.com/v1/chat`
- Auth: none declared

One turn: an answer about hooks or the tool (action "answer", no credits),
one clarifying question (action "clarify"), or a started run
(action "research", with the run in `run`, reserving credits like POST /v1/research).
A deck size typed into the message ("..., 15 hooks") and a recency phrase ("this
month", "last week") set the run's count and posted_within_days; the body's
posted_within_days (the recency chip) wins over the phrase and is kept on the
conversation for later turns (an explicit null clears it); seed_hook_ids applies to
this turn's research ("find more like these"); count sets how many hooks a run this
turn starts delivers (a size typed into the message wins). Asked to make one
of the conversation's hooks yours for a niche ("make hook 2 mine for dentists"), the
turn writes lines from that hook's structure (1 credit, like POST
/v1/hooks/{hook_id}/remix) and answers with them labelled, the result in `remix`.
Every response includes suggestions (0 to 4 follow-ups, [] for research).
Each turn is a model call, so turns are limited per account per hour. 503
model_unavailable when the model is down; nothing is charged then. Needs write, and
research for a turn that starts a run (403 insufficient_scope before anything is
reserved). MCP: chat.

## Headers

- `Idempotency-Key` string, nullable: retry safely: the same key and body within 24 hours answer the first turn again (its conversation, its answer, its run or its remix): no model call, no new message, nothing charged. The same key with another body is 422 idempotency_key_reused
- `authorization` string, nullable: Bearer hd_... from POST /v1/accounts or POST /v1/keys

## Request body

- ChatIn
  - `message` string, required
  - `conversation_id` string, nullable
  - `language` string, nullable: optional, up to 60 characters: only clips spoken or written in this language or dialect, in words or as a tag: "Moroccan Darija", "ar-MA", "Egyptian Arabic", "Brazilian Portuguese". Any language works, including a regional variety ("Quebec French", "Flemish", "Catalan") and a mixed, code-switched one ("Hinglish", "Taglish", "Moroccan Darija with French"), or two joined by "or" ("Tagalog or Taglish"). The value is handed to the judges exactly as written; how well they honour a mixed or either-of value is not measured yet. Saying it inside topic works too; this field wins when both are given. For chat, omitted or null keeps the conversation's current setting; a value sets it for this and later turns; the empty string clears it back to any language / any country.
  - `country` string, nullable: optional: only clips from creators in this country, as an assigned ISO 3166-1 alpha-2 code: MA, EG, BR, US (UK is read as GB). For a region that is not a country, send its country's code and name the regional variety in language: Quebec is country CA with language "Quebec French", Flanders BE with "Flemish", Catalonia ES with "Catalan". The country places the TikTok search there; the language judge does the regional filtering, and Instagram reports no country. A country alone does not restrict the language. For chat, omitted or null keeps the conversation's current setting; a value sets it for this and later turns; the empty string clears it back to any language / any country.
  - `posted_within_days` 7 | 30 | 90 | 365 | 0 | null, nullable: since 2026-10-01: optional, how recent the clips must be, as days: 7, 30, 90, 365. The search asks the platforms for that window and the floor for a clip under 7 days old uses views per day (10,000 a day passes); null or left out means any time. A clip's `posted_at` and `age_days` say what the run found. For chat (the recency chip): omitted keeps the conversation's current setting; 7, 30, 90 or 365 sets it for this and later turns and wins over a recency phrase in the message; an explicit null on REST, or 0 on either door, clears it back to any time.
  - `seed_hook_ids` string[], nullable: since 2026-10-01: optional, up to 10 hook ids from your runs (a kept hook from GET /v1/keeps, or any hook you can read): "find more like these". The engine reads their lines, topics, creators and languages to steer the search phrases and the creators it expands; a seed is never delivered again. An id that is not one of your hooks is 422.
  - `count` integer, nullable: how many hooks a run this turn starts should deliver, 1 to 30; a size typed into the message ("15 hooks") wins over it, and it wins over the turn's own guess; null or omitted lets the turn decide. Fewer hooks is a faster, cheaper run
  - `platforms` string[], nullable: since 2026-10-10: optional, which platforms to search and deliver from, a non-empty list of "tiktok", "instagram" and "youtube" (YouTube Shorts); left out (or all three) searches all three. ["instagram"] delivers Instagram reels only. Names are read without regard to case and a repeat counts once; an empty list or any other name is 422 naming platforms. The run echoes it as `platforms`.

## Response `200`

Successful Response

- ChatOut
  - `conversation_id` string, required
  - `action` 'clarify' | 'research' | 'answer', required
  - `message` string, required
  - `run` RunStartedOut, required
    - `run_id` string, required
    - `status` string, required
    - `kind` string: what the run researches: "short" (hooks from TikTok, Instagram and YouTube Shorts, POST /v1/research), "long" (how long-form YouTube outliers open, POST /v1/longform) or "packaging" (their titles and thumbnails, POST /v1/packaging). Every run from before 2026-10-07 is short
    - `topic` string, required
    - `requested` integer, required
    - `language` string, nullable: the language or dialect asked for, as sent, or null for any language
    - `country` string, nullable: the country asked for, as an ISO 3166-1 alpha-2 code (upper-cased, UK read as GB), or null for any country
    - `posted_within_days` integer, nullable: the recency window asked for (7, 30, 90 or 365 days), or null
    - `seed_hook_ids` string[], nullable: the seed hooks asked for ("find more like these"), or null
    - `platforms` string[], nullable: the platforms this Short form run searches and delivers from ("tiktok", "instagram", "youtube"): the ones asked for, or all three; null on a long or packaging run
    - `credits_reserved` integer, required
    - `created_at` string, required
  - `suggestions` string[], required
  - `remix` RemixOut
    - `hook_id` string, required
    - `niche` string, required
    - `n` integer, required
    - `written` WrittenLine[], required: at most n lines; a line that copies the real hook is dropped
      - `line` string, required: the written opening line, one sentence
      - `kept_structure` string, nullable, required: what was kept from the real hook, in a few words
    - `label` string, required: always "Written by Hook Detector from a real hook's structure": these are written lines, never found hooks; show the label with them
    - `credits_charged` integer, required: always 1
    - `credits` integer, required: your balance after this call
    - `remix_id` string, nullable: this remix's id: GET /v1/remixes/{remix_id} reads it back, free
    - `created_at` string, nullable

## Other responses

- `401`: missing, invalid, revoked or expired API key (code key_expired)
- `402`: not enough credits
- `403`: the key lacks the scope this call needs (insufficient_scope), or its credit_limit is spent (key_credit_limit_reached)
- `404`: not found on this account
- `413`: request body over the size cap
- `422`: invalid input
- `429`: rate limited; see Retry-After
- `500`: our fault; names the request id
- `503`: the language model is unavailable

---

[API](https://skmtc.dev/hookdetector/apis/hook-detector.md) · [All operations](https://skmtc.dev/hookdetector/apis/hook-detector/llms.txt) · [OpenAPI document](https://skmtc.dev/hookdetector/apis/hook-detector/revisions/b136a2804d2b?raw)
