{
"error": {
"type": "invalid_request",
"message": "limit must be between 1 and 100.",
"hint": "Pass limit between 1 and 100 (default 20), and page with after or before.",
"doc_url": "https://api.flow.engineer/docs/errors/invalid_request",
"param": "limit"
}
}
{
"error": {
"type": "authentication",
"message": "No valid API key was given.",
"hint": "Send the header Authorization: Bearer fk_test_... (or fk_live_...); no key yet? Get a test key with curl -X POST https://api.flow.engineer/v0/sandbox/keys",
"doc_url": "https://api.flow.engineer/docs/errors/authentication"
}
}
{
"error": {
"type": "permission",
"message": "This account handles health information under a business associate agreement, and Telegram is not a channel Flow holds such an agreement for, so it cannot be simulated here either.",
"hint": "Simulate an SMS conversation instead: POST /v0/test/conversations with {\"channel\":\"sms\"}.",
"doc_url": "https://api.flow.engineer/docs/errors/permission",
"param": "conversation_id",
"channel_code": "hipaa_channel_not_covered"
}
}
{
"error": {
"type": "idempotency_conflict",
"message": "This Idempotency-Key was already used for a different request (another method, path or body).",
"hint": "Use a new Idempotency-Key for this request; reuse a key only to repeat the identical request.",
"doc_url": "https://api.flow.engineer/docs/errors/idempotency_conflict",
"param": "Idempotency-Key",
"channel_code": "body_mismatch"
}
}
{
"error": {
"type": "invalid_request",
"message": "The request body is larger than 33554432 bytes.",
"hint": "Send a smaller body; upload media with POST /v0/files and send its file_id instead of inlining it.",
"doc_url": "https://api.flow.engineer/docs/errors/invalid_request"
}
}
{
"error": {
"type": "unsupported_content",
"channel": "telegram",
"param": "kind",
"message": "Carousels work on WhatsApp only.",
"hint": "On Telegram, send a basic message, then one buttons message with an image header per card.",
"doc_url": "https://api.flow.engineer/docs/errors/unsupported_content",
"conversation": "conv_01JB8ZC3K5M7P9R1T3V5X7Z9B1"
}
}
{
"error": {
"type": "new_contact_limit",
"message": "This sender has started its 15 new conversations for today.",
"hint": "Retry after 3600 seconds; replies into existing conversations still go.",
"doc_url": "https://api.flow.engineer/docs/errors/new_contact_limit",
"retry_after": 3600,
"sender": "snd_01JB8Z4Q3V6W0R2N7C5H1M9K4T"
}
}
Test keys only. Posts one inbound turn from the simulated contact of a
simulated conversation. It goes through the same inbound path as a real
channel’s message: Flow stores it, then delivers it as a message.received
event by webhook, on the stream and in the event log, in order with the
conversation’s other events. A webhook answer’s reply works as usual. The
answer comes before delivery; the message.received event carries the
message ID given here.
content is what the contact sends, as a kind object with kind: basic
(text and/or media), voice, location, contact_card or
button_reply (a tap on a reply button of a buttons or carousel message
your agent sent, or on a row of a list, given as its message_id and
button_id: Flow fills in the label). Media and voice take the
file_id of a file uploaded with POST /v0/files (no url); the contact’s
message refers to that same file (its file_id, url, mime_type and
size_bytes): Flow does not copy it again. The channel’s rules apply:
what it cannot carry from a person (a document over SMS, a location over
SMS, a file larger than the channel fetches from a person) answers
422 unsupported_content, and text over the channel’s limit answers
400 invalid_request.
On SMS, the keywords work as from a real phone, matched against the whole
text. STOP (or STOPALL, UNSUBSCRIBE, CANCEL, END, QUIT, REVOKE, OPT OUT,
OPTOUT) opts the simulated contact out against the simulated sender: the
contact’s opted_out_at is set and opted_out_via is keyword, and sends
to them answer 403 permission with channel_codecontact_opted_out.
START (or UNSTOP, YES) lifts it. The turn is still delivered as
message.received. A keyword reply is never recorded as a message, so the
transcript shows no automatic reply to STOP, START or HELP.
On WhatsApp, each turn opens the conversation’s 24-hour window, as a real
contact’s message does.
A conversation that is not simulated answers 403 permission with
channel_codenot_simulated; a live key answers 403 permission with
channel_codetest_mode_only. In a HIPAA account (Account.hipaa), a
turn on a simulated telegram, whatsapp or imessage conversation (one made before
the flag was set) answers 403 permission with channel_codehipaa_channel_not_covered (paramconversation_id).
{
"error": {
"type": "invalid_request",
"message": "limit must be between 1 and 100.",
"hint": "Pass limit between 1 and 100 (default 20), and page with after or before.",
"doc_url": "https://api.flow.engineer/docs/errors/invalid_request",
"param": "limit"
}
}
{
"error": {
"type": "authentication",
"message": "No valid API key was given.",
"hint": "Send the header Authorization: Bearer fk_test_... (or fk_live_...); no key yet? Get a test key with curl -X POST https://api.flow.engineer/v0/sandbox/keys",
"doc_url": "https://api.flow.engineer/docs/errors/authentication"
}
}
{
"error": {
"type": "permission",
"message": "This account handles health information under a business associate agreement, and Telegram is not a channel Flow holds such an agreement for, so it cannot be simulated here either.",
"hint": "Simulate an SMS conversation instead: POST /v0/test/conversations with {\"channel\":\"sms\"}.",
"doc_url": "https://api.flow.engineer/docs/errors/permission",
"param": "conversation_id",
"channel_code": "hipaa_channel_not_covered"
}
}
{
"error": {
"type": "idempotency_conflict",
"message": "This Idempotency-Key was already used for a different request (another method, path or body).",
"hint": "Use a new Idempotency-Key for this request; reuse a key only to repeat the identical request.",
"doc_url": "https://api.flow.engineer/docs/errors/idempotency_conflict",
"param": "Idempotency-Key",
"channel_code": "body_mismatch"
}
}
{
"error": {
"type": "invalid_request",
"message": "The request body is larger than 33554432 bytes.",
"hint": "Send a smaller body; upload media with POST /v0/files and send its file_id instead of inlining it.",
"doc_url": "https://api.flow.engineer/docs/errors/invalid_request"
}
}
{
"error": {
"type": "unsupported_content",
"channel": "telegram",
"param": "kind",
"message": "Carousels work on WhatsApp only.",
"hint": "On Telegram, send a basic message, then one buttons message with an image header per card.",
"doc_url": "https://api.flow.engineer/docs/errors/unsupported_content",
"conversation": "conv_01JB8ZC3K5M7P9R1T3V5X7Z9B1"
}
}
{
"error": {
"type": "new_contact_limit",
"message": "This sender has started its 15 new conversations for today.",
"hint": "Retry after 3600 seconds; replies into existing conversations still go.",
"doc_url": "https://api.flow.engineer/docs/errors/new_contact_limit",
"retry_after": 3600,
"sender": "snd_01JB8Z4Q3V6W0R2N7C5H1M9K4T"
}
}
An API key of one app, sent as Authorization: Bearer <key>. Keys start with
fk_test_ (test mode: sandbox senders and test data only) or fk_live_
(live mode). Keep live keys on your server; never ship them in an app or page.
A unique string (up to 255 characters) that makes this request safe to retry.
A repeat with the same key within 24 hours returns the first answer instead of
acting again. See "Idempotency" in the introduction.
What a simulated contact sends in one turn, as a message kind with kind
(required): basic (text and/or media by file_id), voice (by
file_id), location, contact_card, or button_reply (a tap on a reply
button, or on a list row, of a message your app sent). Fields a contact
cannot send (reply_to and metadata inside the kind, channel blocks,
link_preview, url) and other kinds answer 400 invalid_request.
Simulated group conversations only, and required there. The participant who writes this turn (a contact in the conversation's group.participants); another contact answers 400 invalid_request with paramfrom. Refused the same way on one-to-one conversations.
A message of this conversation the contact replies to inline, on channels with inline replies (Telegram, WhatsApp, iMessage; on SMS it answers 400 invalid_request). The received message shows it in reply_to.
Pattern: ^msg_[0-9A-HJKMNP-TV-Z]{26}$
Example:
"msg_01JB8ZD4M6P8R0T2V4X6Z8B0C2"
Response
The turn was accepted; message.received follows.
A simulated turn Flow accepted. Its message.received event follows.
Always queued; the message is stored and delivered right after.
Available options:
queued
{"docsConfig":{"theme":"mint","$schema":"https://mintlify.com/docs.json","name":"Flow","description":"Build for every chat interface with Flow Framework, or connect your agent through the Unified Messaging API.","colors":{"primary":"#0b7a5a","light":"#3cc497","dark":"#0b7a5a"},"api":{"playground":{"display":"interactive"},"examples":{"defaults":"required","languages":["curl","javascript","python","go"]}},"navbar":{"links":[{"href":"https://flow.engineer","label":"Website"},{"href":"https://github.com/flow-engineer/sdk","label":"Messaging SDKs"}],"primary":{"type":"button","label":"GitHub","href":"https://github.com/flow-engineer/flow"}},"footer":{"socials":{"github":"https://github.com/flow-engineer/flow","website":"https://flow.engineer"}},"search":{"prompt":"Search Flow docs"},"seo":{"indexing":"navigable"},"contextual":{"options":["copy","view","chatgpt","claude","cursor","vscode"],"display":"header"},"styling":{"latex":false},"navigation":{"pages":[]}},"deployment":{"deploymentMetadataValue":{"subdomain":"flow-messaging","hasAudioNarration":false,"actualSubdomain":"flow-messaging","customDomains":["docs.flow.engineer"],"gitSource":{"type":"github","owner":"flow-engineer","repo":"docs","deployBranch":"main","contentDirectory":"docs","isPrivate":true,"mountPath":"","label":"","_id":"6ada57ce640382e777556fe3"},"chroma":{"latestBuiltDeploymentHistoryId":"6adcda2f2673ac6bc7cb84b8","collectionId":"13fc76e6-8ba2-4c4a-a3f9-68b8b26261dc","liveDeploymentHistoryId":"6adcda2f2673ac6bc7cb84b8","schemaVersion":"v3"},"entitlements":{"AI_CHAT":{"status":"DISABLED"},"WEB_MCP":{"status":"DISABLED"},"NARRATION":{"status":"DISABLED"}},"buildId":"6addc044fbe3b121222676b9","clientVersion":"0.0.3965","searchFilterCounts":[{"language":"default","tag":"Messaging API","count":100},{"language":"default","tag":"HTTP API","count":64},{"language":"default","tag":"Framework","count":27},{"language":"default","tag":"Messages","count":14},{"language":"default","tag":"Errors","count":14},{"language":"default","tag":"Ship to","count":13},{"language":"default","tag":"Concepts","count":11},{"language":"default","tag":"Get started","count":10},{"language":"default","tag":"Senders and templates","count":8},{"language":"default","tag":"Webhook endpoints","count":6}]},"assistantConfig":{"enableAskAiButton":false,"enableStarterQuestions":false,"enableContextualStarterQuestions":false,"enableAskAiSearchBar":true},"hasChatPermissions":false,"hasWebMcpPermissions":false,"toggles":[{"name":"kill-isr","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-mintlify-slack","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-v7-file-tree","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-integrations-settings","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-pr-grouping","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"custom-prefix","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"analytics-v7","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"user-flows-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"no-publish-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-onboarding","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"llms-txt-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"pause-universal-search-reconcile","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving-live","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"stripTemperatureMiddleware","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"per-site-search-delta","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"support-platform-pylon-zendesk","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"instant-rollback","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications-server","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-private-page-search","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-fable","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fixed-ai-action-pricing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automation-resolve-merge-conflict","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"scoped-publishing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"mintql","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"page-level-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-version-history-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-nav-generations","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"billing-page-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workspace-projects","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"assistant-v5","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"byok-sep-25","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fork-pr-previews","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"revalidation-jwt","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"vite-origin","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-agent-model-picker","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"billing-updated-9-15","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"hobby-abuse-scan","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"dashboard-inbox","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false}],"isAtlas":false,"googleFontsInlined":false},"customCss":[],"page":{"pagePath":"api-reference/testing/write-as-the-simulated-contact","slug":"/api-reference/testing/write-as-the-simulated-contact","pageMetadata":{"openapi":"unified-messaging-api/openapi.yaml POST /v0/test/conversations/{conversation_id}/inbound","title":"Write as the simulated contact","description":"Test keys only. Posts one inbound turn from the simulated contact of a\nsimulated conversation. It goes through the same inbound path as a real\nchannel's message: Flow stores it, then delivers it as a `message.received`\nevent by webhook, on the stream and in the event log, in order with the\nconversation's other events. A webhook answer's `reply` works as usual. The\nanswer comes before delivery; the `message.received` event carries the\n`message` ID given here.\n\n`content` is what the contact sends, as a kind object with `kind`: `basic`\n(`text` and/or `media`), `voice`, `location`, `contact_card` or\n`button_reply` (a tap on a reply button of a buttons or carousel message\nyour agent sent, or on a row of a list, given as its `message_id` and\n`button_id`: Flow fills in the `label`). Media and voice take the\n`file_id` of a file uploaded with `POST /v0/files` (no `url`); the contact's\nmessage refers to that same file (its `file_id`, `url`, `mime_type` and\n`size_bytes`): Flow does not copy it again. The channel's rules apply:\nwhat it cannot carry from a person (a document over SMS, a location over\nSMS, a file larger than the channel fetches from a person) answers\n`422 unsupported_content`, and text over the channel's limit answers\n`400 invalid_request`.\n\nOn SMS, the keywords work as from a real phone, matched against the whole\ntext. STOP (or STOPALL, UNSUBSCRIBE, CANCEL, END, QUIT, REVOKE, OPT OUT,\nOPTOUT) opts the simulated contact out against the simulated sender: the\ncontact's `opted_out_at` is set and `opted_out_via` is `keyword`, and sends\nto them answer `403 permission` with `channel_code` `contact_opted_out`.\nSTART (or UNSTOP, YES) lifts it. The turn is still delivered as\n`message.received`. A keyword reply is never recorded as a message, so the\ntranscript shows no automatic reply to STOP, START or HELP.\n\nOn WhatsApp, each turn opens the conversation's 24-hour window, as a real\ncontact's message does.\n\nA conversation that is not simulated answers `403 permission` with\n`channel_code` `not_simulated`; a live key answers `403 permission` with\n`channel_code` `test_mode_only`. In a HIPAA account (`Account.hipaa`), a\nturn on a simulated `telegram`, `whatsapp` or `imessage` conversation (one made before\nthe flag was set) answers `403 permission` with `channel_code`\n`hipaa_channel_not_covered` (`param` `conversation_id`).\n","href":"/api-reference/testing/write-as-the-simulated-contact"},"description":{"compiledSource":"\"use strict\";\nconst {Fragment: _Fragment, jsx: _jsx, jsxs: _jsxs} = arguments[0];\nconst {useMDXComponents: _provideComponents} = arguments[0];\nfunction _createMdxContent(props) {\n const _components = {\n code: \"code\",\n p: \"p\",\n ..._provideComponents(),\n ...props.components\n };\n return _jsxs(_Fragment, {\n children: [_jsxs(_components.p, {\n children: [\"Test keys only. Posts one inbound turn from the simulated contact of a\\nsimulated conversation. It goes through the same inbound path as a real\\nchannel’s message: Flow stores it, then delivers it as a \", _jsx(_components.code, {\n children: \"message.received\"\n }), \"\\nevent by webhook, on the stream and in the event log, in order with the\\nconversation’s other events. A webhook answer’s \", _jsx(_components.code, {\n children: \"reply\"\n }), \" works as usual. The\\nanswer comes before delivery; the \", _jsx(_components.code, {\n children: \"message.received\"\n }), \" event carries the\\n\", _jsx(_components.code, {\n children: \"message\"\n }), \" ID given here.\"]\n }), \"\\n\", _jsxs(_components.p, {\n children: [_jsx(_components.code, {\n children: \"content\"\n }), \" is what the contact sends, as a kind object with \", _jsx(_components.code, {\n children: \"kind\"\n }), \": \", _jsx(_components.code, {\n children: \"basic\"\n }), \"\\n(\", _jsx(_components.code, {\n children: \"text\"\n }), \" and/or \", _jsx(_components.code, {\n children: \"media\"\n }), \"), \", _jsx(_components.code, {\n children: \"voice\"\n }), \", \", _jsx(_components.code, {\n children: \"location\"\n }), \", \", _jsx(_components.code, {\n children: \"contact_card\"\n }), \" or\\n\", _jsx(_components.code, {\n children: \"button_reply\"\n }), \" (a tap on a reply button of a buttons or carousel message\\nyour agent sent, or on a row of a list, given as its \", _jsx(_components.code, {\n children: \"message_id\"\n }), \" and\\n\", _jsx(_components.code, {\n children: \"button_id\"\n }), \": Flow fills in the \", _jsx(_components.code, {\n children: \"label\"\n }), \"). Media and voice take the\\n\", _jsx(_components.code, {\n children: \"file_id\"\n }), \" of a file uploaded with \", _jsx(_components.code, {\n children: \"POST /v0/files\"\n }), \" (no \", _jsx(_components.code, {\n children: \"url\"\n }), \"); the contact’s\\nmessage refers to that same file (its \", _jsx(_components.code, {\n children: \"file_id\"\n }), \", \", _jsx(_components.code, {\n children: \"url\"\n }), \", \", _jsx(_components.code, {\n children: \"mime_type\"\n }), \" and\\n\", _jsx(_components.code, {\n children: \"size_bytes\"\n }), \"): Flow does not copy it again. The channel’s rules apply:\\nwhat it cannot carry from a person (a document over SMS, a location over\\nSMS, a file larger than the channel fetches from a person) answers\\n\", _jsx(_components.code, {\n children: \"422 unsupported_content\"\n }), \", and text over the channel’s limit answers\\n\", _jsx(_components.code, {\n children: \"400 invalid_request\"\n }), \".\"]\n }), \"\\n\", _jsxs(_components.p, {\n children: [\"On SMS, the keywords work as from a real phone, matched against the whole\\ntext. STOP (or STOPALL, UNSUBSCRIBE, CANCEL, END, QUIT, REVOKE, OPT OUT,\\nOPTOUT) opts the simulated contact out against the simulated sender: the\\ncontact’s \", _jsx(_components.code, {\n children: \"opted_out_at\"\n }), \" is set and \", _jsx(_components.code, {\n children: \"opted_out_via\"\n }), \" is \", _jsx(_components.code, {\n children: \"keyword\"\n }), \", and sends\\nto them answer \", _jsx(_components.code, {\n children: \"403 permission\"\n }), \" with \", _jsx(_components.code, {\n children: \"channel_code\"\n }), \" \", _jsx(_components.code, {\n children: \"contact_opted_out\"\n }), \".\\nSTART (or UNSTOP, YES) lifts it. The turn is still delivered as\\n\", _jsx(_components.code, {\n children: \"message.received\"\n }), \". A keyword reply is never recorded as a message, so the\\ntranscript shows no automatic reply to STOP, START or HELP.\"]\n }), \"\\n\", _jsx(_components.p, {\n children: \"On WhatsApp, each turn opens the conversation’s 24-hour window, as a real\\ncontact’s message does.\"\n }), \"\\n\", _jsxs(_components.p, {\n children: [\"A conversation that is not simulated answers \", _jsx(_components.code, {\n children: \"403 permission\"\n }), \" with\\n\", _jsx(_components.code, {\n children: \"channel_code\"\n }), \" \", _jsx(_components.code, {\n children: \"not_simulated\"\n }), \"; a live key answers \", _jsx(_components.code, {\n children: \"403 permission\"\n }), \" with\\n\", _jsx(_components.code, {\n children: \"channel_code\"\n }), \" \", _jsx(_components.code, {\n children: \"test_mode_only\"\n }), \". In a HIPAA account (\", _jsx(_components.code, {\n children: \"Account.hipaa\"\n }), \"), a\\nturn on a simulated \", _jsx(_components.code, {\n children: \"telegram\"\n }), \", \", _jsx(_components.code, {\n children: \"whatsapp\"\n }), \" or \", _jsx(_components.code, {\n children: \"imessage\"\n }), \" conversation (one made before\\nthe flag was set) answers \", _jsx(_components.code, {\n children: \"403 permission\"\n }), \" with \", _jsx(_components.code, {\n children: \"channel_code\"\n }), \"\\n\", _jsx(_components.code, {\n children: \"hipaa_channel_not_covered\"\n }), \" (\", _jsx(_components.code, {\n children: \"param\"\n }), \" \", _jsx(_components.code, {\n children: \"conversation_id\"\n }), \").\"]\n })]\n });\n}\nfunction MDXContent(props = {}) {\n const {wrapper: MDXLayout} = {\n ..._provideComponents(),\n ...props.components\n };\n return MDXLayout ? _jsx(MDXLayout, {\n ...props,\n children: _jsx(_createMdxContent, {\n ...props\n })\n }) : _createMdxContent(props);\n}\nreturn {\n default: MDXContent\n};\n","frontmatter":{},"scope":{}},"pageType":"default","mdx":{"mdxSource":{"compiledSource":"\"use strict\";const{Fragment:_Fragment,jsx:_jsx}=arguments[0];const{useMDXComponents:_provideComponents}=arguments[0];function _createMdxContent(props){return _jsx(_Fragment,{})}function MDXContent(props={}){const{wrapper:MDXLayout}={..._provideComponents(),...props.components};return MDXLayout?_jsx(MDXLayout,{...props,children:_jsx(_createMdxContent,{...props})}):_createMdxContent(props)}return{default:MDXContent}","frontmatter":{},"scope":{"config":{},"pageMetadata":{"openapi":"unified-messaging-api/openapi.yaml POST /v0/test/conversations/{conversation_id}/inbound","title":"Write as the simulated contact","description":"Test keys only. Posts one inbound turn from the simulated contact of a\nsimulated conversation. It goes through the same inbound path as a real\nchannel's message: Flow stores it, then delivers it as a `message.received`\nevent by webhook, on the stream and in the event log, in order with the\nconversation's other events. A webhook answer's `reply` works as usual. The\nanswer comes before delivery; the `message.received` event carries the\n`message` ID given here.\n\n`content` is what the contact sends, as a kind object with `kind`: `basic`\n(`text` and/or `media`), `voice`, `location`, `contact_card` or\n`button_reply` (a tap on a reply button of a buttons or carousel message\nyour agent sent, or on a row of a list, given as its `message_id` and\n`button_id`: Flow fills in the `label`). Media and voice take the\n`file_id` of a file uploaded with `POST /v0/files` (no `url`); the contact's\nmessage refers to that same file (its `file_id`, `url`, `mime_type` and\n`size_bytes`): Flow does not copy it again. The channel's rules apply:\nwhat it cannot carry from a person (a document over SMS, a location over\nSMS, a file larger than the channel fetches from a person) answers\n`422 unsupported_content`, and text over the channel's limit answers\n`400 invalid_request`.\n\nOn SMS, the keywords work as from a real phone, matched against the whole\ntext. STOP (or STOPALL, UNSUBSCRIBE, CANCEL, END, QUIT, REVOKE, OPT OUT,\nOPTOUT) opts the simulated contact out against the simulated sender: the\ncontact's `opted_out_at` is set and `opted_out_via` is `keyword`, and sends\nto them answer `403 permission` with `channel_code` `contact_opted_out`.\nSTART (or UNSTOP, YES) lifts it. The turn is still delivered as\n`message.received`. A keyword reply is never recorded as a message, so the\ntranscript shows no automatic reply to STOP, START or HELP.\n\nOn WhatsApp, each turn opens the conversation's 24-hour window, as a real\ncontact's message does.\n\nA conversation that is not simulated answers `403 permission` with\n`channel_code` `not_simulated`; a live key answers `403 permission` with\n`channel_code` `test_mode_only`. In a HIPAA account (`Account.hipaa`), a\nturn on a simulated `telegram`, `whatsapp` or `imessage` conversation (one made before\nthe flag was set) answers `403 permission` with `channel_code`\n`hipaa_channel_not_covered` (`param` `conversation_id`).\n","href":"/api-reference/testing/write-as-the-simulated-contact"}}},"mdxSourceWithNoJs":null},"mdxExtracts":{"tableOfContents":[],"codeExamples":{}},"jsLinks":["/_mintlify/custom/flow-messaging/8b05b91b2b2979cc/default-host-redirect.js"],"openApiReferenceData":{"operation":{"operationId":"simulateInbound","tags":["Testing"],"summary":"Write as the simulated contact","description":"Test keys only. Posts one inbound turn from the simulated contact of a\nsimulated conversation. It goes through the same inbound path as a real\nchannel's message: Flow stores it, then delivers it as a `message.received`\nevent by webhook, on the stream and in the event log, in order with the\nconversation's other events. A webhook answer's `reply` works as usual. The\nanswer comes before delivery; the `message.received` event carries the\n`message` ID given here.\n\n`content` is what the contact sends, as a kind object with `kind`: `basic`\n(`text` and/or `media`), `voice`, `location`, `contact_card` or\n`button_reply` (a tap on a reply button of a buttons or carousel message\nyour agent sent, or on a row of a list, given as its `message_id` and\n`button_id`: Flow fills in the `label`). Media and voice take the\n`file_id` of a file uploaded with `POST /v0/files` (no `url`); the contact's\nmessage refers to that same file (its `file_id`, `url`, `mime_type` and\n`size_bytes`): Flow does not copy it again. The channel's rules apply:\nwhat it cannot carry from a person (a document over SMS, a location over\nSMS, a file larger than the channel fetches from a person) answers\n`422 unsupported_content`, and text over the channel's limit answers\n`400 invalid_request`.\n\nOn SMS, the keywords work as from a real phone, matched against the whole\ntext. STOP (or STOPALL, UNSUBSCRIBE, CANCEL, END, QUIT, REVOKE, OPT OUT,\nOPTOUT) opts the simulated contact out against the simulated sender: the\ncontact's `opted_out_at` is set and `opted_out_via` is `keyword`, and sends\nto them answer `403 permission` with `channel_code` `contact_opted_out`.\nSTART (or UNSTOP, YES) lifts it. The turn is still delivered as\n`message.received`. A keyword reply is never recorded as a message, so the\ntranscript shows no automatic reply to STOP, START or HELP.\n\nOn WhatsApp, each turn opens the conversation's 24-hour window, as a real\ncontact's message does.\n\nA conversation that is not simulated answers `403 permission` with\n`channel_code` `not_simulated`; a live key answers `403 permission` with\n`channel_code` `test_mode_only`. In a HIPAA account (`Account.hipaa`), a\nturn on a simulated `telegram`, `whatsapp` or `imessage` conversation (one made before\nthe flag was set) answers `403 permission` with `channel_code`\n`hipaa_channel_not_covered` (`param` `conversation_id`).\n","parameters":["df5bc29f-f3c9-4bbb-bde8-3a492797cede"],"requestBody":"b65838f7-0a2c-4578-b22d-32a87477af0b","responses":{"202":"090ebf18-1dc3-4d7d-a913-0eda973ce401","400":"ad93ef71-30f2-4bdf-b755-19fe6dd60677","401":"a12abdeb-0c7b-4698-a59f-588a91bda27c","403":"da1c6377-5cf4-4dad-bd0b-1541aece5ce9","404":"d69637de-addf-40b0-afef-cc84f7ab92f6","409":"27a2aa50-4498-47f1-9ad3-ea35a2fd9db5","413":"5fb17786-743e-473a-bba0-d31d79e937ad","422":"59a32a55-4d2c-44f1-b867-785ea889d5d8","429":"8846f775-97b0-4cca-951a-13435feb3ad5","default":"b40cfab2-5741-4673-8985-65e58f8a4e17"},"method":"post","type":"path","servers":["90a9657d-20c1-4182-87ad-a4d557fcda08"],"security":["59c5f960-0d19-49e7-944f-bfe75a0ffe7d"],"path":"/v0/test/conversations/{conversation_id}/inbound","baseUrlOptions":["https://api.flow.engineer"],"title":"Write as the simulated contact","codeSamples":[],"requestExampleLanguages":["bash","javascript","python","go"],"requiredOnlyExamples":true,"requestExampleType":"generated","responseExampleType":"generated"},"dependencies":{"parameters":{"df5bc29f-f3c9-4bbb-bde8-3a492797cede":{"name":"Idempotency-Key","in":"header","required":false,"description":"A unique string (up to 255 characters) that makes this request safe to retry.\nA repeat with the same key within 24 hours returns the first answer instead of\nacting again. See \"Idempotency\" in the introduction.\n","schema":{"type":"string","minLength":1,"maxLength":255,"isRequired":false,"description":"A unique string (up to 255 characters) that makes this request safe to retry.\nA repeat with the same key within 24 hours returns the first answer instead of\nacting again. See \"Idempotency\" in the introduction.\n","uniqueKey":"","typeLabel":"string"}},"e26ac2c6-dc5c-4af8-8651-9f8a115b2475":{"name":"conversation_id","in":"path","required":true,"description":"The conversation's ID.","schema":{"type":"string","description":"The conversation's ID.\nA conversation ID, `conv_` and a ULID.","pattern":"^conv_[0-9A-HJKMNP-TV-Z]{26}$","examples":["conv_01JB8ZC3K5M7P9R1T3V5X7Z9B1"],"isRequired":true,"uniqueKey":"","typeLabel":"string"}},"6a38632f-c508-4055-84bd-4dd4b0dfa530":{"name":"Flow-Version","in":"header","required":false,"description":"The API version to use, as a date. Without it, the version pinned to your app when it was created is used.","schema":{"type":"string","format":"date","examples":["2026-11-01"],"isRequired":false,"description":"The API version to use, as a date. Without it, the version pinned to your app when it was created is used.","uniqueKey":"","typeLabel":"string\u003cdate>"}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","description":"One turn from a simulated contact (test keys only).","required":["content"],"properties":{"content":{"oneOf":[{"type":"object","description":"What a simulated contact sends in one turn, as a message kind with `kind`\n(required): `basic` (`text` and/or `media` by `file_id`), `voice` (by\n`file_id`), `location`, `contact_card`, or `button_reply` (a tap on a reply\nbutton, or on a list row, of a message your app sent). Fields a contact\ncannot send (`reply_to` and `metadata` inside the kind, channel blocks,\n`link_preview`, `url`) and other kinds answer `400 invalid_request`.\n","required":["kind"],"properties":{"kind":{"type":"string","const":"basic","default":"basic","description":"Always `basic`. Optional on `POST /v0/conversations/{conversation_id}/basic`; required where no URL names the kind (webhook replies, the stream, starts, a message's `content`).","isRequired":true,"uniqueKey":"content.oneOf.0.kind","typeLabel":"string"},"text":{"type":"string","minLength":1,"maxLength":9999,"description":"The text, sent exactly as written: no markdown, no stripping. At most\n4096 UTF-16 code units on Telegram (an emoji such as 😀 counts as 2),\n4096 characters on WhatsApp, 9999 on iMessage, and 10 segments on SMS\n(see `SmsSegments`). With `media` it is the caption, at most 1024 (UTF-16\ncode units on Telegram). Optional when `media` is given. Longer text is\nrefused with `400 invalid_request`.\n","uniqueKey":"content.oneOf.0.text","typeLabel":"string"},"media":{"type":"object","description":"One image, video, document or audio file. When sending, give exactly one of\n`url` (an HTTPS URL Flow fetches; Flow neither stores nor scans it, and on\nTelegram a document by URL works for PDF and ZIP files) or `file_id` (from\n`POST /v0/files`). Each channel takes files up to its own size: Telegram\n50 MB, WhatsApp and iMessage 100 MB, SMS (MMS) 5 MB; a larger file answers\n`422 unsupported_content` with `param` `media`. SMS carries images, audio\nand video, never documents (`param` `media.type`), and only from numbers\nwith MMS (`channel_code` `no_mms`).\n","required":["type"],"properties":{"type":{"type":"string","enum":["image","video","document","audio"],"description":"What kind of file it is.","x-enum-varnames":["MediaImage","MediaVideo","MediaDocument","MediaAudio"],"isRequired":true,"uniqueKey":"content.oneOf.0.media.type","typeLabel":"enum\u003cstring>"},"url":{"type":"string","format":"uri","description":"Where the bytes are. Sending, an HTTPS URL Flow can fetch; inbound, a Flow file URL.","uniqueKey":"content.oneOf.0.media.url","typeLabel":"string\u003curi>"},"file_id":{"type":"string","description":"A file ID, `file_` and a ULID.","pattern":"^file_[0-9A-HJKMNP-TV-Z]{26}$","examples":["file_01JB8ZG7Q9S1V3X5Z7B9D1F3G5"],"uniqueKey":"content.oneOf.0.media.file_id","typeLabel":"string"},"filename":{"type":"string","maxLength":255,"description":"The file's name, shown for documents and taken only with `type` `document` (on another type it answers `422 unsupported_content` with `param` `media.filename`). On Telegram it works only with `file_id`: Telegram names a document sent by `url` itself, so `filename` with `url` answers `422 unsupported_content` with `param` `media.filename`.","uniqueKey":"content.oneOf.0.media.filename","typeLabel":"string"}},"uniqueKey":"content.oneOf.0.media","typeLabel":"object"},"reply_to":{"type":"string","description":"Send this as an inline reply to an earlier message in the same\nconversation (Telegram, WhatsApp and iMessage show it quoted). SMS has no\ninline replies: there it answers `422 unsupported_content` with `param`\n`reply_to`. Refused with `404 not_found` (`param` `reply_to`) when the\ntarget is not a message in this conversation, and with `400\ninvalid_request` (`param` `reply_to`) when the target never reached the\nchannel (for example, it failed). If the target fails after the send was\naccepted, the reply goes out as a normal message.\n","pattern":"^msg_[0-9A-HJKMNP-TV-Z]{26}$","examples":["msg_01JB8ZD4M6P8R0T2V4X6Z8B0C2"],"uniqueKey":"content.oneOf.0.reply_to","typeLabel":"string"},"link_preview":{"type":"boolean","description":"Whether links in the text show a rich preview. Left out, the channel's\ndefault applies (Telegram, WhatsApp and iMessage show previews).\n`false` sends links as plain text. SMS never shows previews, and no\nchannel shows one on a media message: `false` is accepted there, and\n`true` on SMS or with `media` answers `422 unsupported_content` with\n`param` `link_preview`.\n","uniqueKey":"content.oneOf.0.link_preview","typeLabel":"boolean"},"telegram":{"type":"object","description":"Telegram only: how the message looks and notifies on Telegram. On another\nchannel the block answers `422 unsupported_content` with `param`\n`telegram`; an unknown key `400 invalid_request`.\n","additionalProperties":false,"properties":{"parse_mode":{"type":"string","description":"How Telegram reads the text: `HTML` or `MarkdownV2`, Telegram's own\nformatting (see Telegram's Bot API, \"Formatting options\"). Without it the\ntext is plain. Text that Telegram cannot parse is refused by Telegram:\nthe message fails (`message.failed`, `channel_error`).\n","enum":["HTML","MarkdownV2"],"x-enum-varnames":["ParseModeHTML","ParseModeMarkdownV2"],"uniqueKey":"content.oneOf.0.telegram.parse_mode","typeLabel":"enum\u003cstring>"},"disable_notification":{"type":"boolean","description":"Deliver silently, with no sound.","uniqueKey":"content.oneOf.0.telegram.disable_notification","typeLabel":"boolean"},"protect_content":{"type":"boolean","description":"Stop the contact forwarding or saving the message.","uniqueKey":"content.oneOf.0.telegram.protect_content","typeLabel":"boolean"},"show_caption_above_media":{"type":"boolean","description":"Media only. Show the caption above the photo or video instead of below it.","uniqueKey":"content.oneOf.0.telegram.show_caption_above_media","typeLabel":"boolean"},"has_spoiler":{"type":"boolean","description":"Media only (photos and videos). Cover it with a spoiler animation until tapped.","uniqueKey":"content.oneOf.0.telegram.has_spoiler","typeLabel":"boolean"}},"uniqueKey":"content.oneOf.0.telegram","typeLabel":"object"},"imessage":{"type":"object","description":"iMessage only: styles and a subject line. On another channel the block\nanswers `422 unsupported_content` with `param` `imessage`; an unknown key\n`400 invalid_request`. A message that goes by SMS or RCS (see\n`Message.network`) shows neither.\n","additionalProperties":false,"properties":{"styles":{"type":"array","description":"Styled ranges of `text` (see `ImessageStyle`). Each range must lie within `text`; one that does not answers `400 invalid_request` naming it (`imessage.styles[0]`).","items":{"type":"object","description":"One styled range of an iMessage text.","required":["start","length","style"],"properties":{"start":{"type":"integer","minimum":0,"description":"Where the range starts, in UTF-16 code units from the start of `text`.","isRequired":true,"uniqueKey":"content.oneOf.0.imessage.styles.items.[INDEX].start","typeLabel":"integer"},"length":{"type":"integer","minimum":1,"description":"How long the range is, in UTF-16 code units.","isRequired":true,"uniqueKey":"content.oneOf.0.imessage.styles.items.[INDEX].length","typeLabel":"integer"},"style":{"type":"string","description":"The style. `big` and `small` set the text's size; `shake`, `nod`, `explode`, `ripple`, `bloom` and `jitter` animate it.","enum":["bold","italic","underline","strikethrough","big","small","shake","nod","explode","ripple","bloom","jitter"],"isRequired":true,"uniqueKey":"content.oneOf.0.imessage.styles.items.[INDEX].style","typeLabel":"enum\u003cstring>"}},"uniqueKey":"content.oneOf.0.imessage.styles.items.[INDEX]","typeLabel":"object"},"uniqueKey":"content.oneOf.0.imessage.styles","typeLabel":"object[]"},"subject":{"type":"string","minLength":1,"maxLength":200,"description":"iMessage's subject line, shown in bold above the text.","uniqueKey":"content.oneOf.0.imessage.subject","typeLabel":"string"}},"uniqueKey":"content.oneOf.0.imessage","typeLabel":"object"},"metadata":{"type":"object","description":"Up to 20 string pairs of your own, kept with the object and returned unchanged. Keys up to 40 characters, values up to 500.","maxProperties":20,"additionalProperties":{"type":"string","maxLength":500,"uniqueKey":"content.oneOf.0.metadata.additionalProperties","typeLabel":"string"},"uniqueKey":"content.oneOf.0.metadata","typeLabel":"object"}},"isRequired":true,"uniqueKey":"content.oneOf.0","typeLabel":"object"},{"type":"object","description":"What a simulated contact sends in one turn, as a message kind with `kind`\n(required): `basic` (`text` and/or `media` by `file_id`), `voice` (by\n`file_id`), `location`, `contact_card`, or `button_reply` (a tap on a reply\nbutton, or on a list row, of a message your app sent). Fields a contact\ncannot send (`reply_to` and `metadata` inside the kind, channel blocks,\n`link_preview`, `url`) and other kinds answer `400 invalid_request`.\n","required":["kind"],"properties":{"kind":{"type":"string","const":"voice","default":"voice","description":"Always `voice`. Optional on its endpoint; required where no URL names the kind.","isRequired":true,"uniqueKey":"content.oneOf.1.kind","typeLabel":"string"},"url":{"type":"string","format":"uri","description":"Where the audio is. Sending, an HTTPS URL Flow can fetch; inbound, a Flow file URL.","uniqueKey":"content.oneOf.1.url","typeLabel":"string\u003curi>"},"file_id":{"type":"string","description":"A file ID, `file_` and a ULID.","pattern":"^file_[0-9A-HJKMNP-TV-Z]{26}$","examples":["file_01JB8ZG7Q9S1V3X5Z7B9D1F3G5"],"uniqueKey":"content.oneOf.1.file_id","typeLabel":"string"},"reply_to":{"type":"string","description":"Send this as an inline reply to an earlier message in the same\nconversation (Telegram, WhatsApp and iMessage show it quoted). SMS has no\ninline replies: there it answers `422 unsupported_content` with `param`\n`reply_to`. Refused with `404 not_found` (`param` `reply_to`) when the\ntarget is not a message in this conversation, and with `400\ninvalid_request` (`param` `reply_to`) when the target never reached the\nchannel (for example, it failed). If the target fails after the send was\naccepted, the reply goes out as a normal message.\n","pattern":"^msg_[0-9A-HJKMNP-TV-Z]{26}$","examples":["msg_01JB8ZD4M6P8R0T2V4X6Z8B0C2"],"uniqueKey":"content.oneOf.1.reply_to","typeLabel":"string"},"metadata":{"type":"object","description":"Up to 20 string pairs of your own, kept with the object and returned unchanged. Keys up to 40 characters, values up to 500.","maxProperties":20,"additionalProperties":{"type":"string","maxLength":500,"uniqueKey":"content.oneOf.1.metadata.additionalProperties","typeLabel":"string"},"uniqueKey":"content.oneOf.1.metadata","typeLabel":"object"}},"isRequired":true,"uniqueKey":"content.oneOf.1","typeLabel":"object"},{"type":"object","description":"What a simulated contact sends in one turn, as a message kind with `kind`\n(required): `basic` (`text` and/or `media` by `file_id`), `voice` (by\n`file_id`), `location`, `contact_card`, or `button_reply` (a tap on a reply\nbutton, or on a list row, of a message your app sent). Fields a contact\ncannot send (`reply_to` and `metadata` inside the kind, channel blocks,\n`link_preview`, `url`) and other kinds answer `400 invalid_request`.\n","required":["kind","lat","lng"],"properties":{"kind":{"type":"string","const":"location","default":"location","description":"Always `location`. Optional on its endpoint; required where no URL names the kind.","isRequired":true,"uniqueKey":"content.oneOf.2.kind","typeLabel":"string"},"lat":{"type":"number","minimum":-90,"maximum":90,"description":"Latitude in degrees.","isRequired":true,"uniqueKey":"content.oneOf.2.lat","typeLabel":"number"},"lng":{"type":"number","minimum":-180,"maximum":180,"description":"Longitude in degrees.","isRequired":true,"uniqueKey":"content.oneOf.2.lng","typeLabel":"number"},"name":{"type":"string","description":"The place's name.","uniqueKey":"content.oneOf.2.name","typeLabel":"string"},"address":{"type":"string","description":"The place's address.","uniqueKey":"content.oneOf.2.address","typeLabel":"string"},"reply_to":{"type":"string","description":"Send this as an inline reply to an earlier message in the same\nconversation (Telegram, WhatsApp and iMessage show it quoted). SMS has no\ninline replies: there it answers `422 unsupported_content` with `param`\n`reply_to`. Refused with `404 not_found` (`param` `reply_to`) when the\ntarget is not a message in this conversation, and with `400\ninvalid_request` (`param` `reply_to`) when the target never reached the\nchannel (for example, it failed). If the target fails after the send was\naccepted, the reply goes out as a normal message.\n","pattern":"^msg_[0-9A-HJKMNP-TV-Z]{26}$","examples":["msg_01JB8ZD4M6P8R0T2V4X6Z8B0C2"],"uniqueKey":"content.oneOf.2.reply_to","typeLabel":"string"},"metadata":{"type":"object","description":"Up to 20 string pairs of your own, kept with the object and returned unchanged. Keys up to 40 characters, values up to 500.","maxProperties":20,"additionalProperties":{"type":"string","maxLength":500,"uniqueKey":"content.oneOf.2.metadata.additionalProperties","typeLabel":"string"},"uniqueKey":"content.oneOf.2.metadata","typeLabel":"object"}},"isRequired":true,"uniqueKey":"content.oneOf.2","typeLabel":"object"},{"type":"object","description":"What a simulated contact sends in one turn, as a message kind with `kind`\n(required): `basic` (`text` and/or `media` by `file_id`), `voice` (by\n`file_id`), `location`, `contact_card`, or `button_reply` (a tap on a reply\nbutton, or on a list row, of a message your app sent). Fields a contact\ncannot send (`reply_to` and `metadata` inside the kind, channel blocks,\n`link_preview`, `url`) and other kinds answer `400 invalid_request`.\n","required":["kind","name"],"properties":{"kind":{"type":"string","const":"contact_card","default":"contact_card","description":"Always `contact_card`. Optional on its endpoint; required where no URL names the kind.","isRequired":true,"uniqueKey":"content.oneOf.3.kind","typeLabel":"string"},"name":{"type":"string","minLength":1,"description":"The person's or business's name.","isRequired":true,"uniqueKey":"content.oneOf.3.name","typeLabel":"string"},"phones":{"type":"array","description":"Phone numbers, E.164 where known. Telegram needs at least one (else `422 unsupported_content`, `param` `phones`).","items":{"type":"string","uniqueKey":"content.oneOf.3.phones.items.[INDEX]","typeLabel":"string"},"uniqueKey":"content.oneOf.3.phones","typeLabel":"string[]"},"emails":{"type":"array","description":"Email addresses.","items":{"type":"string","format":"email","uniqueKey":"content.oneOf.3.emails.items.[INDEX]","typeLabel":"string\u003cemail>"},"uniqueKey":"content.oneOf.3.emails","typeLabel":"string\u003cemail>[]"},"reply_to":{"type":"string","description":"Send this as an inline reply to an earlier message in the same\nconversation (Telegram, WhatsApp and iMessage show it quoted). SMS has no\ninline replies: there it answers `422 unsupported_content` with `param`\n`reply_to`. Refused with `404 not_found` (`param` `reply_to`) when the\ntarget is not a message in this conversation, and with `400\ninvalid_request` (`param` `reply_to`) when the target never reached the\nchannel (for example, it failed). If the target fails after the send was\naccepted, the reply goes out as a normal message.\n","pattern":"^msg_[0-9A-HJKMNP-TV-Z]{26}$","examples":["msg_01JB8ZD4M6P8R0T2V4X6Z8B0C2"],"uniqueKey":"content.oneOf.3.reply_to","typeLabel":"string"},"metadata":{"type":"object","description":"Up to 20 string pairs of your own, kept with the object and returned unchanged. Keys up to 40 characters, values up to 500.","maxProperties":20,"additionalProperties":{"type":"string","maxLength":500,"uniqueKey":"content.oneOf.3.metadata.additionalProperties","typeLabel":"string"},"uniqueKey":"content.oneOf.3.metadata","typeLabel":"object"}},"isRequired":true,"uniqueKey":"content.oneOf.3","typeLabel":"object"},{"type":"object","description":"What a simulated contact sends in one turn, as a message kind with `kind`\n(required): `basic` (`text` and/or `media` by `file_id`), `voice` (by\n`file_id`), `location`, `contact_card`, or `button_reply` (a tap on a reply\nbutton, or on a list row, of a message your app sent). Fields a contact\ncannot send (`reply_to` and `metadata` inside the kind, channel blocks,\n`link_preview`, `url`) and other kinds answer `400 invalid_request`.\n","required":["kind","message_id","button_id"],"example":{"kind":"button_reply","message_id":"msg_01JB8ZD4M6P8R0T2V4X6Z8B0C2","button_id":"yes"},"properties":{"kind":{"type":"string","const":"button_reply","description":"Always `button_reply`.","isRequired":true,"uniqueKey":"content.oneOf.4.kind","typeLabel":"string"},"message_id":{"type":"string","description":"The buttons, carousel or list message your app sent, which must have reached the contact (its `message.sent`).","pattern":"^msg_[0-9A-HJKMNP-TV-Z]{26}$","examples":["msg_01JB8ZD4M6P8R0T2V4X6Z8B0C2"],"isRequired":true,"uniqueKey":"content.oneOf.4.message_id","typeLabel":"string"},"button_id":{"type":"string","minLength":1,"description":"The `id` of the tapped reply button or list row.","isRequired":true,"uniqueKey":"content.oneOf.4.button_id","typeLabel":"string"}},"isRequired":true,"uniqueKey":"content.oneOf.4","typeLabel":"object"}],"isRequired":true,"uniqueKey":"content","typeLabel":"object"},"from":{"type":"string","description":"Simulated group conversations only, and required there. The participant who writes this turn (a `contact` in the conversation's `group.participants`); another contact answers `400 invalid_request` with `param` `from`. Refused the same way on one-to-one conversations.","pattern":"^ct_[0-9A-HJKMNP-TV-Z]{26}$","examples":["ct_01JB8ZB2J4K6N8Q0S2V4W6Y8A0"],"uniqueKey":"from","typeLabel":"string"},"reply_to":{"type":"string","description":"A message of this conversation the contact replies to inline, on channels with inline replies (Telegram, WhatsApp, iMessage; on SMS it answers `400 invalid_request`). The received message shows it in `reply_to`.","pattern":"^msg_[0-9A-HJKMNP-TV-Z]{26}$","examples":["msg_01JB8ZD4M6P8R0T2V4X6Z8B0C2"],"uniqueKey":"reply_to","typeLabel":"string"}},"uniqueKey":"","typeLabel":"object"},"examples":{"text":{"title":"text","value":{"content":{"kind":"basic","text":"Do you deliver on Sundays?"}}},"mms":{"title":"mms","value":{"content":{"kind":"basic","text":"Do you have this in blue?","media":{"type":"image","file_id":"file_01JB8ZG7Q9S1V3X5Z7B9D1F3G5"}}}},"tap":{"title":"tap","value":{"content":{"kind":"button_reply","message_id":"msg_01JB8ZD4M6P8R0T2V4X6Z8B0C2","button_id":"yes"}}}},"exampleType":"examples"}},"exampleType":"examples"},"responses":{"090ebf18-1dc3-4d7d-a913-0eda973ce401":{"description":"The turn was accepted; `message.received` follows.","content":{"application/json":{"schema":{"type":"object","description":"A simulated turn Flow accepted. Its `message.received` event follows.","required":["message","conversation","channel_message_id","status"],"properties":{"message":{"type":"string","description":"The ID the received message will have, in its `message.received` event and in the transcript.","pattern":"^msg_[0-9A-HJKMNP-TV-Z]{26}$","examples":["msg_01JB8ZD4M6P8R0T2V4X6Z8B0C2"],"isRequired":true,"uniqueKey":"message","typeLabel":"string"},"conversation":{"type":"string","description":"A conversation ID, `conv_` and a ULID.","pattern":"^conv_[0-9A-HJKMNP-TV-Z]{26}$","examples":["conv_01JB8ZC3K5M7P9R1T3V5X7Z9B1"],"isRequired":true,"uniqueKey":"conversation","typeLabel":"string"},"channel_message_id":{"type":"string","description":"The simulated channel's ID for the message (the received message's `channel_message_id`).","isRequired":true,"uniqueKey":"channel_message_id","typeLabel":"string"},"status":{"type":"string","description":"Always `queued`; the message is stored and delivered right after.","enum":["queued"],"isRequired":true,"uniqueKey":"status","typeLabel":"enum\u003cstring>"}},"uniqueKey":"","typeLabel":"object"},"examples":{"Example":{"title":"Example","value":{"message":"msg_01JB8ZD4M6P8R0T2V4X6Z8B0C2","conversation":"conv_01JB8ZC3K5M7P9R1T3V5X7Z9B1","channel_message_id":"\u003cstring>","status":"queued"}}},"exampleType":"generated"}},"exampleType":"generated"},"ad93ef71-30f2-4bdf-b755-19fe6dd60677":{"description":"The request is malformed or a parameter is invalid (`invalid_request`).\n`error.param` names the parameter. Every endpoint answers this for a\n`Flow-Version` header that is not a date this API serves (`param`\n`Flow-Version`), and every `POST` that takes an `Idempotency-Key` for a key\nover 255 characters (`param` `Idempotency-Key`).\n","content":{"application/json":{"schema":{"type":"object","description":"The body of every error answer.","required":["error"],"properties":{"error":{"type":"object","description":"The details of an error.","required":["type","message","hint","doc_url"],"properties":{"type":{"type":"string","description":"What went wrong. A closed list: new types arrive only with a new API version.\nEach type has a page at `https://api.flow.engineer/docs/errors/\u003ctype>`, given in\nthe error's `doc_url`; the error's `hint` says what to change for the case.\n\n- `invalid_request` (400, or 413 for a request body over the size limit): the request is malformed or a parameter is invalid. Fix the parameter named in `param`; the `hint` says what it must look like. Every endpoint answers it for a `Flow-Version` header that is not a version this API serves.\n- `authentication` (401): the API key is missing, malformed, unknown, revoked or expired. An `Authorization: Bearer` header with nothing after it usually means the environment variable holding the key was empty when the header was built (for an MCP client, when the server was added): the `hint` says so. Send `Authorization: Bearer fk_test_...` or `fk_live_...` with a current key; with no key at all, get a test key with `POST /v0/sandbox/keys`. A sandbox key past its `expires_at` has `channel_code` `sandbox_key_expired`: sign in to claim the app, or get a new key.\n- `permission` (403): the key may not do this, for example a test key using a live sender, a sandbox contact who joined another app, a new conversation from an iMessage line that may only reply, or a send past the app's sandbox allowance (`channel_code` `sandbox_allowance_used`, `sandbox_contact_limit` or `sandbox_channel_not_included`). Use the key of the right mode, have the contact send your join message, wait for the contact to message the line first, or sign in through the device flow to lift the allowance. On SMS: `contact_opted_out` (the contact texted STOP; nothing more goes to them until they text START), `consent_required` (a start to someone who never texted your numbers: record their consent with `consent` on the start). In a HIPAA account: `hipaa_channel_not_covered` (the channel has no business associate agreement; use SMS). Simulated conversations: `simulated_contact` (a real sender was pointed at a simulated contact; reply in the contact's simulated conversation), `not_simulated` (a simulated sender or test endpoint was pointed at a real contact), `test_mode_only`, `simulated_conversation_limit`. In `message.failed`: `sandbox_contact_left` (the contact joined another app on the sandbox sender while the message was queued; have them send your join message again).\n- `not_found` (404): no such object for this app and mode. Check the ID's prefix and that it was made with a key of the same mode (test and live data are separate).\n- `idempotency_conflict` (409): the idempotency key was used for a different request (`channel_code` `body_mismatch`: use a new key), or that request is still running (`in_progress`: wait `retry_after` seconds and repeat it with the same key), or it already created a secret that is shown only once (`secret_not_kept`: creating a webhook endpoint, rotating its secret, connecting a WhatsApp number; a secret that was lost must be rotated again, and a WhatsApp number connected again to see its verify token).\n- `outside_window` (409, or in `message.failed`): the channel will not deliver outside its conversation window. On WhatsApp the 24-hour window is closed: send a template (`POST /v0/conversations/{conversation_id}/template`, or `POST /v0/messages` with `kind` `template` to start), or wait for the contact to write. On iMessage the contact has not messaged the line (or opted in) yet, or not recently, so the failure arrives as a `message.failed` event: wait for the contact to write, then reply in that conversation. Typing and read receipts answer `409 outside_window` directly when the channel's window for them is closed (iMessage typing works only within 5 minutes of the contact's last message; WhatsApp read receipts go at any time); ignore it and send your reply. On SMS, `channel_code` `quiet_hours`: the contact has not texted in the last hour and it is inside the sender's quiet hours in their time zone; retry after `retry_after` seconds, or wait for them to text. In `message.failed`: `window_closed` (WhatsApp's 24-hour window closed while the message was queued; send a template) and `queued_too_long` (the message waited in the queue longer than its sender allows, for example while the sender was paused; send it again if it still applies).\n- `unsupported_content` (422): the conversation's channel does not take this kind, field, block or value, and nothing was sent; Flow never converts a message. `param` names what was refused: `kind` (the endpoint's kind is not on this channel, such as a carousel on Telegram), a field or block (`reply_to` on SMS, `telegram` on WhatsApp, `telegram.is_big`), or the field whose value is not taken (`emoji` outside Telegram's reaction emoji, `buttons` for more than 3 WhatsApp buttons). `channel` is the conversation's channel and `hint` says what to send instead over plain HTTP; each endpoint's page lists its channels. In an iMessage group conversation, a reaction, typing or a read receipt answers it with `param` `kind` and `channel_code` `group_unsupported`: leave it out. Media to an SMS number without MMS answers it with `param` `media` and `channel_code` `no_mms`. An edit or unsend of an iMessage message that went by SMS or RCS (its `network`) answers it with `channel_code` `not_on_sms`: send a new message instead. An edit or unsend past the channel's time limit answers it with `channel_code` `edit_window_closed` or `unsend_window_closed` (iMessage: edits within 15 minutes of sending, unsends within 2 minutes; Telegram: unsends within 48 hours), and an iMessage edit of the message that started the conversation with `first_message_not_editable`; `message` states the limit and how long ago the message was sent. Send a new message instead. `POST /v0/files` answers it for a type Flow does not take (programs, web pages, unknown types). A send by `file_id` of a document a contact sent before Flow malware-scanned documents answers it too: Flow sends only documents it scanned.\n- `new_contact_limit` (429): the sender has used its budget for starting conversations. Wait `retry_after` seconds; replies into existing conversations still go.\n- `sender_throttled` (429): abuse signals tripped (the same text to many new contacts, many starts with no reply, blocks), so the sender may not start conversations until `retry_after`; replies into existing conversations still go. Personalise first messages and start only conversations people expect.\n- `file_blocked` (422): the file failed the malware scan and was not stored. Send a different file. (When the scan cannot run at all, the upload answers a retryable `500 api_error` with `Retry-After` instead.)\n- `rate_limited` (429): either too many requests for this key (the per-key request limit, reported in the `RateLimit-*` headers) or sends faster than the sender's sending rate (pacing). Wait `Retry-After` (`retry_after`) seconds and retry with the same `Idempotency-Key`.\n- `channel_error` (502, or in `message.failed`): the channel refused or failed the message, or timed out; `channel_code` carries its own code. Read `message`, change what the channel objected to, and send again. From typing and read receipts, which call the channel at once, it is safe to ignore. SMS codes are Flow's, the same whichever carrier network carries the message: `carrier_filtered` (a carrier's spam filter blocked it: make it read like a person's message, no shortened links), `unreachable`, `invalid_number`, `landline`, `registration_required` (the number's 10DLC or toll-free registration is not approved yet), `carrier_error`, and `provider_not_configured` (the SMS sender is not fully set up on Flow's side, so nothing was sent; contact the Flow team). Flow's own codes on every channel: `channel_timeout_unknown_outcome` (in `message.failed`: the channel did not answer after the message was sent, and the resends its policy allows did not settle it, so the message may have arrived or not; check with the contact before sending it again) and `unknown_outcome` (from typing and read receipts: the call timed out after it was sent).\n- `not_implemented` (501): this endpoint or channel is not live yet during the beta. Use a channel that is live (Telegram, iMessage), or check the changelog.\n- `api_error` (500, 503): something went wrong on Flow's side. Retry with the same idempotency key after `retry_after` seconds, and quote `request_id` if it persists.\n","enum":["invalid_request","authentication","permission","not_found","idempotency_conflict","outside_window","unsupported_content","new_contact_limit","sender_throttled","file_blocked","rate_limited","channel_error","not_implemented","api_error"],"isRequired":true,"uniqueKey":"error.type","typeLabel":"enum\u003cstring>"},"message":{"type":"string","description":"A sentence for a developer saying what went wrong. Do not parse it; switch on `type`.","isRequired":true,"uniqueKey":"error.message","typeLabel":"string"},"hint":{"type":"string","description":"One sentence saying what to change to make the request succeed, specific\nto this case, for example \"Send a template instead: POST\n/v0/conversations/{conversation_id}/template.\" For `unsupported_content`\nit says what to send instead over plain HTTP. Written for developers and\ncoding agents alike. Do not parse it; it may be reworded at any time.\n","examples":["Send a template instead: POST /v0/conversations/{conversation_id}/template."],"isRequired":true,"uniqueKey":"error.hint","typeLabel":"string"},"doc_url":{"type":"string","format":"uri","description":"The documentation page for this error's type, `https://api.flow.engineer/docs/errors/\u003ctype>`. It says what the error means, why it happens and how to fix it, with code.","examples":["https://api.flow.engineer/docs/errors/outside_window"],"isRequired":true,"uniqueKey":"error.doc_url","typeLabel":"string\u003curi>"},"param":{"type":"string","description":"The request parameter the error is about, as a dotted path (`kind`, `telegram.is_big`, `buttons`, `cards[1].header.type`, `limit`).","uniqueKey":"error.param","typeLabel":"string"},"retry_after":{"type":"integer","minimum":0,"description":"Seconds to wait before retrying, for errors that clear by themselves.","uniqueKey":"error.retry_after","typeLabel":"integer"},"channel":{"type":"string","description":"The conversation's channel, on errors about what it takes (`unsupported_content`) and its windows (`outside_window`).","enum":["telegram","whatsapp","imessage","sms"],"uniqueKey":"error.channel","typeLabel":"enum\u003cstring>"},"conversation":{"type":"string","description":"A conversation ID, `conv_` and a ULID.","pattern":"^conv_[0-9A-HJKMNP-TV-Z]{26}$","examples":["conv_01JB8ZC3K5M7P9R1T3V5X7Z9B1"],"uniqueKey":"error.conversation","typeLabel":"string"},"sender":{"type":"string","description":"A sender ID, `snd_` and a ULID.","pattern":"^snd_[0-9A-HJKMNP-TV-Z]{26}$","examples":["snd_01JB8Z4Q3V6W0R2N7C5H1M9K4T"],"uniqueKey":"error.sender","typeLabel":"string"},"channel_code":{"type":"string","description":"For `channel_error`, the channel's own error code, as given by the\nchannel. For some other errors, Flow's own code naming the case, for\nexample `sandbox_allowance_used` (`permission`: the sandbox allowance is\nused up), `sandbox_contact_limit` (`permission`: the allowance has no\nroom for another contact), `sandbox_channel_not_included` (`permission`:\nthe allowance does not cover this sandbox channel), `sign_in_required`\n(`permission`: an app made without an account cannot do this) or\n`sandbox_key_expired` (`authentication`: a key from `POST\n/v0/sandbox/keys` passed its `expires_at`; `permission`, for a send\nthat carries no key, such as a reply in a webhook answer, from such an\napp). For `idempotency_conflict`, always one of `body_mismatch` (the\nkey was used for a different request), `in_progress` (the first\nrequest is still running; `retry_after` says when to repeat it) or\n`secret_not_kept` (its answer carried a secret shown only once).\nSMS: `contact_opted_out` and `consent_required` (`permission`),\n`quiet_hours` (`outside_window`, with `retry_after`), and\n`carrier_filtered`, `unreachable`, `invalid_number`, `landline`,\n`registration_required`, `carrier_error` and `provider_not_configured`\n(`channel_error`). HIPAA accounts: `hipaa_channel_not_covered`\n(`permission`). Connecting your own WhatsApp number (`POST\n/v0/senders`): `whatsapp_token_malformed`, `whatsapp_token_rejected`,\n`whatsapp_app_secret_malformed` and `whatsapp_number_not_in_account`\n(`invalid_request`), and `whatsapp_number_taken`,\n`whatsapp_account_taken` and `whatsapp_number_managed` (`permission`).\nSimulated conversations (`permission`): `test_mode_only`,\n`not_simulated`, `simulated_contact` and `simulated_conversation_limit`.\nFor a message that failed after it was queued (`message.failed`):\n`sandbox_contact_left` (`permission`), `window_closed` and\n`queued_too_long` (`outside_window`), and\n`channel_timeout_unknown_outcome` (`channel_error`: the channel never\nconfirmed the send, so it may or may not have arrived). iMessage\n(`unsupported_content`): `group_unsupported` (a reaction, typing or a\nread receipt in a group conversation; `param` `kind`) and\n`not_on_sms` (an edit or unsend of a message that went by SMS or RCS;\n`param` `kind`). SMS (`unsupported_content`): `no_mms` (media to a\nnumber without MMS; `param` `media`).\nEdits and unsends (`unsupported_content`): `edit_window_closed` and\n`unsend_window_closed` (the message is older than the channel allows\nfor the change) and `first_message_not_editable` (iMessage: the message\nthat started the conversation). From typing and\nread receipts, `unknown_outcome` (`channel_error`: the call timed out after\nit was sent).\n","uniqueKey":"error.channel_code","typeLabel":"string"},"request_id":{"type":"string","description":"Flow's ID for this request. Quote it when asking for help.","uniqueKey":"error.request_id","typeLabel":"string"}},"isRequired":true,"uniqueKey":"error","typeLabel":"object"}},"uniqueKey":"","typeLabel":"object"},"example":{"error":{"type":"invalid_request","message":"limit must be between 1 and 100.","hint":"Pass limit between 1 and 100 (default 20), and page with after or before.","doc_url":"https://api.flow.engineer/docs/errors/invalid_request","param":"limit"}},"examples":{"Example":{"title":"Example","value":{"error":{"type":"invalid_request","message":"limit must be between 1 and 100.","hint":"Pass limit between 1 and 100 (default 20), and page with after or before.","doc_url":"https://api.flow.engineer/docs/errors/invalid_request","param":"limit"}}}},"exampleType":"examples"}},"exampleType":"examples"},"a12abdeb-0c7b-4698-a59f-588a91bda27c":{"description":"The API key is missing, malformed, unknown, revoked or expired (`authentication`).","headers":{"WWW-Authenticate":{"description":"The authentication scheme, `Bearer realm=\"api.flow.engineer\"`. When the\nrequest carried a key that is well formed but unknown, revoked or\nexpired, it also says `error=\"invalid_token\"`:\n`Bearer realm=\"api.flow.engineer\", error=\"invalid_token\"`. A missing or\nmalformed key gets the scheme alone.\n","schema":{"type":"string","examples":["Bearer realm=\"api.flow.engineer\", error=\"invalid_token\""]}}},"content":{"application/json":{"schema":{"type":"object","description":"The body of every error answer.","required":["error"],"properties":{"error":{"type":"object","description":"The details of an error.","required":["type","message","hint","doc_url"],"properties":{"type":{"type":"string","description":"What went wrong. A closed list: new types arrive only with a new API version.\nEach type has a page at `https://api.flow.engineer/docs/errors/\u003ctype>`, given in\nthe error's `doc_url`; the error's `hint` says what to change for the case.\n\n- `invalid_request` (400, or 413 for a request body over the size limit): the request is malformed or a parameter is invalid. Fix the parameter named in `param`; the `hint` says what it must look like. Every endpoint answers it for a `Flow-Version` header that is not a version this API serves.\n- `authentication` (401): the API key is missing, malformed, unknown, revoked or expired. An `Authorization: Bearer` header with nothing after it usually means the environment variable holding the key was empty when the header was built (for an MCP client, when the server was added): the `hint` says so. Send `Authorization: Bearer fk_test_...` or `fk_live_...` with a current key; with no key at all, get a test key with `POST /v0/sandbox/keys`. A sandbox key past its `expires_at` has `channel_code` `sandbox_key_expired`: sign in to claim the app, or get a new key.\n- `permission` (403): the key may not do this, for example a test key using a live sender, a sandbox contact who joined another app, a new conversation from an iMessage line that may only reply, or a send past the app's sandbox allowance (`channel_code` `sandbox_allowance_used`, `sandbox_contact_limit` or `sandbox_channel_not_included`). Use the key of the right mode, have the contact send your join message, wait for the contact to message the line first, or sign in through the device flow to lift the allowance. On SMS: `contact_opted_out` (the contact texted STOP; nothing more goes to them until they text START), `consent_required` (a start to someone who never texted your numbers: record their consent with `consent` on the start). In a HIPAA account: `hipaa_channel_not_covered` (the channel has no business associate agreement; use SMS). Simulated conversations: `simulated_contact` (a real sender was pointed at a simulated contact; reply in the contact's simulated conversation), `not_simulated` (a simulated sender or test endpoint was pointed at a real contact), `test_mode_only`, `simulated_conversation_limit`. In `message.failed`: `sandbox_contact_left` (the contact joined another app on the sandbox sender while the message was queued; have them send your join message again).\n- `not_found` (404): no such object for this app and mode. Check the ID's prefix and that it was made with a key of the same mode (test and live data are separate).\n- `idempotency_conflict` (409): the idempotency key was used for a different request (`channel_code` `body_mismatch`: use a new key), or that request is still running (`in_progress`: wait `retry_after` seconds and repeat it with the same key), or it already created a secret that is shown only once (`secret_not_kept`: creating a webhook endpoint, rotating its secret, connecting a WhatsApp number; a secret that was lost must be rotated again, and a WhatsApp number connected again to see its verify token).\n- `outside_window` (409, or in `message.failed`): the channel will not deliver outside its conversation window. On WhatsApp the 24-hour window is closed: send a template (`POST /v0/conversations/{conversation_id}/template`, or `POST /v0/messages` with `kind` `template` to start), or wait for the contact to write. On iMessage the contact has not messaged the line (or opted in) yet, or not recently, so the failure arrives as a `message.failed` event: wait for the contact to write, then reply in that conversation. Typing and read receipts answer `409 outside_window` directly when the channel's window for them is closed (iMessage typing works only within 5 minutes of the contact's last message; WhatsApp read receipts go at any time); ignore it and send your reply. On SMS, `channel_code` `quiet_hours`: the contact has not texted in the last hour and it is inside the sender's quiet hours in their time zone; retry after `retry_after` seconds, or wait for them to text. In `message.failed`: `window_closed` (WhatsApp's 24-hour window closed while the message was queued; send a template) and `queued_too_long` (the message waited in the queue longer than its sender allows, for example while the sender was paused; send it again if it still applies).\n- `unsupported_content` (422): the conversation's channel does not take this kind, field, block or value, and nothing was sent; Flow never converts a message. `param` names what was refused: `kind` (the endpoint's kind is not on this channel, such as a carousel on Telegram), a field or block (`reply_to` on SMS, `telegram` on WhatsApp, `telegram.is_big`), or the field whose value is not taken (`emoji` outside Telegram's reaction emoji, `buttons` for more than 3 WhatsApp buttons). `channel` is the conversation's channel and `hint` says what to send instead over plain HTTP; each endpoint's page lists its channels. In an iMessage group conversation, a reaction, typing or a read receipt answers it with `param` `kind` and `channel_code` `group_unsupported`: leave it out. Media to an SMS number without MMS answers it with `param` `media` and `channel_code` `no_mms`. An edit or unsend of an iMessage message that went by SMS or RCS (its `network`) answers it with `channel_code` `not_on_sms`: send a new message instead. An edit or unsend past the channel's time limit answers it with `channel_code` `edit_window_closed` or `unsend_window_closed` (iMessage: edits within 15 minutes of sending, unsends within 2 minutes; Telegram: unsends within 48 hours), and an iMessage edit of the message that started the conversation with `first_message_not_editable`; `message` states the limit and how long ago the message was sent. Send a new message instead. `POST /v0/files` answers it for a type Flow does not take (programs, web pages, unknown types). A send by `file_id` of a document a contact sent before Flow malware-scanned documents answers it too: Flow sends only documents it scanned.\n- `new_contact_limit` (429): the sender has used its budget for starting conversations. Wait `retry_after` seconds; replies into existing conversations still go.\n- `sender_throttled` (429): abuse signals tripped (the same text to many new contacts, many starts with no reply, blocks), so the sender may not start conversations until `retry_after`; replies into existing conversations still go. Personalise first messages and start only conversations people expect.\n- `file_blocked` (422): the file failed the malware scan and was not stored. Send a different file. (When the scan cannot run at all, the upload answers a retryable `500 api_error` with `Retry-After` instead.)\n- `rate_limited` (429): either too many requests for this key (the per-key request limit, reported in the `RateLimit-*` headers) or sends faster than the sender's sending rate (pacing). Wait `Retry-After` (`retry_after`) seconds and retry with the same `Idempotency-Key`.\n- `channel_error` (502, or in `message.failed`): the channel refused or failed the message, or timed out; `channel_code` carries its own code. Read `message`, change what the channel objected to, and send again. From typing and read receipts, which call the channel at once, it is safe to ignore. SMS codes are Flow's, the same whichever carrier network carries the message: `carrier_filtered` (a carrier's spam filter blocked it: make it read like a person's message, no shortened links), `unreachable`, `invalid_number`, `landline`, `registration_required` (the number's 10DLC or toll-free registration is not approved yet), `carrier_error`, and `provider_not_configured` (the SMS sender is not fully set up on Flow's side, so nothing was sent; contact the Flow team). Flow's own codes on every channel: `channel_timeout_unknown_outcome` (in `message.failed`: the channel did not answer after the message was sent, and the resends its policy allows did not settle it, so the message may have arrived or not; check with the contact before sending it again) and `unknown_outcome` (from typing and read receipts: the call timed out after it was sent).\n- `not_implemented` (501): this endpoint or channel is not live yet during the beta. Use a channel that is live (Telegram, iMessage), or check the changelog.\n- `api_error` (500, 503): something went wrong on Flow's side. Retry with the same idempotency key after `retry_after` seconds, and quote `request_id` if it persists.\n","enum":["invalid_request","authentication","permission","not_found","idempotency_conflict","outside_window","unsupported_content","new_contact_limit","sender_throttled","file_blocked","rate_limited","channel_error","not_implemented","api_error"],"isRequired":true,"uniqueKey":"error.type","typeLabel":"enum\u003cstring>"},"message":{"type":"string","description":"A sentence for a developer saying what went wrong. Do not parse it; switch on `type`.","isRequired":true,"uniqueKey":"error.message","typeLabel":"string"},"hint":{"type":"string","description":"One sentence saying what to change to make the request succeed, specific\nto this case, for example \"Send a template instead: POST\n/v0/conversations/{conversation_id}/template.\" For `unsupported_content`\nit says what to send instead over plain HTTP. Written for developers and\ncoding agents alike. Do not parse it; it may be reworded at any time.\n","examples":["Send a template instead: POST /v0/conversations/{conversation_id}/template."],"isRequired":true,"uniqueKey":"error.hint","typeLabel":"string"},"doc_url":{"type":"string","format":"uri","description":"The documentation page for this error's type, `https://api.flow.engineer/docs/errors/\u003ctype>`. It says what the error means, why it happens and how to fix it, with code.","examples":["https://api.flow.engineer/docs/errors/outside_window"],"isRequired":true,"uniqueKey":"error.doc_url","typeLabel":"string\u003curi>"},"param":{"type":"string","description":"The request parameter the error is about, as a dotted path (`kind`, `telegram.is_big`, `buttons`, `cards[1].header.type`, `limit`).","uniqueKey":"error.param","typeLabel":"string"},"retry_after":{"type":"integer","minimum":0,"description":"Seconds to wait before retrying, for errors that clear by themselves.","uniqueKey":"error.retry_after","typeLabel":"integer"},"channel":{"type":"string","description":"The conversation's channel, on errors about what it takes (`unsupported_content`) and its windows (`outside_window`).","enum":["telegram","whatsapp","imessage","sms"],"uniqueKey":"error.channel","typeLabel":"enum\u003cstring>"},"conversation":{"type":"string","description":"A conversation ID, `conv_` and a ULID.","pattern":"^conv_[0-9A-HJKMNP-TV-Z]{26}$","examples":["conv_01JB8ZC3K5M7P9R1T3V5X7Z9B1"],"uniqueKey":"error.conversation","typeLabel":"string"},"sender":{"type":"string","description":"A sender ID, `snd_` and a ULID.","pattern":"^snd_[0-9A-HJKMNP-TV-Z]{26}$","examples":["snd_01JB8Z4Q3V6W0R2N7C5H1M9K4T"],"uniqueKey":"error.sender","typeLabel":"string"},"channel_code":{"type":"string","description":"For `channel_error`, the channel's own error code, as given by the\nchannel. For some other errors, Flow's own code naming the case, for\nexample `sandbox_allowance_used` (`permission`: the sandbox allowance is\nused up), `sandbox_contact_limit` (`permission`: the allowance has no\nroom for another contact), `sandbox_channel_not_included` (`permission`:\nthe allowance does not cover this sandbox channel), `sign_in_required`\n(`permission`: an app made without an account cannot do this) or\n`sandbox_key_expired` (`authentication`: a key from `POST\n/v0/sandbox/keys` passed its `expires_at`; `permission`, for a send\nthat carries no key, such as a reply in a webhook answer, from such an\napp). For `idempotency_conflict`, always one of `body_mismatch` (the\nkey was used for a different request), `in_progress` (the first\nrequest is still running; `retry_after` says when to repeat it) or\n`secret_not_kept` (its answer carried a secret shown only once).\nSMS: `contact_opted_out` and `consent_required` (`permission`),\n`quiet_hours` (`outside_window`, with `retry_after`), and\n`carrier_filtered`, `unreachable`, `invalid_number`, `landline`,\n`registration_required`, `carrier_error` and `provider_not_configured`\n(`channel_error`). HIPAA accounts: `hipaa_channel_not_covered`\n(`permission`). Connecting your own WhatsApp number (`POST\n/v0/senders`): `whatsapp_token_malformed`, `whatsapp_token_rejected`,\n`whatsapp_app_secret_malformed` and `whatsapp_number_not_in_account`\n(`invalid_request`), and `whatsapp_number_taken`,\n`whatsapp_account_taken` and `whatsapp_number_managed` (`permission`).\nSimulated conversations (`permission`): `test_mode_only`,\n`not_simulated`, `simulated_contact` and `simulated_conversation_limit`.\nFor a message that failed after it was queued (`message.failed`):\n`sandbox_contact_left` (`permission`), `window_closed` and\n`queued_too_long` (`outside_window`), and\n`channel_timeout_unknown_outcome` (`channel_error`: the channel never\nconfirmed the send, so it may or may not have arrived). iMessage\n(`unsupported_content`): `group_unsupported` (a reaction, typing or a\nread receipt in a group conversation; `param` `kind`) and\n`not_on_sms` (an edit or unsend of a message that went by SMS or RCS;\n`param` `kind`). SMS (`unsupported_content`): `no_mms` (media to a\nnumber without MMS; `param` `media`).\nEdits and unsends (`unsupported_content`): `edit_window_closed` and\n`unsend_window_closed` (the message is older than the channel allows\nfor the change) and `first_message_not_editable` (iMessage: the message\nthat started the conversation). From typing and\nread receipts, `unknown_outcome` (`channel_error`: the call timed out after\nit was sent).\n","uniqueKey":"error.channel_code","typeLabel":"string"},"request_id":{"type":"string","description":"Flow's ID for this request. Quote it when asking for help.","uniqueKey":"error.request_id","typeLabel":"string"}},"isRequired":true,"uniqueKey":"error","typeLabel":"object"}},"uniqueKey":"","typeLabel":"object"},"example":{"error":{"type":"authentication","message":"No valid API key was given.","hint":"Send the header Authorization: Bearer fk_test_... (or fk_live_...); no key yet? Get a test key with curl -X POST https://api.flow.engineer/v0/sandbox/keys","doc_url":"https://api.flow.engineer/docs/errors/authentication"}},"examples":{"Example":{"title":"Example","value":{"error":{"type":"authentication","message":"No valid API key was given.","hint":"Send the header Authorization: Bearer fk_test_... (or fk_live_...); no key yet? Get a test key with curl -X POST https://api.flow.engineer/v0/sandbox/keys","doc_url":"https://api.flow.engineer/docs/errors/authentication"}}}},"exampleType":"examples"}},"exampleType":"examples"},"da1c6377-5cf4-4dad-bd0b-1541aece5ce9":{"description":"`permission`: a live key (`channel_code` `test_mode_only`), a\nconversation that is not simulated (`not_simulated`), or, in a HIPAA\naccount, a simulated conversation on a channel its business associate\nagreement does not cover (`hipaa_channel_not_covered`).\n","content":{"application/json":{"schema":{"type":"object","description":"The body of every error answer.","required":["error"],"properties":{"error":{"type":"object","description":"The details of an error.","required":["type","message","hint","doc_url"],"properties":{"type":{"type":"string","description":"What went wrong. A closed list: new types arrive only with a new API version.\nEach type has a page at `https://api.flow.engineer/docs/errors/\u003ctype>`, given in\nthe error's `doc_url`; the error's `hint` says what to change for the case.\n\n- `invalid_request` (400, or 413 for a request body over the size limit): the request is malformed or a parameter is invalid. Fix the parameter named in `param`; the `hint` says what it must look like. Every endpoint answers it for a `Flow-Version` header that is not a version this API serves.\n- `authentication` (401): the API key is missing, malformed, unknown, revoked or expired. An `Authorization: Bearer` header with nothing after it usually means the environment variable holding the key was empty when the header was built (for an MCP client, when the server was added): the `hint` says so. Send `Authorization: Bearer fk_test_...` or `fk_live_...` with a current key; with no key at all, get a test key with `POST /v0/sandbox/keys`. A sandbox key past its `expires_at` has `channel_code` `sandbox_key_expired`: sign in to claim the app, or get a new key.\n- `permission` (403): the key may not do this, for example a test key using a live sender, a sandbox contact who joined another app, a new conversation from an iMessage line that may only reply, or a send past the app's sandbox allowance (`channel_code` `sandbox_allowance_used`, `sandbox_contact_limit` or `sandbox_channel_not_included`). Use the key of the right mode, have the contact send your join message, wait for the contact to message the line first, or sign in through the device flow to lift the allowance. On SMS: `contact_opted_out` (the contact texted STOP; nothing more goes to them until they text START), `consent_required` (a start to someone who never texted your numbers: record their consent with `consent` on the start). In a HIPAA account: `hipaa_channel_not_covered` (the channel has no business associate agreement; use SMS). Simulated conversations: `simulated_contact` (a real sender was pointed at a simulated contact; reply in the contact's simulated conversation), `not_simulated` (a simulated sender or test endpoint was pointed at a real contact), `test_mode_only`, `simulated_conversation_limit`. In `message.failed`: `sandbox_contact_left` (the contact joined another app on the sandbox sender while the message was queued; have them send your join message again).\n- `not_found` (404): no such object for this app and mode. Check the ID's prefix and that it was made with a key of the same mode (test and live data are separate).\n- `idempotency_conflict` (409): the idempotency key was used for a different request (`channel_code` `body_mismatch`: use a new key), or that request is still running (`in_progress`: wait `retry_after` seconds and repeat it with the same key), or it already created a secret that is shown only once (`secret_not_kept`: creating a webhook endpoint, rotating its secret, connecting a WhatsApp number; a secret that was lost must be rotated again, and a WhatsApp number connected again to see its verify token).\n- `outside_window` (409, or in `message.failed`): the channel will not deliver outside its conversation window. On WhatsApp the 24-hour window is closed: send a template (`POST /v0/conversations/{conversation_id}/template`, or `POST /v0/messages` with `kind` `template` to start), or wait for the contact to write. On iMessage the contact has not messaged the line (or opted in) yet, or not recently, so the failure arrives as a `message.failed` event: wait for the contact to write, then reply in that conversation. Typing and read receipts answer `409 outside_window` directly when the channel's window for them is closed (iMessage typing works only within 5 minutes of the contact's last message; WhatsApp read receipts go at any time); ignore it and send your reply. On SMS, `channel_code` `quiet_hours`: the contact has not texted in the last hour and it is inside the sender's quiet hours in their time zone; retry after `retry_after` seconds, or wait for them to text. In `message.failed`: `window_closed` (WhatsApp's 24-hour window closed while the message was queued; send a template) and `queued_too_long` (the message waited in the queue longer than its sender allows, for example while the sender was paused; send it again if it still applies).\n- `unsupported_content` (422): the conversation's channel does not take this kind, field, block or value, and nothing was sent; Flow never converts a message. `param` names what was refused: `kind` (the endpoint's kind is not on this channel, such as a carousel on Telegram), a field or block (`reply_to` on SMS, `telegram` on WhatsApp, `telegram.is_big`), or the field whose value is not taken (`emoji` outside Telegram's reaction emoji, `buttons` for more than 3 WhatsApp buttons). `channel` is the conversation's channel and `hint` says what to send instead over plain HTTP; each endpoint's page lists its channels. In an iMessage group conversation, a reaction, typing or a read receipt answers it with `param` `kind` and `channel_code` `group_unsupported`: leave it out. Media to an SMS number without MMS answers it with `param` `media` and `channel_code` `no_mms`. An edit or unsend of an iMessage message that went by SMS or RCS (its `network`) answers it with `channel_code` `not_on_sms`: send a new message instead. An edit or unsend past the channel's time limit answers it with `channel_code` `edit_window_closed` or `unsend_window_closed` (iMessage: edits within 15 minutes of sending, unsends within 2 minutes; Telegram: unsends within 48 hours), and an iMessage edit of the message that started the conversation with `first_message_not_editable`; `message` states the limit and how long ago the message was sent. Send a new message instead. `POST /v0/files` answers it for a type Flow does not take (programs, web pages, unknown types). A send by `file_id` of a document a contact sent before Flow malware-scanned documents answers it too: Flow sends only documents it scanned.\n- `new_contact_limit` (429): the sender has used its budget for starting conversations. Wait `retry_after` seconds; replies into existing conversations still go.\n- `sender_throttled` (429): abuse signals tripped (the same text to many new contacts, many starts with no reply, blocks), so the sender may not start conversations until `retry_after`; replies into existing conversations still go. Personalise first messages and start only conversations people expect.\n- `file_blocked` (422): the file failed the malware scan and was not stored. Send a different file. (When the scan cannot run at all, the upload answers a retryable `500 api_error` with `Retry-After` instead.)\n- `rate_limited` (429): either too many requests for this key (the per-key request limit, reported in the `RateLimit-*` headers) or sends faster than the sender's sending rate (pacing). Wait `Retry-After` (`retry_after`) seconds and retry with the same `Idempotency-Key`.\n- `channel_error` (502, or in `message.failed`): the channel refused or failed the message, or timed out; `channel_code` carries its own code. Read `message`, change what the channel objected to, and send again. From typing and read receipts, which call the channel at once, it is safe to ignore. SMS codes are Flow's, the same whichever carrier network carries the message: `carrier_filtered` (a carrier's spam filter blocked it: make it read like a person's message, no shortened links), `unreachable`, `invalid_number`, `landline`, `registration_required` (the number's 10DLC or toll-free registration is not approved yet), `carrier_error`, and `provider_not_configured` (the SMS sender is not fully set up on Flow's side, so nothing was sent; contact the Flow team). Flow's own codes on every channel: `channel_timeout_unknown_outcome` (in `message.failed`: the channel did not answer after the message was sent, and the resends its policy allows did not settle it, so the message may have arrived or not; check with the contact before sending it again) and `unknown_outcome` (from typing and read receipts: the call timed out after it was sent).\n- `not_implemented` (501): this endpoint or channel is not live yet during the beta. Use a channel that is live (Telegram, iMessage), or check the changelog.\n- `api_error` (500, 503): something went wrong on Flow's side. Retry with the same idempotency key after `retry_after` seconds, and quote `request_id` if it persists.\n","enum":["invalid_request","authentication","permission","not_found","idempotency_conflict","outside_window","unsupported_content","new_contact_limit","sender_throttled","file_blocked","rate_limited","channel_error","not_implemented","api_error"],"isRequired":true,"uniqueKey":"error.type","typeLabel":"enum\u003cstring>"},"message":{"type":"string","description":"A sentence for a developer saying what went wrong. Do not parse it; switch on `type`.","isRequired":true,"uniqueKey":"error.message","typeLabel":"string"},"hint":{"type":"string","description":"One sentence saying what to change to make the request succeed, specific\nto this case, for example \"Send a template instead: POST\n/v0/conversations/{conversation_id}/template.\" For `unsupported_content`\nit says what to send instead over plain HTTP. Written for developers and\ncoding agents alike. Do not parse it; it may be reworded at any time.\n","examples":["Send a template instead: POST /v0/conversations/{conversation_id}/template."],"isRequired":true,"uniqueKey":"error.hint","typeLabel":"string"},"doc_url":{"type":"string","format":"uri","description":"The documentation page for this error's type, `https://api.flow.engineer/docs/errors/\u003ctype>`. It says what the error means, why it happens and how to fix it, with code.","examples":["https://api.flow.engineer/docs/errors/outside_window"],"isRequired":true,"uniqueKey":"error.doc_url","typeLabel":"string\u003curi>"},"param":{"type":"string","description":"The request parameter the error is about, as a dotted path (`kind`, `telegram.is_big`, `buttons`, `cards[1].header.type`, `limit`).","uniqueKey":"error.param","typeLabel":"string"},"retry_after":{"type":"integer","minimum":0,"description":"Seconds to wait before retrying, for errors that clear by themselves.","uniqueKey":"error.retry_after","typeLabel":"integer"},"channel":{"type":"string","description":"The conversation's channel, on errors about what it takes (`unsupported_content`) and its windows (`outside_window`).","enum":["telegram","whatsapp","imessage","sms"],"uniqueKey":"error.channel","typeLabel":"enum\u003cstring>"},"conversation":{"type":"string","description":"A conversation ID, `conv_` and a ULID.","pattern":"^conv_[0-9A-HJKMNP-TV-Z]{26}$","examples":["conv_01JB8ZC3K5M7P9R1T3V5X7Z9B1"],"uniqueKey":"error.conversation","typeLabel":"string"},"sender":{"type":"string","description":"A sender ID, `snd_` and a ULID.","pattern":"^snd_[0-9A-HJKMNP-TV-Z]{26}$","examples":["snd_01JB8Z4Q3V6W0R2N7C5H1M9K4T"],"uniqueKey":"error.sender","typeLabel":"string"},"channel_code":{"type":"string","description":"For `channel_error`, the channel's own error code, as given by the\nchannel. For some other errors, Flow's own code naming the case, for\nexample `sandbox_allowance_used` (`permission`: the sandbox allowance is\nused up), `sandbox_contact_limit` (`permission`: the allowance has no\nroom for another contact), `sandbox_channel_not_included` (`permission`:\nthe allowance does not cover this sandbox channel), `sign_in_required`\n(`permission`: an app made without an account cannot do this) or\n`sandbox_key_expired` (`authentication`: a key from `POST\n/v0/sandbox/keys` passed its `expires_at`; `permission`, for a send\nthat carries no key, such as a reply in a webhook answer, from such an\napp). For `idempotency_conflict`, always one of `body_mismatch` (the\nkey was used for a different request), `in_progress` (the first\nrequest is still running; `retry_after` says when to repeat it) or\n`secret_not_kept` (its answer carried a secret shown only once).\nSMS: `contact_opted_out` and `consent_required` (`permission`),\n`quiet_hours` (`outside_window`, with `retry_after`), and\n`carrier_filtered`, `unreachable`, `invalid_number`, `landline`,\n`registration_required`, `carrier_error` and `provider_not_configured`\n(`channel_error`). HIPAA accounts: `hipaa_channel_not_covered`\n(`permission`). Connecting your own WhatsApp number (`POST\n/v0/senders`): `whatsapp_token_malformed`, `whatsapp_token_rejected`,\n`whatsapp_app_secret_malformed` and `whatsapp_number_not_in_account`\n(`invalid_request`), and `whatsapp_number_taken`,\n`whatsapp_account_taken` and `whatsapp_number_managed` (`permission`).\nSimulated conversations (`permission`): `test_mode_only`,\n`not_simulated`, `simulated_contact` and `simulated_conversation_limit`.\nFor a message that failed after it was queued (`message.failed`):\n`sandbox_contact_left` (`permission`), `window_closed` and\n`queued_too_long` (`outside_window`), and\n`channel_timeout_unknown_outcome` (`channel_error`: the channel never\nconfirmed the send, so it may or may not have arrived). iMessage\n(`unsupported_content`): `group_unsupported` (a reaction, typing or a\nread receipt in a group conversation; `param` `kind`) and\n`not_on_sms` (an edit or unsend of a message that went by SMS or RCS;\n`param` `kind`). SMS (`unsupported_content`): `no_mms` (media to a\nnumber without MMS; `param` `media`).\nEdits and unsends (`unsupported_content`): `edit_window_closed` and\n`unsend_window_closed` (the message is older than the channel allows\nfor the change) and `first_message_not_editable` (iMessage: the message\nthat started the conversation). From typing and\nread receipts, `unknown_outcome` (`channel_error`: the call timed out after\nit was sent).\n","uniqueKey":"error.channel_code","typeLabel":"string"},"request_id":{"type":"string","description":"Flow's ID for this request. Quote it when asking for help.","uniqueKey":"error.request_id","typeLabel":"string"}},"isRequired":true,"uniqueKey":"error","typeLabel":"object"}},"uniqueKey":"","typeLabel":"object"},"example":{"error":{"type":"permission","message":"This account handles health information under a business associate agreement, and Telegram is not a channel Flow holds such an agreement for, so it cannot be simulated here either.","hint":"Simulate an SMS conversation instead: POST /v0/test/conversations with {\"channel\":\"sms\"}.","doc_url":"https://api.flow.engineer/docs/errors/permission","param":"conversation_id","channel_code":"hipaa_channel_not_covered"}},"examples":{"Example":{"title":"Example","value":{"error":{"type":"permission","message":"This account handles health information under a business associate agreement, and Telegram is not a channel Flow holds such an agreement for, so it cannot be simulated here either.","hint":"Simulate an SMS conversation instead: POST /v0/test/conversations with {\"channel\":\"sms\"}.","doc_url":"https://api.flow.engineer/docs/errors/permission","param":"conversation_id","channel_code":"hipaa_channel_not_covered"}}}},"exampleType":"examples"}},"exampleType":"examples"},"d69637de-addf-40b0-afef-cc84f7ab92f6":{"description":"No such object for this app and mode (`not_found`).","content":{"application/json":{"schema":{"type":"object","description":"The body of every error answer.","required":["error"],"properties":{"error":{"type":"object","description":"The details of an error.","required":["type","message","hint","doc_url"],"properties":{"type":{"type":"string","description":"What went wrong. A closed list: new types arrive only with a new API version.\nEach type has a page at `https://api.flow.engineer/docs/errors/\u003ctype>`, given in\nthe error's `doc_url`; the error's `hint` says what to change for the case.\n\n- `invalid_request` (400, or 413 for a request body over the size limit): the request is malformed or a parameter is invalid. Fix the parameter named in `param`; the `hint` says what it must look like. Every endpoint answers it for a `Flow-Version` header that is not a version this API serves.\n- `authentication` (401): the API key is missing, malformed, unknown, revoked or expired. An `Authorization: Bearer` header with nothing after it usually means the environment variable holding the key was empty when the header was built (for an MCP client, when the server was added): the `hint` says so. Send `Authorization: Bearer fk_test_...` or `fk_live_...` with a current key; with no key at all, get a test key with `POST /v0/sandbox/keys`. A sandbox key past its `expires_at` has `channel_code` `sandbox_key_expired`: sign in to claim the app, or get a new key.\n- `permission` (403): the key may not do this, for example a test key using a live sender, a sandbox contact who joined another app, a new conversation from an iMessage line that may only reply, or a send past the app's sandbox allowance (`channel_code` `sandbox_allowance_used`, `sandbox_contact_limit` or `sandbox_channel_not_included`). Use the key of the right mode, have the contact send your join message, wait for the contact to message the line first, or sign in through the device flow to lift the allowance. On SMS: `contact_opted_out` (the contact texted STOP; nothing more goes to them until they text START), `consent_required` (a start to someone who never texted your numbers: record their consent with `consent` on the start). In a HIPAA account: `hipaa_channel_not_covered` (the channel has no business associate agreement; use SMS). Simulated conversations: `simulated_contact` (a real sender was pointed at a simulated contact; reply in the contact's simulated conversation), `not_simulated` (a simulated sender or test endpoint was pointed at a real contact), `test_mode_only`, `simulated_conversation_limit`. In `message.failed`: `sandbox_contact_left` (the contact joined another app on the sandbox sender while the message was queued; have them send your join message again).\n- `not_found` (404): no such object for this app and mode. Check the ID's prefix and that it was made with a key of the same mode (test and live data are separate).\n- `idempotency_conflict` (409): the idempotency key was used for a different request (`channel_code` `body_mismatch`: use a new key), or that request is still running (`in_progress`: wait `retry_after` seconds and repeat it with the same key), or it already created a secret that is shown only once (`secret_not_kept`: creating a webhook endpoint, rotating its secret, connecting a WhatsApp number; a secret that was lost must be rotated again, and a WhatsApp number connected again to see its verify token).\n- `outside_window` (409, or in `message.failed`): the channel will not deliver outside its conversation window. On WhatsApp the 24-hour window is closed: send a template (`POST /v0/conversations/{conversation_id}/template`, or `POST /v0/messages` with `kind` `template` to start), or wait for the contact to write. On iMessage the contact has not messaged the line (or opted in) yet, or not recently, so the failure arrives as a `message.failed` event: wait for the contact to write, then reply in that conversation. Typing and read receipts answer `409 outside_window` directly when the channel's window for them is closed (iMessage typing works only within 5 minutes of the contact's last message; WhatsApp read receipts go at any time); ignore it and send your reply. On SMS, `channel_code` `quiet_hours`: the contact has not texted in the last hour and it is inside the sender's quiet hours in their time zone; retry after `retry_after` seconds, or wait for them to text. In `message.failed`: `window_closed` (WhatsApp's 24-hour window closed while the message was queued; send a template) and `queued_too_long` (the message waited in the queue longer than its sender allows, for example while the sender was paused; send it again if it still applies).\n- `unsupported_content` (422): the conversation's channel does not take this kind, field, block or value, and nothing was sent; Flow never converts a message. `param` names what was refused: `kind` (the endpoint's kind is not on this channel, such as a carousel on Telegram), a field or block (`reply_to` on SMS, `telegram` on WhatsApp, `telegram.is_big`), or the field whose value is not taken (`emoji` outside Telegram's reaction emoji, `buttons` for more than 3 WhatsApp buttons). `channel` is the conversation's channel and `hint` says what to send instead over plain HTTP; each endpoint's page lists its channels. In an iMessage group conversation, a reaction, typing or a read receipt answers it with `param` `kind` and `channel_code` `group_unsupported`: leave it out. Media to an SMS number without MMS answers it with `param` `media` and `channel_code` `no_mms`. An edit or unsend of an iMessage message that went by SMS or RCS (its `network`) answers it with `channel_code` `not_on_sms`: send a new message instead. An edit or unsend past the channel's time limit answers it with `channel_code` `edit_window_closed` or `unsend_window_closed` (iMessage: edits within 15 minutes of sending, unsends within 2 minutes; Telegram: unsends within 48 hours), and an iMessage edit of the message that started the conversation with `first_message_not_editable`; `message` states the limit and how long ago the message was sent. Send a new message instead. `POST /v0/files` answers it for a type Flow does not take (programs, web pages, unknown types). A send by `file_id` of a document a contact sent before Flow malware-scanned documents answers it too: Flow sends only documents it scanned.\n- `new_contact_limit` (429): the sender has used its budget for starting conversations. Wait `retry_after` seconds; replies into existing conversations still go.\n- `sender_throttled` (429): abuse signals tripped (the same text to many new contacts, many starts with no reply, blocks), so the sender may not start conversations until `retry_after`; replies into existing conversations still go. Personalise first messages and start only conversations people expect.\n- `file_blocked` (422): the file failed the malware scan and was not stored. Send a different file. (When the scan cannot run at all, the upload answers a retryable `500 api_error` with `Retry-After` instead.)\n- `rate_limited` (429): either too many requests for this key (the per-key request limit, reported in the `RateLimit-*` headers) or sends faster than the sender's sending rate (pacing). Wait `Retry-After` (`retry_after`) seconds and retry with the same `Idempotency-Key`.\n- `channel_error` (502, or in `message.failed`): the channel refused or failed the message, or timed out; `channel_code` carries its own code. Read `message`, change what the channel objected to, and send again. From typing and read receipts, which call the channel at once, it is safe to ignore. SMS codes are Flow's, the same whichever carrier network carries the message: `carrier_filtered` (a carrier's spam filter blocked it: make it read like a person's message, no shortened links), `unreachable`, `invalid_number`, `landline`, `registration_required` (the number's 10DLC or toll-free registration is not approved yet), `carrier_error`, and `provider_not_configured` (the SMS sender is not fully set up on Flow's side, so nothing was sent; contact the Flow team). Flow's own codes on every channel: `channel_timeout_unknown_outcome` (in `message.failed`: the channel did not answer after the message was sent, and the resends its policy allows did not settle it, so the message may have arrived or not; check with the contact before sending it again) and `unknown_outcome` (from typing and read receipts: the call timed out after it was sent).\n- `not_implemented` (501): this endpoint or channel is not live yet during the beta. Use a channel that is live (Telegram, iMessage), or check the changelog.\n- `api_error` (500, 503): something went wrong on Flow's side. Retry with the same idempotency key after `retry_after` seconds, and quote `request_id` if it persists.\n","enum":["invalid_request","authentication","permission","not_found","idempotency_conflict","outside_window","unsupported_content","new_contact_limit","sender_throttled","file_blocked","rate_limited","channel_error","not_implemented","api_error"],"isRequired":true,"uniqueKey":"error.type","typeLabel":"enum\u003cstring>"},"message":{"type":"string","description":"A sentence for a developer saying what went wrong. Do not parse it; switch on `type`.","isRequired":true,"uniqueKey":"error.message","typeLabel":"string"},"hint":{"type":"string","description":"One sentence saying what to change to make the request succeed, specific\nto this case, for example \"Send a template instead: POST\n/v0/conversations/{conversation_id}/template.\" For `unsupported_content`\nit says what to send instead over plain HTTP. Written for developers and\ncoding agents alike. Do not parse it; it may be reworded at any time.\n","examples":["Send a template instead: POST /v0/conversations/{conversation_id}/template."],"isRequired":true,"uniqueKey":"error.hint","typeLabel":"string"},"doc_url":{"type":"string","format":"uri","description":"The documentation page for this error's type, `https://api.flow.engineer/docs/errors/\u003ctype>`. It says what the error means, why it happens and how to fix it, with code.","examples":["https://api.flow.engineer/docs/errors/outside_window"],"isRequired":true,"uniqueKey":"error.doc_url","typeLabel":"string\u003curi>"},"param":{"type":"string","description":"The request parameter the error is about, as a dotted path (`kind`, `telegram.is_big`, `buttons`, `cards[1].header.type`, `limit`).","uniqueKey":"error.param","typeLabel":"string"},"retry_after":{"type":"integer","minimum":0,"description":"Seconds to wait before retrying, for errors that clear by themselves.","uniqueKey":"error.retry_after","typeLabel":"integer"},"channel":{"type":"string","description":"The conversation's channel, on errors about what it takes (`unsupported_content`) and its windows (`outside_window`).","enum":["telegram","whatsapp","imessage","sms"],"uniqueKey":"error.channel","typeLabel":"enum\u003cstring>"},"conversation":{"type":"string","description":"A conversation ID, `conv_` and a ULID.","pattern":"^conv_[0-9A-HJKMNP-TV-Z]{26}$","examples":["conv_01JB8ZC3K5M7P9R1T3V5X7Z9B1"],"uniqueKey":"error.conversation","typeLabel":"string"},"sender":{"type":"string","description":"A sender ID, `snd_` and a ULID.","pattern":"^snd_[0-9A-HJKMNP-TV-Z]{26}$","examples":["snd_01JB8Z4Q3V6W0R2N7C5H1M9K4T"],"uniqueKey":"error.sender","typeLabel":"string"},"channel_code":{"type":"string","description":"For `channel_error`, the channel's own error code, as given by the\nchannel. For some other errors, Flow's own code naming the case, for\nexample `sandbox_allowance_used` (`permission`: the sandbox allowance is\nused up), `sandbox_contact_limit` (`permission`: the allowance has no\nroom for another contact), `sandbox_channel_not_included` (`permission`:\nthe allowance does not cover this sandbox channel), `sign_in_required`\n(`permission`: an app made without an account cannot do this) or\n`sandbox_key_expired` (`authentication`: a key from `POST\n/v0/sandbox/keys` passed its `expires_at`; `permission`, for a send\nthat carries no key, such as a reply in a webhook answer, from such an\napp). For `idempotency_conflict`, always one of `body_mismatch` (the\nkey was used for a different request), `in_progress` (the first\nrequest is still running; `retry_after` says when to repeat it) or\n`secret_not_kept` (its answer carried a secret shown only once).\nSMS: `contact_opted_out` and `consent_required` (`permission`),\n`quiet_hours` (`outside_window`, with `retry_after`), and\n`carrier_filtered`, `unreachable`, `invalid_number`, `landline`,\n`registration_required`, `carrier_error` and `provider_not_configured`\n(`channel_error`). HIPAA accounts: `hipaa_channel_not_covered`\n(`permission`). Connecting your own WhatsApp number (`POST\n/v0/senders`): `whatsapp_token_malformed`, `whatsapp_token_rejected`,\n`whatsapp_app_secret_malformed` and `whatsapp_number_not_in_account`\n(`invalid_request`), and `whatsapp_number_taken`,\n`whatsapp_account_taken` and `whatsapp_number_managed` (`permission`).\nSimulated conversations (`permission`): `test_mode_only`,\n`not_simulated`, `simulated_contact` and `simulated_conversation_limit`.\nFor a message that failed after it was queued (`message.failed`):\n`sandbox_contact_left` (`permission`), `window_closed` and\n`queued_too_long` (`outside_window`), and\n`channel_timeout_unknown_outcome` (`channel_error`: the channel never\nconfirmed the send, so it may or may not have arrived). iMessage\n(`unsupported_content`): `group_unsupported` (a reaction, typing or a\nread receipt in a group conversation; `param` `kind`) and\n`not_on_sms` (an edit or unsend of a message that went by SMS or RCS;\n`param` `kind`). SMS (`unsupported_content`): `no_mms` (media to a\nnumber without MMS; `param` `media`).\nEdits and unsends (`unsupported_content`): `edit_window_closed` and\n`unsend_window_closed` (the message is older than the channel allows\nfor the change) and `first_message_not_editable` (iMessage: the message\nthat started the conversation). From typing and\nread receipts, `unknown_outcome` (`channel_error`: the call timed out after\nit was sent).\n","uniqueKey":"error.channel_code","typeLabel":"string"},"request_id":{"type":"string","description":"Flow's ID for this request. Quote it when asking for help.","uniqueKey":"error.request_id","typeLabel":"string"}},"isRequired":true,"uniqueKey":"error","typeLabel":"object"}},"uniqueKey":"","typeLabel":"object"},"examples":{"Example":{"title":"Example","value":{"error":{"type":"invalid_request","message":"\u003cstring>","hint":"Send a template instead: POST /v0/conversations/{conversation_id}/template.","doc_url":"https://api.flow.engineer/docs/errors/outside_window","param":"\u003cstring>","retry_after":1,"channel":"telegram","conversation":"conv_01JB8ZC3K5M7P9R1T3V5X7Z9B1","sender":"snd_01JB8Z4Q3V6W0R2N7C5H1M9K4T","channel_code":"\u003cstring>","request_id":"\u003cstring>"}}}},"exampleType":"generated"}},"exampleType":"generated"},"27a2aa50-4498-47f1-9ad3-ea35a2fd9db5":{"description":"The `Idempotency-Key` was already used (`idempotency_conflict`):\n`channel_code` `body_mismatch` (a different method, path or body; use a new\nkey), `in_progress` (the first request with the key is still running; repeat\nit after `retry_after` seconds), or `secret_not_kept` (the first request went\nthrough and its answer carried a secret shown only once). See \"Idempotency\"\nin the introduction.\n","content":{"application/json":{"schema":{"type":"object","description":"The body of every error answer.","required":["error"],"properties":{"error":{"type":"object","description":"The details of an error.","required":["type","message","hint","doc_url"],"properties":{"type":{"type":"string","description":"What went wrong. A closed list: new types arrive only with a new API version.\nEach type has a page at `https://api.flow.engineer/docs/errors/\u003ctype>`, given in\nthe error's `doc_url`; the error's `hint` says what to change for the case.\n\n- `invalid_request` (400, or 413 for a request body over the size limit): the request is malformed or a parameter is invalid. Fix the parameter named in `param`; the `hint` says what it must look like. Every endpoint answers it for a `Flow-Version` header that is not a version this API serves.\n- `authentication` (401): the API key is missing, malformed, unknown, revoked or expired. An `Authorization: Bearer` header with nothing after it usually means the environment variable holding the key was empty when the header was built (for an MCP client, when the server was added): the `hint` says so. Send `Authorization: Bearer fk_test_...` or `fk_live_...` with a current key; with no key at all, get a test key with `POST /v0/sandbox/keys`. A sandbox key past its `expires_at` has `channel_code` `sandbox_key_expired`: sign in to claim the app, or get a new key.\n- `permission` (403): the key may not do this, for example a test key using a live sender, a sandbox contact who joined another app, a new conversation from an iMessage line that may only reply, or a send past the app's sandbox allowance (`channel_code` `sandbox_allowance_used`, `sandbox_contact_limit` or `sandbox_channel_not_included`). Use the key of the right mode, have the contact send your join message, wait for the contact to message the line first, or sign in through the device flow to lift the allowance. On SMS: `contact_opted_out` (the contact texted STOP; nothing more goes to them until they text START), `consent_required` (a start to someone who never texted your numbers: record their consent with `consent` on the start). In a HIPAA account: `hipaa_channel_not_covered` (the channel has no business associate agreement; use SMS). Simulated conversations: `simulated_contact` (a real sender was pointed at a simulated contact; reply in the contact's simulated conversation), `not_simulated` (a simulated sender or test endpoint was pointed at a real contact), `test_mode_only`, `simulated_conversation_limit`. In `message.failed`: `sandbox_contact_left` (the contact joined another app on the sandbox sender while the message was queued; have them send your join message again).\n- `not_found` (404): no such object for this app and mode. Check the ID's prefix and that it was made with a key of the same mode (test and live data are separate).\n- `idempotency_conflict` (409): the idempotency key was used for a different request (`channel_code` `body_mismatch`: use a new key), or that request is still running (`in_progress`: wait `retry_after` seconds and repeat it with the same key), or it already created a secret that is shown only once (`secret_not_kept`: creating a webhook endpoint, rotating its secret, connecting a WhatsApp number; a secret that was lost must be rotated again, and a WhatsApp number connected again to see its verify token).\n- `outside_window` (409, or in `message.failed`): the channel will not deliver outside its conversation window. On WhatsApp the 24-hour window is closed: send a template (`POST /v0/conversations/{conversation_id}/template`, or `POST /v0/messages` with `kind` `template` to start), or wait for the contact to write. On iMessage the contact has not messaged the line (or opted in) yet, or not recently, so the failure arrives as a `message.failed` event: wait for the contact to write, then reply in that conversation. Typing and read receipts answer `409 outside_window` directly when the channel's window for them is closed (iMessage typing works only within 5 minutes of the contact's last message; WhatsApp read receipts go at any time); ignore it and send your reply. On SMS, `channel_code` `quiet_hours`: the contact has not texted in the last hour and it is inside the sender's quiet hours in their time zone; retry after `retry_after` seconds, or wait for them to text. In `message.failed`: `window_closed` (WhatsApp's 24-hour window closed while the message was queued; send a template) and `queued_too_long` (the message waited in the queue longer than its sender allows, for example while the sender was paused; send it again if it still applies).\n- `unsupported_content` (422): the conversation's channel does not take this kind, field, block or value, and nothing was sent; Flow never converts a message. `param` names what was refused: `kind` (the endpoint's kind is not on this channel, such as a carousel on Telegram), a field or block (`reply_to` on SMS, `telegram` on WhatsApp, `telegram.is_big`), or the field whose value is not taken (`emoji` outside Telegram's reaction emoji, `buttons` for more than 3 WhatsApp buttons). `channel` is the conversation's channel and `hint` says what to send instead over plain HTTP; each endpoint's page lists its channels. In an iMessage group conversation, a reaction, typing or a read receipt answers it with `param` `kind` and `channel_code` `group_unsupported`: leave it out. Media to an SMS number without MMS answers it with `param` `media` and `channel_code` `no_mms`. An edit or unsend of an iMessage message that went by SMS or RCS (its `network`) answers it with `channel_code` `not_on_sms`: send a new message instead. An edit or unsend past the channel's time limit answers it with `channel_code` `edit_window_closed` or `unsend_window_closed` (iMessage: edits within 15 minutes of sending, unsends within 2 minutes; Telegram: unsends within 48 hours), and an iMessage edit of the message that started the conversation with `first_message_not_editable`; `message` states the limit and how long ago the message was sent. Send a new message instead. `POST /v0/files` answers it for a type Flow does not take (programs, web pages, unknown types). A send by `file_id` of a document a contact sent before Flow malware-scanned documents answers it too: Flow sends only documents it scanned.\n- `new_contact_limit` (429): the sender has used its budget for starting conversations. Wait `retry_after` seconds; replies into existing conversations still go.\n- `sender_throttled` (429): abuse signals tripped (the same text to many new contacts, many starts with no reply, blocks), so the sender may not start conversations until `retry_after`; replies into existing conversations still go. Personalise first messages and start only conversations people expect.\n- `file_blocked` (422): the file failed the malware scan and was not stored. Send a different file. (When the scan cannot run at all, the upload answers a retryable `500 api_error` with `Retry-After` instead.)\n- `rate_limited` (429): either too many requests for this key (the per-key request limit, reported in the `RateLimit-*` headers) or sends faster than the sender's sending rate (pacing). Wait `Retry-After` (`retry_after`) seconds and retry with the same `Idempotency-Key`.\n- `channel_error` (502, or in `message.failed`): the channel refused or failed the message, or timed out; `channel_code` carries its own code. Read `message`, change what the channel objected to, and send again. From typing and read receipts, which call the channel at once, it is safe to ignore. SMS codes are Flow's, the same whichever carrier network carries the message: `carrier_filtered` (a carrier's spam filter blocked it: make it read like a person's message, no shortened links), `unreachable`, `invalid_number`, `landline`, `registration_required` (the number's 10DLC or toll-free registration is not approved yet), `carrier_error`, and `provider_not_configured` (the SMS sender is not fully set up on Flow's side, so nothing was sent; contact the Flow team). Flow's own codes on every channel: `channel_timeout_unknown_outcome` (in `message.failed`: the channel did not answer after the message was sent, and the resends its policy allows did not settle it, so the message may have arrived or not; check with the contact before sending it again) and `unknown_outcome` (from typing and read receipts: the call timed out after it was sent).\n- `not_implemented` (501): this endpoint or channel is not live yet during the beta. Use a channel that is live (Telegram, iMessage), or check the changelog.\n- `api_error` (500, 503): something went wrong on Flow's side. Retry with the same idempotency key after `retry_after` seconds, and quote `request_id` if it persists.\n","enum":["invalid_request","authentication","permission","not_found","idempotency_conflict","outside_window","unsupported_content","new_contact_limit","sender_throttled","file_blocked","rate_limited","channel_error","not_implemented","api_error"],"isRequired":true,"uniqueKey":"error.type","typeLabel":"enum\u003cstring>"},"message":{"type":"string","description":"A sentence for a developer saying what went wrong. Do not parse it; switch on `type`.","isRequired":true,"uniqueKey":"error.message","typeLabel":"string"},"hint":{"type":"string","description":"One sentence saying what to change to make the request succeed, specific\nto this case, for example \"Send a template instead: POST\n/v0/conversations/{conversation_id}/template.\" For `unsupported_content`\nit says what to send instead over plain HTTP. Written for developers and\ncoding agents alike. Do not parse it; it may be reworded at any time.\n","examples":["Send a template instead: POST /v0/conversations/{conversation_id}/template."],"isRequired":true,"uniqueKey":"error.hint","typeLabel":"string"},"doc_url":{"type":"string","format":"uri","description":"The documentation page for this error's type, `https://api.flow.engineer/docs/errors/\u003ctype>`. It says what the error means, why it happens and how to fix it, with code.","examples":["https://api.flow.engineer/docs/errors/outside_window"],"isRequired":true,"uniqueKey":"error.doc_url","typeLabel":"string\u003curi>"},"param":{"type":"string","description":"The request parameter the error is about, as a dotted path (`kind`, `telegram.is_big`, `buttons`, `cards[1].header.type`, `limit`).","uniqueKey":"error.param","typeLabel":"string"},"retry_after":{"type":"integer","minimum":0,"description":"Seconds to wait before retrying, for errors that clear by themselves.","uniqueKey":"error.retry_after","typeLabel":"integer"},"channel":{"type":"string","description":"The conversation's channel, on errors about what it takes (`unsupported_content`) and its windows (`outside_window`).","enum":["telegram","whatsapp","imessage","sms"],"uniqueKey":"error.channel","typeLabel":"enum\u003cstring>"},"conversation":{"type":"string","description":"A conversation ID, `conv_` and a ULID.","pattern":"^conv_[0-9A-HJKMNP-TV-Z]{26}$","examples":["conv_01JB8ZC3K5M7P9R1T3V5X7Z9B1"],"uniqueKey":"error.conversation","typeLabel":"string"},"sender":{"type":"string","description":"A sender ID, `snd_` and a ULID.","pattern":"^snd_[0-9A-HJKMNP-TV-Z]{26}$","examples":["snd_01JB8Z4Q3V6W0R2N7C5H1M9K4T"],"uniqueKey":"error.sender","typeLabel":"string"},"channel_code":{"type":"string","description":"For `channel_error`, the channel's own error code, as given by the\nchannel. For some other errors, Flow's own code naming the case, for\nexample `sandbox_allowance_used` (`permission`: the sandbox allowance is\nused up), `sandbox_contact_limit` (`permission`: the allowance has no\nroom for another contact), `sandbox_channel_not_included` (`permission`:\nthe allowance does not cover this sandbox channel), `sign_in_required`\n(`permission`: an app made without an account cannot do this) or\n`sandbox_key_expired` (`authentication`: a key from `POST\n/v0/sandbox/keys` passed its `expires_at`; `permission`, for a send\nthat carries no key, such as a reply in a webhook answer, from such an\napp). For `idempotency_conflict`, always one of `body_mismatch` (the\nkey was used for a different request), `in_progress` (the first\nrequest is still running; `retry_after` says when to repeat it) or\n`secret_not_kept` (its answer carried a secret shown only once).\nSMS: `contact_opted_out` and `consent_required` (`permission`),\n`quiet_hours` (`outside_window`, with `retry_after`), and\n`carrier_filtered`, `unreachable`, `invalid_number`, `landline`,\n`registration_required`, `carrier_error` and `provider_not_configured`\n(`channel_error`). HIPAA accounts: `hipaa_channel_not_covered`\n(`permission`). Connecting your own WhatsApp number (`POST\n/v0/senders`): `whatsapp_token_malformed`, `whatsapp_token_rejected`,\n`whatsapp_app_secret_malformed` and `whatsapp_number_not_in_account`\n(`invalid_request`), and `whatsapp_number_taken`,\n`whatsapp_account_taken` and `whatsapp_number_managed` (`permission`).\nSimulated conversations (`permission`): `test_mode_only`,\n`not_simulated`, `simulated_contact` and `simulated_conversation_limit`.\nFor a message that failed after it was queued (`message.failed`):\n`sandbox_contact_left` (`permission`), `window_closed` and\n`queued_too_long` (`outside_window`), and\n`channel_timeout_unknown_outcome` (`channel_error`: the channel never\nconfirmed the send, so it may or may not have arrived). iMessage\n(`unsupported_content`): `group_unsupported` (a reaction, typing or a\nread receipt in a group conversation; `param` `kind`) and\n`not_on_sms` (an edit or unsend of a message that went by SMS or RCS;\n`param` `kind`). SMS (`unsupported_content`): `no_mms` (media to a\nnumber without MMS; `param` `media`).\nEdits and unsends (`unsupported_content`): `edit_window_closed` and\n`unsend_window_closed` (the message is older than the channel allows\nfor the change) and `first_message_not_editable` (iMessage: the message\nthat started the conversation). From typing and\nread receipts, `unknown_outcome` (`channel_error`: the call timed out after\nit was sent).\n","uniqueKey":"error.channel_code","typeLabel":"string"},"request_id":{"type":"string","description":"Flow's ID for this request. Quote it when asking for help.","uniqueKey":"error.request_id","typeLabel":"string"}},"isRequired":true,"uniqueKey":"error","typeLabel":"object"}},"uniqueKey":"","typeLabel":"object"},"example":{"error":{"type":"idempotency_conflict","message":"This Idempotency-Key was already used for a different request (another method, path or body).","hint":"Use a new Idempotency-Key for this request; reuse a key only to repeat the identical request.","doc_url":"https://api.flow.engineer/docs/errors/idempotency_conflict","param":"Idempotency-Key","channel_code":"body_mismatch"}},"examples":{"Example":{"title":"Example","value":{"error":{"type":"idempotency_conflict","message":"This Idempotency-Key was already used for a different request (another method, path or body).","hint":"Use a new Idempotency-Key for this request; reuse a key only to repeat the identical request.","doc_url":"https://api.flow.engineer/docs/errors/idempotency_conflict","param":"Idempotency-Key","channel_code":"body_mismatch"}}}},"exampleType":"examples"}},"exampleType":"examples"},"5fb17786-743e-473a-bba0-d31d79e937ad":{"description":"The request body is larger than Flow takes (`invalid_request`): 32 MB, or\n101 MB for `POST /v0/files` (a 100 MB file and its multipart framing). A body\nthat declares a larger `Content-Length` is refused before it is read. Send\nmedia as a `file_id` from `POST /v0/files` or by `url` instead of inlining it.\n","content":{"application/json":{"schema":{"type":"object","description":"The body of every error answer.","required":["error"],"properties":{"error":{"type":"object","description":"The details of an error.","required":["type","message","hint","doc_url"],"properties":{"type":{"type":"string","description":"What went wrong. A closed list: new types arrive only with a new API version.\nEach type has a page at `https://api.flow.engineer/docs/errors/\u003ctype>`, given in\nthe error's `doc_url`; the error's `hint` says what to change for the case.\n\n- `invalid_request` (400, or 413 for a request body over the size limit): the request is malformed or a parameter is invalid. Fix the parameter named in `param`; the `hint` says what it must look like. Every endpoint answers it for a `Flow-Version` header that is not a version this API serves.\n- `authentication` (401): the API key is missing, malformed, unknown, revoked or expired. An `Authorization: Bearer` header with nothing after it usually means the environment variable holding the key was empty when the header was built (for an MCP client, when the server was added): the `hint` says so. Send `Authorization: Bearer fk_test_...` or `fk_live_...` with a current key; with no key at all, get a test key with `POST /v0/sandbox/keys`. A sandbox key past its `expires_at` has `channel_code` `sandbox_key_expired`: sign in to claim the app, or get a new key.\n- `permission` (403): the key may not do this, for example a test key using a live sender, a sandbox contact who joined another app, a new conversation from an iMessage line that may only reply, or a send past the app's sandbox allowance (`channel_code` `sandbox_allowance_used`, `sandbox_contact_limit` or `sandbox_channel_not_included`). Use the key of the right mode, have the contact send your join message, wait for the contact to message the line first, or sign in through the device flow to lift the allowance. On SMS: `contact_opted_out` (the contact texted STOP; nothing more goes to them until they text START), `consent_required` (a start to someone who never texted your numbers: record their consent with `consent` on the start). In a HIPAA account: `hipaa_channel_not_covered` (the channel has no business associate agreement; use SMS). Simulated conversations: `simulated_contact` (a real sender was pointed at a simulated contact; reply in the contact's simulated conversation), `not_simulated` (a simulated sender or test endpoint was pointed at a real contact), `test_mode_only`, `simulated_conversation_limit`. In `message.failed`: `sandbox_contact_left` (the contact joined another app on the sandbox sender while the message was queued; have them send your join message again).\n- `not_found` (404): no such object for this app and mode. Check the ID's prefix and that it was made with a key of the same mode (test and live data are separate).\n- `idempotency_conflict` (409): the idempotency key was used for a different request (`channel_code` `body_mismatch`: use a new key), or that request is still running (`in_progress`: wait `retry_after` seconds and repeat it with the same key), or it already created a secret that is shown only once (`secret_not_kept`: creating a webhook endpoint, rotating its secret, connecting a WhatsApp number; a secret that was lost must be rotated again, and a WhatsApp number connected again to see its verify token).\n- `outside_window` (409, or in `message.failed`): the channel will not deliver outside its conversation window. On WhatsApp the 24-hour window is closed: send a template (`POST /v0/conversations/{conversation_id}/template`, or `POST /v0/messages` with `kind` `template` to start), or wait for the contact to write. On iMessage the contact has not messaged the line (or opted in) yet, or not recently, so the failure arrives as a `message.failed` event: wait for the contact to write, then reply in that conversation. Typing and read receipts answer `409 outside_window` directly when the channel's window for them is closed (iMessage typing works only within 5 minutes of the contact's last message; WhatsApp read receipts go at any time); ignore it and send your reply. On SMS, `channel_code` `quiet_hours`: the contact has not texted in the last hour and it is inside the sender's quiet hours in their time zone; retry after `retry_after` seconds, or wait for them to text. In `message.failed`: `window_closed` (WhatsApp's 24-hour window closed while the message was queued; send a template) and `queued_too_long` (the message waited in the queue longer than its sender allows, for example while the sender was paused; send it again if it still applies).\n- `unsupported_content` (422): the conversation's channel does not take this kind, field, block or value, and nothing was sent; Flow never converts a message. `param` names what was refused: `kind` (the endpoint's kind is not on this channel, such as a carousel on Telegram), a field or block (`reply_to` on SMS, `telegram` on WhatsApp, `telegram.is_big`), or the field whose value is not taken (`emoji` outside Telegram's reaction emoji, `buttons` for more than 3 WhatsApp buttons). `channel` is the conversation's channel and `hint` says what to send instead over plain HTTP; each endpoint's page lists its channels. In an iMessage group conversation, a reaction, typing or a read receipt answers it with `param` `kind` and `channel_code` `group_unsupported`: leave it out. Media to an SMS number without MMS answers it with `param` `media` and `channel_code` `no_mms`. An edit or unsend of an iMessage message that went by SMS or RCS (its `network`) answers it with `channel_code` `not_on_sms`: send a new message instead. An edit or unsend past the channel's time limit answers it with `channel_code` `edit_window_closed` or `unsend_window_closed` (iMessage: edits within 15 minutes of sending, unsends within 2 minutes; Telegram: unsends within 48 hours), and an iMessage edit of the message that started the conversation with `first_message_not_editable`; `message` states the limit and how long ago the message was sent. Send a new message instead. `POST /v0/files` answers it for a type Flow does not take (programs, web pages, unknown types). A send by `file_id` of a document a contact sent before Flow malware-scanned documents answers it too: Flow sends only documents it scanned.\n- `new_contact_limit` (429): the sender has used its budget for starting conversations. Wait `retry_after` seconds; replies into existing conversations still go.\n- `sender_throttled` (429): abuse signals tripped (the same text to many new contacts, many starts with no reply, blocks), so the sender may not start conversations until `retry_after`; replies into existing conversations still go. Personalise first messages and start only conversations people expect.\n- `file_blocked` (422): the file failed the malware scan and was not stored. Send a different file. (When the scan cannot run at all, the upload answers a retryable `500 api_error` with `Retry-After` instead.)\n- `rate_limited` (429): either too many requests for this key (the per-key request limit, reported in the `RateLimit-*` headers) or sends faster than the sender's sending rate (pacing). Wait `Retry-After` (`retry_after`) seconds and retry with the same `Idempotency-Key`.\n- `channel_error` (502, or in `message.failed`): the channel refused or failed the message, or timed out; `channel_code` carries its own code. Read `message`, change what the channel objected to, and send again. From typing and read receipts, which call the channel at once, it is safe to ignore. SMS codes are Flow's, the same whichever carrier network carries the message: `carrier_filtered` (a carrier's spam filter blocked it: make it read like a person's message, no shortened links), `unreachable`, `invalid_number`, `landline`, `registration_required` (the number's 10DLC or toll-free registration is not approved yet), `carrier_error`, and `provider_not_configured` (the SMS sender is not fully set up on Flow's side, so nothing was sent; contact the Flow team). Flow's own codes on every channel: `channel_timeout_unknown_outcome` (in `message.failed`: the channel did not answer after the message was sent, and the resends its policy allows did not settle it, so the message may have arrived or not; check with the contact before sending it again) and `unknown_outcome` (from typing and read receipts: the call timed out after it was sent).\n- `not_implemented` (501): this endpoint or channel is not live yet during the beta. Use a channel that is live (Telegram, iMessage), or check the changelog.\n- `api_error` (500, 503): something went wrong on Flow's side. Retry with the same idempotency key after `retry_after` seconds, and quote `request_id` if it persists.\n","enum":["invalid_request","authentication","permission","not_found","idempotency_conflict","outside_window","unsupported_content","new_contact_limit","sender_throttled","file_blocked","rate_limited","channel_error","not_implemented","api_error"],"isRequired":true,"uniqueKey":"error.type","typeLabel":"enum\u003cstring>"},"message":{"type":"string","description":"A sentence for a developer saying what went wrong. Do not parse it; switch on `type`.","isRequired":true,"uniqueKey":"error.message","typeLabel":"string"},"hint":{"type":"string","description":"One sentence saying what to change to make the request succeed, specific\nto this case, for example \"Send a template instead: POST\n/v0/conversations/{conversation_id}/template.\" For `unsupported_content`\nit says what to send instead over plain HTTP. Written for developers and\ncoding agents alike. Do not parse it; it may be reworded at any time.\n","examples":["Send a template instead: POST /v0/conversations/{conversation_id}/template."],"isRequired":true,"uniqueKey":"error.hint","typeLabel":"string"},"doc_url":{"type":"string","format":"uri","description":"The documentation page for this error's type, `https://api.flow.engineer/docs/errors/\u003ctype>`. It says what the error means, why it happens and how to fix it, with code.","examples":["https://api.flow.engineer/docs/errors/outside_window"],"isRequired":true,"uniqueKey":"error.doc_url","typeLabel":"string\u003curi>"},"param":{"type":"string","description":"The request parameter the error is about, as a dotted path (`kind`, `telegram.is_big`, `buttons`, `cards[1].header.type`, `limit`).","uniqueKey":"error.param","typeLabel":"string"},"retry_after":{"type":"integer","minimum":0,"description":"Seconds to wait before retrying, for errors that clear by themselves.","uniqueKey":"error.retry_after","typeLabel":"integer"},"channel":{"type":"string","description":"The conversation's channel, on errors about what it takes (`unsupported_content`) and its windows (`outside_window`).","enum":["telegram","whatsapp","imessage","sms"],"uniqueKey":"error.channel","typeLabel":"enum\u003cstring>"},"conversation":{"type":"string","description":"A conversation ID, `conv_` and a ULID.","pattern":"^conv_[0-9A-HJKMNP-TV-Z]{26}$","examples":["conv_01JB8ZC3K5M7P9R1T3V5X7Z9B1"],"uniqueKey":"error.conversation","typeLabel":"string"},"sender":{"type":"string","description":"A sender ID, `snd_` and a ULID.","pattern":"^snd_[0-9A-HJKMNP-TV-Z]{26}$","examples":["snd_01JB8Z4Q3V6W0R2N7C5H1M9K4T"],"uniqueKey":"error.sender","typeLabel":"string"},"channel_code":{"type":"string","description":"For `channel_error`, the channel's own error code, as given by the\nchannel. For some other errors, Flow's own code naming the case, for\nexample `sandbox_allowance_used` (`permission`: the sandbox allowance is\nused up), `sandbox_contact_limit` (`permission`: the allowance has no\nroom for another contact), `sandbox_channel_not_included` (`permission`:\nthe allowance does not cover this sandbox channel), `sign_in_required`\n(`permission`: an app made without an account cannot do this) or\n`sandbox_key_expired` (`authentication`: a key from `POST\n/v0/sandbox/keys` passed its `expires_at`; `permission`, for a send\nthat carries no key, such as a reply in a webhook answer, from such an\napp). For `idempotency_conflict`, always one of `body_mismatch` (the\nkey was used for a different request), `in_progress` (the first\nrequest is still running; `retry_after` says when to repeat it) or\n`secret_not_kept` (its answer carried a secret shown only once).\nSMS: `contact_opted_out` and `consent_required` (`permission`),\n`quiet_hours` (`outside_window`, with `retry_after`), and\n`carrier_filtered`, `unreachable`, `invalid_number`, `landline`,\n`registration_required`, `carrier_error` and `provider_not_configured`\n(`channel_error`). HIPAA accounts: `hipaa_channel_not_covered`\n(`permission`). Connecting your own WhatsApp number (`POST\n/v0/senders`): `whatsapp_token_malformed`, `whatsapp_token_rejected`,\n`whatsapp_app_secret_malformed` and `whatsapp_number_not_in_account`\n(`invalid_request`), and `whatsapp_number_taken`,\n`whatsapp_account_taken` and `whatsapp_number_managed` (`permission`).\nSimulated conversations (`permission`): `test_mode_only`,\n`not_simulated`, `simulated_contact` and `simulated_conversation_limit`.\nFor a message that failed after it was queued (`message.failed`):\n`sandbox_contact_left` (`permission`), `window_closed` and\n`queued_too_long` (`outside_window`), and\n`channel_timeout_unknown_outcome` (`channel_error`: the channel never\nconfirmed the send, so it may or may not have arrived). iMessage\n(`unsupported_content`): `group_unsupported` (a reaction, typing or a\nread receipt in a group conversation; `param` `kind`) and\n`not_on_sms` (an edit or unsend of a message that went by SMS or RCS;\n`param` `kind`). SMS (`unsupported_content`): `no_mms` (media to a\nnumber without MMS; `param` `media`).\nEdits and unsends (`unsupported_content`): `edit_window_closed` and\n`unsend_window_closed` (the message is older than the channel allows\nfor the change) and `first_message_not_editable` (iMessage: the message\nthat started the conversation). From typing and\nread receipts, `unknown_outcome` (`channel_error`: the call timed out after\nit was sent).\n","uniqueKey":"error.channel_code","typeLabel":"string"},"request_id":{"type":"string","description":"Flow's ID for this request. Quote it when asking for help.","uniqueKey":"error.request_id","typeLabel":"string"}},"isRequired":true,"uniqueKey":"error","typeLabel":"object"}},"uniqueKey":"","typeLabel":"object"},"example":{"error":{"type":"invalid_request","message":"The request body is larger than 33554432 bytes.","hint":"Send a smaller body; upload media with POST /v0/files and send its file_id instead of inlining it.","doc_url":"https://api.flow.engineer/docs/errors/invalid_request"}},"examples":{"Example":{"title":"Example","value":{"error":{"type":"invalid_request","message":"The request body is larger than 33554432 bytes.","hint":"Send a smaller body; upload media with POST /v0/files and send its file_id instead of inlining it.","doc_url":"https://api.flow.engineer/docs/errors/invalid_request"}}}},"exampleType":"examples"}},"exampleType":"examples"},"59a32a55-4d2c-44f1-b867-785ea889d5d8":{"description":"The conversation's channel does not take this kind, field, block or value,\nso nothing was sent (`unsupported_content`). `param` names what was\nrefused, `channel` the channel, and `hint` what to send instead.\n","content":{"application/json":{"schema":{"type":"object","description":"The body of every error answer.","required":["error"],"properties":{"error":{"type":"object","description":"The details of an error.","required":["type","message","hint","doc_url"],"properties":{"type":{"type":"string","description":"What went wrong. A closed list: new types arrive only with a new API version.\nEach type has a page at `https://api.flow.engineer/docs/errors/\u003ctype>`, given in\nthe error's `doc_url`; the error's `hint` says what to change for the case.\n\n- `invalid_request` (400, or 413 for a request body over the size limit): the request is malformed or a parameter is invalid. Fix the parameter named in `param`; the `hint` says what it must look like. Every endpoint answers it for a `Flow-Version` header that is not a version this API serves.\n- `authentication` (401): the API key is missing, malformed, unknown, revoked or expired. An `Authorization: Bearer` header with nothing after it usually means the environment variable holding the key was empty when the header was built (for an MCP client, when the server was added): the `hint` says so. Send `Authorization: Bearer fk_test_...` or `fk_live_...` with a current key; with no key at all, get a test key with `POST /v0/sandbox/keys`. A sandbox key past its `expires_at` has `channel_code` `sandbox_key_expired`: sign in to claim the app, or get a new key.\n- `permission` (403): the key may not do this, for example a test key using a live sender, a sandbox contact who joined another app, a new conversation from an iMessage line that may only reply, or a send past the app's sandbox allowance (`channel_code` `sandbox_allowance_used`, `sandbox_contact_limit` or `sandbox_channel_not_included`). Use the key of the right mode, have the contact send your join message, wait for the contact to message the line first, or sign in through the device flow to lift the allowance. On SMS: `contact_opted_out` (the contact texted STOP; nothing more goes to them until they text START), `consent_required` (a start to someone who never texted your numbers: record their consent with `consent` on the start). In a HIPAA account: `hipaa_channel_not_covered` (the channel has no business associate agreement; use SMS). Simulated conversations: `simulated_contact` (a real sender was pointed at a simulated contact; reply in the contact's simulated conversation), `not_simulated` (a simulated sender or test endpoint was pointed at a real contact), `test_mode_only`, `simulated_conversation_limit`. In `message.failed`: `sandbox_contact_left` (the contact joined another app on the sandbox sender while the message was queued; have them send your join message again).\n- `not_found` (404): no such object for this app and mode. Check the ID's prefix and that it was made with a key of the same mode (test and live data are separate).\n- `idempotency_conflict` (409): the idempotency key was used for a different request (`channel_code` `body_mismatch`: use a new key), or that request is still running (`in_progress`: wait `retry_after` seconds and repeat it with the same key), or it already created a secret that is shown only once (`secret_not_kept`: creating a webhook endpoint, rotating its secret, connecting a WhatsApp number; a secret that was lost must be rotated again, and a WhatsApp number connected again to see its verify token).\n- `outside_window` (409, or in `message.failed`): the channel will not deliver outside its conversation window. On WhatsApp the 24-hour window is closed: send a template (`POST /v0/conversations/{conversation_id}/template`, or `POST /v0/messages` with `kind` `template` to start), or wait for the contact to write. On iMessage the contact has not messaged the line (or opted in) yet, or not recently, so the failure arrives as a `message.failed` event: wait for the contact to write, then reply in that conversation. Typing and read receipts answer `409 outside_window` directly when the channel's window for them is closed (iMessage typing works only within 5 minutes of the contact's last message; WhatsApp read receipts go at any time); ignore it and send your reply. On SMS, `channel_code` `quiet_hours`: the contact has not texted in the last hour and it is inside the sender's quiet hours in their time zone; retry after `retry_after` seconds, or wait for them to text. In `message.failed`: `window_closed` (WhatsApp's 24-hour window closed while the message was queued; send a template) and `queued_too_long` (the message waited in the queue longer than its sender allows, for example while the sender was paused; send it again if it still applies).\n- `unsupported_content` (422): the conversation's channel does not take this kind, field, block or value, and nothing was sent; Flow never converts a message. `param` names what was refused: `kind` (the endpoint's kind is not on this channel, such as a carousel on Telegram), a field or block (`reply_to` on SMS, `telegram` on WhatsApp, `telegram.is_big`), or the field whose value is not taken (`emoji` outside Telegram's reaction emoji, `buttons` for more than 3 WhatsApp buttons). `channel` is the conversation's channel and `hint` says what to send instead over plain HTTP; each endpoint's page lists its channels. In an iMessage group conversation, a reaction, typing or a read receipt answers it with `param` `kind` and `channel_code` `group_unsupported`: leave it out. Media to an SMS number without MMS answers it with `param` `media` and `channel_code` `no_mms`. An edit or unsend of an iMessage message that went by SMS or RCS (its `network`) answers it with `channel_code` `not_on_sms`: send a new message instead. An edit or unsend past the channel's time limit answers it with `channel_code` `edit_window_closed` or `unsend_window_closed` (iMessage: edits within 15 minutes of sending, unsends within 2 minutes; Telegram: unsends within 48 hours), and an iMessage edit of the message that started the conversation with `first_message_not_editable`; `message` states the limit and how long ago the message was sent. Send a new message instead. `POST /v0/files` answers it for a type Flow does not take (programs, web pages, unknown types). A send by `file_id` of a document a contact sent before Flow malware-scanned documents answers it too: Flow sends only documents it scanned.\n- `new_contact_limit` (429): the sender has used its budget for starting conversations. Wait `retry_after` seconds; replies into existing conversations still go.\n- `sender_throttled` (429): abuse signals tripped (the same text to many new contacts, many starts with no reply, blocks), so the sender may not start conversations until `retry_after`; replies into existing conversations still go. Personalise first messages and start only conversations people expect.\n- `file_blocked` (422): the file failed the malware scan and was not stored. Send a different file. (When the scan cannot run at all, the upload answers a retryable `500 api_error` with `Retry-After` instead.)\n- `rate_limited` (429): either too many requests for this key (the per-key request limit, reported in the `RateLimit-*` headers) or sends faster than the sender's sending rate (pacing). Wait `Retry-After` (`retry_after`) seconds and retry with the same `Idempotency-Key`.\n- `channel_error` (502, or in `message.failed`): the channel refused or failed the message, or timed out; `channel_code` carries its own code. Read `message`, change what the channel objected to, and send again. From typing and read receipts, which call the channel at once, it is safe to ignore. SMS codes are Flow's, the same whichever carrier network carries the message: `carrier_filtered` (a carrier's spam filter blocked it: make it read like a person's message, no shortened links), `unreachable`, `invalid_number`, `landline`, `registration_required` (the number's 10DLC or toll-free registration is not approved yet), `carrier_error`, and `provider_not_configured` (the SMS sender is not fully set up on Flow's side, so nothing was sent; contact the Flow team). Flow's own codes on every channel: `channel_timeout_unknown_outcome` (in `message.failed`: the channel did not answer after the message was sent, and the resends its policy allows did not settle it, so the message may have arrived or not; check with the contact before sending it again) and `unknown_outcome` (from typing and read receipts: the call timed out after it was sent).\n- `not_implemented` (501): this endpoint or channel is not live yet during the beta. Use a channel that is live (Telegram, iMessage), or check the changelog.\n- `api_error` (500, 503): something went wrong on Flow's side. Retry with the same idempotency key after `retry_after` seconds, and quote `request_id` if it persists.\n","enum":["invalid_request","authentication","permission","not_found","idempotency_conflict","outside_window","unsupported_content","new_contact_limit","sender_throttled","file_blocked","rate_limited","channel_error","not_implemented","api_error"],"isRequired":true,"uniqueKey":"error.type","typeLabel":"enum\u003cstring>"},"message":{"type":"string","description":"A sentence for a developer saying what went wrong. Do not parse it; switch on `type`.","isRequired":true,"uniqueKey":"error.message","typeLabel":"string"},"hint":{"type":"string","description":"One sentence saying what to change to make the request succeed, specific\nto this case, for example \"Send a template instead: POST\n/v0/conversations/{conversation_id}/template.\" For `unsupported_content`\nit says what to send instead over plain HTTP. Written for developers and\ncoding agents alike. Do not parse it; it may be reworded at any time.\n","examples":["Send a template instead: POST /v0/conversations/{conversation_id}/template."],"isRequired":true,"uniqueKey":"error.hint","typeLabel":"string"},"doc_url":{"type":"string","format":"uri","description":"The documentation page for this error's type, `https://api.flow.engineer/docs/errors/\u003ctype>`. It says what the error means, why it happens and how to fix it, with code.","examples":["https://api.flow.engineer/docs/errors/outside_window"],"isRequired":true,"uniqueKey":"error.doc_url","typeLabel":"string\u003curi>"},"param":{"type":"string","description":"The request parameter the error is about, as a dotted path (`kind`, `telegram.is_big`, `buttons`, `cards[1].header.type`, `limit`).","uniqueKey":"error.param","typeLabel":"string"},"retry_after":{"type":"integer","minimum":0,"description":"Seconds to wait before retrying, for errors that clear by themselves.","uniqueKey":"error.retry_after","typeLabel":"integer"},"channel":{"type":"string","description":"The conversation's channel, on errors about what it takes (`unsupported_content`) and its windows (`outside_window`).","enum":["telegram","whatsapp","imessage","sms"],"uniqueKey":"error.channel","typeLabel":"enum\u003cstring>"},"conversation":{"type":"string","description":"A conversation ID, `conv_` and a ULID.","pattern":"^conv_[0-9A-HJKMNP-TV-Z]{26}$","examples":["conv_01JB8ZC3K5M7P9R1T3V5X7Z9B1"],"uniqueKey":"error.conversation","typeLabel":"string"},"sender":{"type":"string","description":"A sender ID, `snd_` and a ULID.","pattern":"^snd_[0-9A-HJKMNP-TV-Z]{26}$","examples":["snd_01JB8Z4Q3V6W0R2N7C5H1M9K4T"],"uniqueKey":"error.sender","typeLabel":"string"},"channel_code":{"type":"string","description":"For `channel_error`, the channel's own error code, as given by the\nchannel. For some other errors, Flow's own code naming the case, for\nexample `sandbox_allowance_used` (`permission`: the sandbox allowance is\nused up), `sandbox_contact_limit` (`permission`: the allowance has no\nroom for another contact), `sandbox_channel_not_included` (`permission`:\nthe allowance does not cover this sandbox channel), `sign_in_required`\n(`permission`: an app made without an account cannot do this) or\n`sandbox_key_expired` (`authentication`: a key from `POST\n/v0/sandbox/keys` passed its `expires_at`; `permission`, for a send\nthat carries no key, such as a reply in a webhook answer, from such an\napp). For `idempotency_conflict`, always one of `body_mismatch` (the\nkey was used for a different request), `in_progress` (the first\nrequest is still running; `retry_after` says when to repeat it) or\n`secret_not_kept` (its answer carried a secret shown only once).\nSMS: `contact_opted_out` and `consent_required` (`permission`),\n`quiet_hours` (`outside_window`, with `retry_after`), and\n`carrier_filtered`, `unreachable`, `invalid_number`, `landline`,\n`registration_required`, `carrier_error` and `provider_not_configured`\n(`channel_error`). HIPAA accounts: `hipaa_channel_not_covered`\n(`permission`). Connecting your own WhatsApp number (`POST\n/v0/senders`): `whatsapp_token_malformed`, `whatsapp_token_rejected`,\n`whatsapp_app_secret_malformed` and `whatsapp_number_not_in_account`\n(`invalid_request`), and `whatsapp_number_taken`,\n`whatsapp_account_taken` and `whatsapp_number_managed` (`permission`).\nSimulated conversations (`permission`): `test_mode_only`,\n`not_simulated`, `simulated_contact` and `simulated_conversation_limit`.\nFor a message that failed after it was queued (`message.failed`):\n`sandbox_contact_left` (`permission`), `window_closed` and\n`queued_too_long` (`outside_window`), and\n`channel_timeout_unknown_outcome` (`channel_error`: the channel never\nconfirmed the send, so it may or may not have arrived). iMessage\n(`unsupported_content`): `group_unsupported` (a reaction, typing or a\nread receipt in a group conversation; `param` `kind`) and\n`not_on_sms` (an edit or unsend of a message that went by SMS or RCS;\n`param` `kind`). SMS (`unsupported_content`): `no_mms` (media to a\nnumber without MMS; `param` `media`).\nEdits and unsends (`unsupported_content`): `edit_window_closed` and\n`unsend_window_closed` (the message is older than the channel allows\nfor the change) and `first_message_not_editable` (iMessage: the message\nthat started the conversation). From typing and\nread receipts, `unknown_outcome` (`channel_error`: the call timed out after\nit was sent).\n","uniqueKey":"error.channel_code","typeLabel":"string"},"request_id":{"type":"string","description":"Flow's ID for this request. Quote it when asking for help.","uniqueKey":"error.request_id","typeLabel":"string"}},"isRequired":true,"uniqueKey":"error","typeLabel":"object"}},"uniqueKey":"","typeLabel":"object"},"example":{"error":{"type":"unsupported_content","channel":"telegram","param":"kind","message":"Carousels work on WhatsApp only.","hint":"On Telegram, send a basic message, then one buttons message with an image header per card.","doc_url":"https://api.flow.engineer/docs/errors/unsupported_content","conversation":"conv_01JB8ZC3K5M7P9R1T3V5X7Z9B1"}},"examples":{"Example":{"title":"Example","value":{"error":{"type":"unsupported_content","channel":"telegram","param":"kind","message":"Carousels work on WhatsApp only.","hint":"On Telegram, send a basic message, then one buttons message with an image header per card.","doc_url":"https://api.flow.engineer/docs/errors/unsupported_content","conversation":"conv_01JB8ZC3K5M7P9R1T3V5X7Z9B1"}}}},"exampleType":"examples"}},"exampleType":"examples"},"8846f775-97b0-4cca-951a-13435feb3ad5":{"description":"Too many requests (`rate_limited`): either this key's request limit (see the\n`RateLimit-*` headers) or the sender's sending rate (pacing) was exceeded;\nretry after `Retry-After` seconds (also `error.retry_after`) with the same\n`Idempotency-Key`. Or the sender has used its budget for starting\nconversations (`new_contact_limit`), or the sender is throttled after abuse\nsignals (`sender_throttled`); wait `retry_after` seconds.\n","headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}},"RateLimit-Limit":{"description":"Requests allowed in the current window for this key.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests left in the current window for this key.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","description":"The body of every error answer.","required":["error"],"properties":{"error":{"type":"object","description":"The details of an error.","required":["type","message","hint","doc_url"],"properties":{"type":{"type":"string","description":"What went wrong. A closed list: new types arrive only with a new API version.\nEach type has a page at `https://api.flow.engineer/docs/errors/\u003ctype>`, given in\nthe error's `doc_url`; the error's `hint` says what to change for the case.\n\n- `invalid_request` (400, or 413 for a request body over the size limit): the request is malformed or a parameter is invalid. Fix the parameter named in `param`; the `hint` says what it must look like. Every endpoint answers it for a `Flow-Version` header that is not a version this API serves.\n- `authentication` (401): the API key is missing, malformed, unknown, revoked or expired. An `Authorization: Bearer` header with nothing after it usually means the environment variable holding the key was empty when the header was built (for an MCP client, when the server was added): the `hint` says so. Send `Authorization: Bearer fk_test_...` or `fk_live_...` with a current key; with no key at all, get a test key with `POST /v0/sandbox/keys`. A sandbox key past its `expires_at` has `channel_code` `sandbox_key_expired`: sign in to claim the app, or get a new key.\n- `permission` (403): the key may not do this, for example a test key using a live sender, a sandbox contact who joined another app, a new conversation from an iMessage line that may only reply, or a send past the app's sandbox allowance (`channel_code` `sandbox_allowance_used`, `sandbox_contact_limit` or `sandbox_channel_not_included`). Use the key of the right mode, have the contact send your join message, wait for the contact to message the line first, or sign in through the device flow to lift the allowance. On SMS: `contact_opted_out` (the contact texted STOP; nothing more goes to them until they text START), `consent_required` (a start to someone who never texted your numbers: record their consent with `consent` on the start). In a HIPAA account: `hipaa_channel_not_covered` (the channel has no business associate agreement; use SMS). Simulated conversations: `simulated_contact` (a real sender was pointed at a simulated contact; reply in the contact's simulated conversation), `not_simulated` (a simulated sender or test endpoint was pointed at a real contact), `test_mode_only`, `simulated_conversation_limit`. In `message.failed`: `sandbox_contact_left` (the contact joined another app on the sandbox sender while the message was queued; have them send your join message again).\n- `not_found` (404): no such object for this app and mode. Check the ID's prefix and that it was made with a key of the same mode (test and live data are separate).\n- `idempotency_conflict` (409): the idempotency key was used for a different request (`channel_code` `body_mismatch`: use a new key), or that request is still running (`in_progress`: wait `retry_after` seconds and repeat it with the same key), or it already created a secret that is shown only once (`secret_not_kept`: creating a webhook endpoint, rotating its secret, connecting a WhatsApp number; a secret that was lost must be rotated again, and a WhatsApp number connected again to see its verify token).\n- `outside_window` (409, or in `message.failed`): the channel will not deliver outside its conversation window. On WhatsApp the 24-hour window is closed: send a template (`POST /v0/conversations/{conversation_id}/template`, or `POST /v0/messages` with `kind` `template` to start), or wait for the contact to write. On iMessage the contact has not messaged the line (or opted in) yet, or not recently, so the failure arrives as a `message.failed` event: wait for the contact to write, then reply in that conversation. Typing and read receipts answer `409 outside_window` directly when the channel's window for them is closed (iMessage typing works only within 5 minutes of the contact's last message; WhatsApp read receipts go at any time); ignore it and send your reply. On SMS, `channel_code` `quiet_hours`: the contact has not texted in the last hour and it is inside the sender's quiet hours in their time zone; retry after `retry_after` seconds, or wait for them to text. In `message.failed`: `window_closed` (WhatsApp's 24-hour window closed while the message was queued; send a template) and `queued_too_long` (the message waited in the queue longer than its sender allows, for example while the sender was paused; send it again if it still applies).\n- `unsupported_content` (422): the conversation's channel does not take this kind, field, block or value, and nothing was sent; Flow never converts a message. `param` names what was refused: `kind` (the endpoint's kind is not on this channel, such as a carousel on Telegram), a field or block (`reply_to` on SMS, `telegram` on WhatsApp, `telegram.is_big`), or the field whose value is not taken (`emoji` outside Telegram's reaction emoji, `buttons` for more than 3 WhatsApp buttons). `channel` is the conversation's channel and `hint` says what to send instead over plain HTTP; each endpoint's page lists its channels. In an iMessage group conversation, a reaction, typing or a read receipt answers it with `param` `kind` and `channel_code` `group_unsupported`: leave it out. Media to an SMS number without MMS answers it with `param` `media` and `channel_code` `no_mms`. An edit or unsend of an iMessage message that went by SMS or RCS (its `network`) answers it with `channel_code` `not_on_sms`: send a new message instead. An edit or unsend past the channel's time limit answers it with `channel_code` `edit_window_closed` or `unsend_window_closed` (iMessage: edits within 15 minutes of sending, unsends within 2 minutes; Telegram: unsends within 48 hours), and an iMessage edit of the message that started the conversation with `first_message_not_editable`; `message` states the limit and how long ago the message was sent. Send a new message instead. `POST /v0/files` answers it for a type Flow does not take (programs, web pages, unknown types). A send by `file_id` of a document a contact sent before Flow malware-scanned documents answers it too: Flow sends only documents it scanned.\n- `new_contact_limit` (429): the sender has used its budget for starting conversations. Wait `retry_after` seconds; replies into existing conversations still go.\n- `sender_throttled` (429): abuse signals tripped (the same text to many new contacts, many starts with no reply, blocks), so the sender may not start conversations until `retry_after`; replies into existing conversations still go. Personalise first messages and start only conversations people expect.\n- `file_blocked` (422): the file failed the malware scan and was not stored. Send a different file. (When the scan cannot run at all, the upload answers a retryable `500 api_error` with `Retry-After` instead.)\n- `rate_limited` (429): either too many requests for this key (the per-key request limit, reported in the `RateLimit-*` headers) or sends faster than the sender's sending rate (pacing). Wait `Retry-After` (`retry_after`) seconds and retry with the same `Idempotency-Key`.\n- `channel_error` (502, or in `message.failed`): the channel refused or failed the message, or timed out; `channel_code` carries its own code. Read `message`, change what the channel objected to, and send again. From typing and read receipts, which call the channel at once, it is safe to ignore. SMS codes are Flow's, the same whichever carrier network carries the message: `carrier_filtered` (a carrier's spam filter blocked it: make it read like a person's message, no shortened links), `unreachable`, `invalid_number`, `landline`, `registration_required` (the number's 10DLC or toll-free registration is not approved yet), `carrier_error`, and `provider_not_configured` (the SMS sender is not fully set up on Flow's side, so nothing was sent; contact the Flow team). Flow's own codes on every channel: `channel_timeout_unknown_outcome` (in `message.failed`: the channel did not answer after the message was sent, and the resends its policy allows did not settle it, so the message may have arrived or not; check with the contact before sending it again) and `unknown_outcome` (from typing and read receipts: the call timed out after it was sent).\n- `not_implemented` (501): this endpoint or channel is not live yet during the beta. Use a channel that is live (Telegram, iMessage), or check the changelog.\n- `api_error` (500, 503): something went wrong on Flow's side. Retry with the same idempotency key after `retry_after` seconds, and quote `request_id` if it persists.\n","enum":["invalid_request","authentication","permission","not_found","idempotency_conflict","outside_window","unsupported_content","new_contact_limit","sender_throttled","file_blocked","rate_limited","channel_error","not_implemented","api_error"],"isRequired":true,"uniqueKey":"error.type","typeLabel":"enum\u003cstring>"},"message":{"type":"string","description":"A sentence for a developer saying what went wrong. Do not parse it; switch on `type`.","isRequired":true,"uniqueKey":"error.message","typeLabel":"string"},"hint":{"type":"string","description":"One sentence saying what to change to make the request succeed, specific\nto this case, for example \"Send a template instead: POST\n/v0/conversations/{conversation_id}/template.\" For `unsupported_content`\nit says what to send instead over plain HTTP. Written for developers and\ncoding agents alike. Do not parse it; it may be reworded at any time.\n","examples":["Send a template instead: POST /v0/conversations/{conversation_id}/template."],"isRequired":true,"uniqueKey":"error.hint","typeLabel":"string"},"doc_url":{"type":"string","format":"uri","description":"The documentation page for this error's type, `https://api.flow.engineer/docs/errors/\u003ctype>`. It says what the error means, why it happens and how to fix it, with code.","examples":["https://api.flow.engineer/docs/errors/outside_window"],"isRequired":true,"uniqueKey":"error.doc_url","typeLabel":"string\u003curi>"},"param":{"type":"string","description":"The request parameter the error is about, as a dotted path (`kind`, `telegram.is_big`, `buttons`, `cards[1].header.type`, `limit`).","uniqueKey":"error.param","typeLabel":"string"},"retry_after":{"type":"integer","minimum":0,"description":"Seconds to wait before retrying, for errors that clear by themselves.","uniqueKey":"error.retry_after","typeLabel":"integer"},"channel":{"type":"string","description":"The conversation's channel, on errors about what it takes (`unsupported_content`) and its windows (`outside_window`).","enum":["telegram","whatsapp","imessage","sms"],"uniqueKey":"error.channel","typeLabel":"enum\u003cstring>"},"conversation":{"type":"string","description":"A conversation ID, `conv_` and a ULID.","pattern":"^conv_[0-9A-HJKMNP-TV-Z]{26}$","examples":["conv_01JB8ZC3K5M7P9R1T3V5X7Z9B1"],"uniqueKey":"error.conversation","typeLabel":"string"},"sender":{"type":"string","description":"A sender ID, `snd_` and a ULID.","pattern":"^snd_[0-9A-HJKMNP-TV-Z]{26}$","examples":["snd_01JB8Z4Q3V6W0R2N7C5H1M9K4T"],"uniqueKey":"error.sender","typeLabel":"string"},"channel_code":{"type":"string","description":"For `channel_error`, the channel's own error code, as given by the\nchannel. For some other errors, Flow's own code naming the case, for\nexample `sandbox_allowance_used` (`permission`: the sandbox allowance is\nused up), `sandbox_contact_limit` (`permission`: the allowance has no\nroom for another contact), `sandbox_channel_not_included` (`permission`:\nthe allowance does not cover this sandbox channel), `sign_in_required`\n(`permission`: an app made without an account cannot do this) or\n`sandbox_key_expired` (`authentication`: a key from `POST\n/v0/sandbox/keys` passed its `expires_at`; `permission`, for a send\nthat carries no key, such as a reply in a webhook answer, from such an\napp). For `idempotency_conflict`, always one of `body_mismatch` (the\nkey was used for a different request), `in_progress` (the first\nrequest is still running; `retry_after` says when to repeat it) or\n`secret_not_kept` (its answer carried a secret shown only once).\nSMS: `contact_opted_out` and `consent_required` (`permission`),\n`quiet_hours` (`outside_window`, with `retry_after`), and\n`carrier_filtered`, `unreachable`, `invalid_number`, `landline`,\n`registration_required`, `carrier_error` and `provider_not_configured`\n(`channel_error`). HIPAA accounts: `hipaa_channel_not_covered`\n(`permission`). Connecting your own WhatsApp number (`POST\n/v0/senders`): `whatsapp_token_malformed`, `whatsapp_token_rejected`,\n`whatsapp_app_secret_malformed` and `whatsapp_number_not_in_account`\n(`invalid_request`), and `whatsapp_number_taken`,\n`whatsapp_account_taken` and `whatsapp_number_managed` (`permission`).\nSimulated conversations (`permission`): `test_mode_only`,\n`not_simulated`, `simulated_contact` and `simulated_conversation_limit`.\nFor a message that failed after it was queued (`message.failed`):\n`sandbox_contact_left` (`permission`), `window_closed` and\n`queued_too_long` (`outside_window`), and\n`channel_timeout_unknown_outcome` (`channel_error`: the channel never\nconfirmed the send, so it may or may not have arrived). iMessage\n(`unsupported_content`): `group_unsupported` (a reaction, typing or a\nread receipt in a group conversation; `param` `kind`) and\n`not_on_sms` (an edit or unsend of a message that went by SMS or RCS;\n`param` `kind`). SMS (`unsupported_content`): `no_mms` (media to a\nnumber without MMS; `param` `media`).\nEdits and unsends (`unsupported_content`): `edit_window_closed` and\n`unsend_window_closed` (the message is older than the channel allows\nfor the change) and `first_message_not_editable` (iMessage: the message\nthat started the conversation). From typing and\nread receipts, `unknown_outcome` (`channel_error`: the call timed out after\nit was sent).\n","uniqueKey":"error.channel_code","typeLabel":"string"},"request_id":{"type":"string","description":"Flow's ID for this request. Quote it when asking for help.","uniqueKey":"error.request_id","typeLabel":"string"}},"isRequired":true,"uniqueKey":"error","typeLabel":"object"}},"uniqueKey":"","typeLabel":"object"},"example":{"error":{"type":"new_contact_limit","message":"This sender has started its 15 new conversations for today.","hint":"Retry after 3600 seconds; replies into existing conversations still go.","doc_url":"https://api.flow.engineer/docs/errors/new_contact_limit","retry_after":3600,"sender":"snd_01JB8Z4Q3V6W0R2N7C5H1M9K4T"}},"examples":{"Example":{"title":"Example","value":{"error":{"type":"new_contact_limit","message":"This sender has started its 15 new conversations for today.","hint":"Retry after 3600 seconds; replies into existing conversations still go.","doc_url":"https://api.flow.engineer/docs/errors/new_contact_limit","retry_after":3600,"sender":"snd_01JB8Z4Q3V6W0R2N7C5H1M9K4T"}}}},"exampleType":"examples"}},"exampleType":"examples"},"b40cfab2-5741-4673-8985-65e58f8a4e17":{"description":"An error. `error.type` says which (see `ErrorType`).","content":{"application/json":{"schema":{"type":"object","description":"The body of every error answer.","required":["error"],"properties":{"error":{"type":"object","description":"The details of an error.","required":["type","message","hint","doc_url"],"properties":{"type":{"type":"string","description":"What went wrong. A closed list: new types arrive only with a new API version.\nEach type has a page at `https://api.flow.engineer/docs/errors/\u003ctype>`, given in\nthe error's `doc_url`; the error's `hint` says what to change for the case.\n\n- `invalid_request` (400, or 413 for a request body over the size limit): the request is malformed or a parameter is invalid. Fix the parameter named in `param`; the `hint` says what it must look like. Every endpoint answers it for a `Flow-Version` header that is not a version this API serves.\n- `authentication` (401): the API key is missing, malformed, unknown, revoked or expired. An `Authorization: Bearer` header with nothing after it usually means the environment variable holding the key was empty when the header was built (for an MCP client, when the server was added): the `hint` says so. Send `Authorization: Bearer fk_test_...` or `fk_live_...` with a current key; with no key at all, get a test key with `POST /v0/sandbox/keys`. A sandbox key past its `expires_at` has `channel_code` `sandbox_key_expired`: sign in to claim the app, or get a new key.\n- `permission` (403): the key may not do this, for example a test key using a live sender, a sandbox contact who joined another app, a new conversation from an iMessage line that may only reply, or a send past the app's sandbox allowance (`channel_code` `sandbox_allowance_used`, `sandbox_contact_limit` or `sandbox_channel_not_included`). Use the key of the right mode, have the contact send your join message, wait for the contact to message the line first, or sign in through the device flow to lift the allowance. On SMS: `contact_opted_out` (the contact texted STOP; nothing more goes to them until they text START), `consent_required` (a start to someone who never texted your numbers: record their consent with `consent` on the start). In a HIPAA account: `hipaa_channel_not_covered` (the channel has no business associate agreement; use SMS). Simulated conversations: `simulated_contact` (a real sender was pointed at a simulated contact; reply in the contact's simulated conversation), `not_simulated` (a simulated sender or test endpoint was pointed at a real contact), `test_mode_only`, `simulated_conversation_limit`. In `message.failed`: `sandbox_contact_left` (the contact joined another app on the sandbox sender while the message was queued; have them send your join message again).\n- `not_found` (404): no such object for this app and mode. Check the ID's prefix and that it was made with a key of the same mode (test and live data are separate).\n- `idempotency_conflict` (409): the idempotency key was used for a different request (`channel_code` `body_mismatch`: use a new key), or that request is still running (`in_progress`: wait `retry_after` seconds and repeat it with the same key), or it already created a secret that is shown only once (`secret_not_kept`: creating a webhook endpoint, rotating its secret, connecting a WhatsApp number; a secret that was lost must be rotated again, and a WhatsApp number connected again to see its verify token).\n- `outside_window` (409, or in `message.failed`): the channel will not deliver outside its conversation window. On WhatsApp the 24-hour window is closed: send a template (`POST /v0/conversations/{conversation_id}/template`, or `POST /v0/messages` with `kind` `template` to start), or wait for the contact to write. On iMessage the contact has not messaged the line (or opted in) yet, or not recently, so the failure arrives as a `message.failed` event: wait for the contact to write, then reply in that conversation. Typing and read receipts answer `409 outside_window` directly when the channel's window for them is closed (iMessage typing works only within 5 minutes of the contact's last message; WhatsApp read receipts go at any time); ignore it and send your reply. On SMS, `channel_code` `quiet_hours`: the contact has not texted in the last hour and it is inside the sender's quiet hours in their time zone; retry after `retry_after` seconds, or wait for them to text. In `message.failed`: `window_closed` (WhatsApp's 24-hour window closed while the message was queued; send a template) and `queued_too_long` (the message waited in the queue longer than its sender allows, for example while the sender was paused; send it again if it still applies).\n- `unsupported_content` (422): the conversation's channel does not take this kind, field, block or value, and nothing was sent; Flow never converts a message. `param` names what was refused: `kind` (the endpoint's kind is not on this channel, such as a carousel on Telegram), a field or block (`reply_to` on SMS, `telegram` on WhatsApp, `telegram.is_big`), or the field whose value is not taken (`emoji` outside Telegram's reaction emoji, `buttons` for more than 3 WhatsApp buttons). `channel` is the conversation's channel and `hint` says what to send instead over plain HTTP; each endpoint's page lists its channels. In an iMessage group conversation, a reaction, typing or a read receipt answers it with `param` `kind` and `channel_code` `group_unsupported`: leave it out. Media to an SMS number without MMS answers it with `param` `media` and `channel_code` `no_mms`. An edit or unsend of an iMessage message that went by SMS or RCS (its `network`) answers it with `channel_code` `not_on_sms`: send a new message instead. An edit or unsend past the channel's time limit answers it with `channel_code` `edit_window_closed` or `unsend_window_closed` (iMessage: edits within 15 minutes of sending, unsends within 2 minutes; Telegram: unsends within 48 hours), and an iMessage edit of the message that started the conversation with `first_message_not_editable`; `message` states the limit and how long ago the message was sent. Send a new message instead. `POST /v0/files` answers it for a type Flow does not take (programs, web pages, unknown types). A send by `file_id` of a document a contact sent before Flow malware-scanned documents answers it too: Flow sends only documents it scanned.\n- `new_contact_limit` (429): the sender has used its budget for starting conversations. Wait `retry_after` seconds; replies into existing conversations still go.\n- `sender_throttled` (429): abuse signals tripped (the same text to many new contacts, many starts with no reply, blocks), so the sender may not start conversations until `retry_after`; replies into existing conversations still go. Personalise first messages and start only conversations people expect.\n- `file_blocked` (422): the file failed the malware scan and was not stored. Send a different file. (When the scan cannot run at all, the upload answers a retryable `500 api_error` with `Retry-After` instead.)\n- `rate_limited` (429): either too many requests for this key (the per-key request limit, reported in the `RateLimit-*` headers) or sends faster than the sender's sending rate (pacing). Wait `Retry-After` (`retry_after`) seconds and retry with the same `Idempotency-Key`.\n- `channel_error` (502, or in `message.failed`): the channel refused or failed the message, or timed out; `channel_code` carries its own code. Read `message`, change what the channel objected to, and send again. From typing and read receipts, which call the channel at once, it is safe to ignore. SMS codes are Flow's, the same whichever carrier network carries the message: `carrier_filtered` (a carrier's spam filter blocked it: make it read like a person's message, no shortened links), `unreachable`, `invalid_number`, `landline`, `registration_required` (the number's 10DLC or toll-free registration is not approved yet), `carrier_error`, and `provider_not_configured` (the SMS sender is not fully set up on Flow's side, so nothing was sent; contact the Flow team). Flow's own codes on every channel: `channel_timeout_unknown_outcome` (in `message.failed`: the channel did not answer after the message was sent, and the resends its policy allows did not settle it, so the message may have arrived or not; check with the contact before sending it again) and `unknown_outcome` (from typing and read receipts: the call timed out after it was sent).\n- `not_implemented` (501): this endpoint or channel is not live yet during the beta. Use a channel that is live (Telegram, iMessage), or check the changelog.\n- `api_error` (500, 503): something went wrong on Flow's side. Retry with the same idempotency key after `retry_after` seconds, and quote `request_id` if it persists.\n","enum":["invalid_request","authentication","permission","not_found","idempotency_conflict","outside_window","unsupported_content","new_contact_limit","sender_throttled","file_blocked","rate_limited","channel_error","not_implemented","api_error"],"isRequired":true,"uniqueKey":"error.type","typeLabel":"enum\u003cstring>"},"message":{"type":"string","description":"A sentence for a developer saying what went wrong. Do not parse it; switch on `type`.","isRequired":true,"uniqueKey":"error.message","typeLabel":"string"},"hint":{"type":"string","description":"One sentence saying what to change to make the request succeed, specific\nto this case, for example \"Send a template instead: POST\n/v0/conversations/{conversation_id}/template.\" For `unsupported_content`\nit says what to send instead over plain HTTP. Written for developers and\ncoding agents alike. Do not parse it; it may be reworded at any time.\n","examples":["Send a template instead: POST /v0/conversations/{conversation_id}/template."],"isRequired":true,"uniqueKey":"error.hint","typeLabel":"string"},"doc_url":{"type":"string","format":"uri","description":"The documentation page for this error's type, `https://api.flow.engineer/docs/errors/\u003ctype>`. It says what the error means, why it happens and how to fix it, with code.","examples":["https://api.flow.engineer/docs/errors/outside_window"],"isRequired":true,"uniqueKey":"error.doc_url","typeLabel":"string\u003curi>"},"param":{"type":"string","description":"The request parameter the error is about, as a dotted path (`kind`, `telegram.is_big`, `buttons`, `cards[1].header.type`, `limit`).","uniqueKey":"error.param","typeLabel":"string"},"retry_after":{"type":"integer","minimum":0,"description":"Seconds to wait before retrying, for errors that clear by themselves.","uniqueKey":"error.retry_after","typeLabel":"integer"},"channel":{"type":"string","description":"The conversation's channel, on errors about what it takes (`unsupported_content`) and its windows (`outside_window`).","enum":["telegram","whatsapp","imessage","sms"],"uniqueKey":"error.channel","typeLabel":"enum\u003cstring>"},"conversation":{"type":"string","description":"A conversation ID, `conv_` and a ULID.","pattern":"^conv_[0-9A-HJKMNP-TV-Z]{26}$","examples":["conv_01JB8ZC3K5M7P9R1T3V5X7Z9B1"],"uniqueKey":"error.conversation","typeLabel":"string"},"sender":{"type":"string","description":"A sender ID, `snd_` and a ULID.","pattern":"^snd_[0-9A-HJKMNP-TV-Z]{26}$","examples":["snd_01JB8Z4Q3V6W0R2N7C5H1M9K4T"],"uniqueKey":"error.sender","typeLabel":"string"},"channel_code":{"type":"string","description":"For `channel_error`, the channel's own error code, as given by the\nchannel. For some other errors, Flow's own code naming the case, for\nexample `sandbox_allowance_used` (`permission`: the sandbox allowance is\nused up), `sandbox_contact_limit` (`permission`: the allowance has no\nroom for another contact), `sandbox_channel_not_included` (`permission`:\nthe allowance does not cover this sandbox channel), `sign_in_required`\n(`permission`: an app made without an account cannot do this) or\n`sandbox_key_expired` (`authentication`: a key from `POST\n/v0/sandbox/keys` passed its `expires_at`; `permission`, for a send\nthat carries no key, such as a reply in a webhook answer, from such an\napp). For `idempotency_conflict`, always one of `body_mismatch` (the\nkey was used for a different request), `in_progress` (the first\nrequest is still running; `retry_after` says when to repeat it) or\n`secret_not_kept` (its answer carried a secret shown only once).\nSMS: `contact_opted_out` and `consent_required` (`permission`),\n`quiet_hours` (`outside_window`, with `retry_after`), and\n`carrier_filtered`, `unreachable`, `invalid_number`, `landline`,\n`registration_required`, `carrier_error` and `provider_not_configured`\n(`channel_error`). HIPAA accounts: `hipaa_channel_not_covered`\n(`permission`). Connecting your own WhatsApp number (`POST\n/v0/senders`): `whatsapp_token_malformed`, `whatsapp_token_rejected`,\n`whatsapp_app_secret_malformed` and `whatsapp_number_not_in_account`\n(`invalid_request`), and `whatsapp_number_taken`,\n`whatsapp_account_taken` and `whatsapp_number_managed` (`permission`).\nSimulated conversations (`permission`): `test_mode_only`,\n`not_simulated`, `simulated_contact` and `simulated_conversation_limit`.\nFor a message that failed after it was queued (`message.failed`):\n`sandbox_contact_left` (`permission`), `window_closed` and\n`queued_too_long` (`outside_window`), and\n`channel_timeout_unknown_outcome` (`channel_error`: the channel never\nconfirmed the send, so it may or may not have arrived). iMessage\n(`unsupported_content`): `group_unsupported` (a reaction, typing or a\nread receipt in a group conversation; `param` `kind`) and\n`not_on_sms` (an edit or unsend of a message that went by SMS or RCS;\n`param` `kind`). SMS (`unsupported_content`): `no_mms` (media to a\nnumber without MMS; `param` `media`).\nEdits and unsends (`unsupported_content`): `edit_window_closed` and\n`unsend_window_closed` (the message is older than the channel allows\nfor the change) and `first_message_not_editable` (iMessage: the message\nthat started the conversation). From typing and\nread receipts, `unknown_outcome` (`channel_error`: the call timed out after\nit was sent).\n","uniqueKey":"error.channel_code","typeLabel":"string"},"request_id":{"type":"string","description":"Flow's ID for this request. Quote it when asking for help.","uniqueKey":"error.request_id","typeLabel":"string"}},"isRequired":true,"uniqueKey":"error","typeLabel":"object"}},"uniqueKey":"","typeLabel":"object"},"examples":{"Example":{"title":"Example","value":{"error":{"type":"invalid_request","message":"\u003cstring>","hint":"Send a template instead: POST /v0/conversations/{conversation_id}/template.","doc_url":"https://api.flow.engineer/docs/errors/outside_window","param":"\u003cstring>","retry_after":1,"channel":"telegram","conversation":"conv_01JB8ZC3K5M7P9R1T3V5X7Z9B1","sender":"snd_01JB8Z4Q3V6W0R2N7C5H1M9K4T","channel_code":"\u003cstring>","request_id":"\u003cstring>"}}}},"exampleType":"generated"}},"exampleType":"generated"}},"security":{"59c5f960-0d19-49e7-944f-bfe75a0ffe7d":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"fk_test_... or fk_live_...","description":"An API key of one app, sent as `Authorization: Bearer \u003ckey>`. Keys start with\n`fk_test_` (test mode: sandbox senders and test data only) or `fk_live_`\n(live mode). Keep live keys on your server; never ship them in an app or page.\n","scopes":[],"schemeName":"bearerAuth"}}},"servers":{"90a9657d-20c1-4182-87ad-a4d557fcda08":{"url":"https://api.flow.engineer","description":"Production. Test and live mode are chosen by the API key."}},"processedSecurityOptions":[{"schemes":[{"scheme":{"type":"http","scheme":"bearer","bearerFormat":"fk_test_... or fk_live_...","description":"An API key of one app, sent as `Authorization: Bearer \u003ckey>`. Keys start with\n`fk_test_` (test mode: sandbox senders and test data only) or `fk_live_`\n(live mode). Keep live keys on your server; never ship them in an app or page.\n","scopes":[],"schemeName":"bearerAuth"},"schema":{"title":"Bearer Auth","type":"string","isRequired":true,"description":"An API key of one app, sent as `Authorization: Bearer \u003ckey>`. Keys start with\n`fk_test_` (test mode: sandbox senders and test data only) or `fk_live_`\n(live mode). Keep live keys on your server; never ship them in an app or page.\n","format":"bearer","typeLabel":"string\u003cbearer>","deprecated":false},"schemeName":"bearerAuth","name":"Authorization"}],"title":"bearerAuth"}]},"originalFileLocation":"unified-messaging-api/openapi.yaml"},"subdomain":"flow-messaging","siteName":"Flow","title":"Write as the simulated contact - Flow","navMaps":{"firstHrefInVersion":{},"firstHrefInLanguage":{}},"lastModified":"2026-10-24T16:17:30.743Z","originalFileLocation":"api-reference/testing/write-as-the-simulated-contact.mdx"},"headHtml":"\u003cscript data-mint-head>(function(){var d=document.documentElement,p=null;if(!p){try{p=localStorage.getItem(\"isDarkMode\")}catch(e){}if(p==='true')p='dark';else if(p==='false')p='light';if(p!=='light'\u0026\u0026p!=='dark'\u0026\u0026p!=='system')p=\"system\"}var r=p==='system'?(matchMedia(\"(prefers-color-scheme: dark)\").matches?'dark':'light'):p;d.classList.remove(r==='dark'?'light':'dark');d.classList.add(r);d.setAttribute(\"data-theme-preference\",p)})();\u003c/script>\u003cmeta data-mint-head name=\"description\" content=\"Test keys only. Posts one inbound turn from the simulated contact of a simulated conversation. It goes through the same inbound path as a real channel\u0026#x27;s message: Flow stores it, then delivers it as a message.received event by webhook, on the stream and in the event log, in order with the conversation\u0026#x27;s other events. A webhook answer\u0026#x27;s reply works as usual. The answer comes before delivery; the message.received event carries the message ID given here. content is what the contact sends, as a kind object with kind: basic (text and/or media), voice, location, contact_card or button_reply (a tap on a reply button of a buttons or carousel message your agent sent, or on a row of a list, given as its message_id and button_id: Flow fills in the label). Media and voice take the file_id of a file uploaded with POST /v0/files (no url); the contact\u0026#x27;s message refers to that same file (its file_id, url, mime_type and size_bytes): Flow does not copy it again. The channel\u0026#x27;s rules apply: what it cannot carry from a person (a document over SMS, a location over SMS, a file larger than the channel fetches from a person) answers 422 unsupported_content, and text over the channel\u0026#x27;s limit answers 400 invalid_request. On SMS, the keywords work as from a real phone, matched against the whole text. STOP (or STOPALL, UNSUBSCRIBE, CANCEL, END, QUIT, REVOKE, OPT OUT, OPTOUT) opts the simulated contact out against the simulated sender: the contact\u0026#x27;s opted_out_at is set and opted_out_via is keyword, and sends to them answer 403 permission with channel_code contact_opted_out. START (or UNSTOP, YES) lifts it. The turn is still delivered as message.received. A keyword reply is never recorded as a message, so the transcript shows no automatic reply to STOP, START or HELP. On WhatsApp, each turn opens the conversation\u0026#x27;s 24-hour window, as a real contact\u0026#x27;s message does. A conversation that is not simulated answers 403 permission with channel_code not_simulated; a live key answers 403 permission with channel_code test_mode_only. In a HIPAA account (Account.hipaa), a turn on a simulated telegram, whatsapp or imessage conversation (one made before the flag was set) answers 403 permission with channel_code hipaa_channel_not_covered (param conversation_id).\">\u003cmeta data-mint-head name=\"application-name\" content=\"Flow\">\u003cmeta data-mint-head name=\"generator\" content=\"Mintlify\">\u003clink data-mint-head rel=\"apple-touch-icon\" type=\"image/png\" sizes=\"180x180\" href=\"https://mintlify.s3.us-west-1.amazonaws.com/flow-messaging/_generated/favicon/apple-touch-icon.png?v=3\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"https://mintlify.s3.us-west-1.amazonaws.com/flow-messaging/_generated/favicon/favicon-32x32.png?v=3\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"https://mintlify.s3.us-west-1.amazonaws.com/flow-messaging/_generated/favicon/favicon-16x16.png?v=3\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"https://mintlify.s3.us-west-1.amazonaws.com/flow-messaging/_generated/favicon/favicon.ico?v=3\">\u003cmeta data-mint-head property=\"og:title\" content=\"Write as the simulated contact - Flow\">\u003cmeta data-mint-head property=\"og:description\" content=\"Test keys only. Posts one inbound turn from the simulated contact of a simulated conversation. It goes through the same inbound path as a real channel\u0026#x27;s message: Flow stores it, then delivers it as a message.received event by webhook, on the stream and in the event log, in order with the conversation\u0026#x27;s other events. A webhook answer\u0026#x27;s reply works as usual. The answer comes before delivery; the message.received event carries the message ID given here. content is what the contact sends, as a kind object with kind: basic (text and/or media), voice, location, contact_card or button_reply (a tap on a reply button of a buttons or carousel message your agent sent, or on a row of a list, given as its message_id and button_id: Flow fills in the label). Media and voice take the file_id of a file uploaded with POST /v0/files (no url); the contact\u0026#x27;s message refers to that same file (its file_id, url, mime_type and size_bytes): Flow does not copy it again. The channel\u0026#x27;s rules apply: what it cannot carry from a person (a document over SMS, a location over SMS, a file larger than the channel fetches from a person) answers 422 unsupported_content, and text over the channel\u0026#x27;s limit answers 400 invalid_request. On SMS, the keywords work as from a real phone, matched against the whole text. STOP (or STOPALL, UNSUBSCRIBE, CANCEL, END, QUIT, REVOKE, OPT OUT, OPTOUT) opts the simulated contact out against the simulated sender: the contact\u0026#x27;s opted_out_at is set and opted_out_via is keyword, and sends to them answer 403 permission with channel_code contact_opted_out. START (or UNSTOP, YES) lifts it. The turn is still delivered as message.received. A keyword reply is never recorded as a message, so the transcript shows no automatic reply to STOP, START or HELP. On WhatsApp, each turn opens the conversation\u0026#x27;s 24-hour window, as a real contact\u0026#x27;s message does. A conversation that is not simulated answers 403 permission with channel_code not_simulated; a live key answers 403 permission with channel_code test_mode_only. In a HIPAA account (Account.hipaa), a turn on a simulated telegram, whatsapp or imessage conversation (one made before the flag was set) answers 403 permission with channel_code hipaa_channel_not_covered (param conversation_id).\">\u003cmeta data-mint-head property=\"og:site_name\" content=\"Flow\">\u003cmeta data-mint-head property=\"og:url\" content=\"https://docs.flow.engineer/api-reference/testing/write-as-the-simulated-contact\">\u003cmeta data-mint-head property=\"og:type\" content=\"website\">\u003cmeta data-mint-head property=\"og:image\" content=\"https://flow-messaging.mintlify.app/_mintlify/api/og/v2?division=Simulated+conversations\u0026amp;title=Write+as+the+simulated+contact\u0026amp;description=Test+keys+only.+Posts+one+inbound+turn+from+the+simulated+contact+of+a%0Asimulated+conversation.+It+goes+through+the+same+inbound+path+as+a+real%0Achannel%27s+message\u0026amp;theme=052dec2df14a3094abf38032\u0026amp;signature=6cb7e5994b56254832b49b2b2b0f891926388e7b1d7cf040b9911870ca69fd8e\">\u003cmeta data-mint-head property=\"og:image:width\" content=\"1200\">\u003cmeta data-mint-head property=\"og:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"twitter:card\" content=\"summary_large_image\">\u003cmeta data-mint-head name=\"twitter:title\" content=\"Write as the simulated contact - Flow\">\u003cmeta data-mint-head name=\"twitter:description\" content=\"Test keys only. Posts one inbound turn from the simulated contact of a simulated conversation. It goes through the same inbound path as a real channel\u0026#x27;s message: Flow stores it, then delivers it as a message.received event by webhook, on the stream and in the event log, in order with the conversation\u0026#x27;s other events. A webhook answer\u0026#x27;s reply works as usual. The answer comes before delivery; the message.received event carries the message ID given here. content is what the contact sends, as a kind object with kind: basic (text and/or media), voice, location, contact_card or button_reply (a tap on a reply button of a buttons or carousel message your agent sent, or on a row of a list, given as its message_id and button_id: Flow fills in the label). Media and voice take the file_id of a file uploaded with POST /v0/files (no url); the contact\u0026#x27;s message refers to that same file (its file_id, url, mime_type and size_bytes): Flow does not copy it again. The channel\u0026#x27;s rules apply: what it cannot carry from a person (a document over SMS, a location over SMS, a file larger than the channel fetches from a person) answers 422 unsupported_content, and text over the channel\u0026#x27;s limit answers 400 invalid_request. On SMS, the keywords work as from a real phone, matched against the whole text. STOP (or STOPALL, UNSUBSCRIBE, CANCEL, END, QUIT, REVOKE, OPT OUT, OPTOUT) opts the simulated contact out against the simulated sender: the contact\u0026#x27;s opted_out_at is set and opted_out_via is keyword, and sends to them answer 403 permission with channel_code contact_opted_out. START (or UNSTOP, YES) lifts it. The turn is still delivered as message.received. A keyword reply is never recorded as a message, so the transcript shows no automatic reply to STOP, START or HELP. On WhatsApp, each turn opens the conversation\u0026#x27;s 24-hour window, as a real contact\u0026#x27;s message does. A conversation that is not simulated answers 403 permission with channel_code not_simulated; a live key answers 403 permission with channel_code test_mode_only. In a HIPAA account (Account.hipaa), a turn on a simulated telegram, whatsapp or imessage conversation (one made before the flag was set) answers 403 permission with channel_code hipaa_channel_not_covered (param conversation_id).\">\u003cmeta data-mint-head name=\"twitter:image\" content=\"https://flow-messaging.mintlify.app/_mintlify/api/og/v2?division=Simulated+conversations\u0026amp;title=Write+as+the+simulated+contact\u0026amp;description=Test+keys+only.+Posts+one+inbound+turn+from+the+simulated+contact+of+a%0Asimulated+conversation.+It+goes+through+the+same+inbound+path+as+a+real%0Achannel%27s+message\u0026amp;theme=052dec2df14a3094abf38032\u0026amp;signature=6cb7e5994b56254832b49b2b2b0f891926388e7b1d7cf040b9911870ca69fd8e\">\u003cmeta data-mint-head name=\"twitter:image:width\" content=\"1200\">\u003cmeta data-mint-head name=\"twitter:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"msapplication-config\" content=\"https://mintlify.s3.us-west-1.amazonaws.com/flow-messaging/_generated/favicon/browserconfig.xml?v=3\">\u003cmeta data-mint-head name=\"apple-mobile-web-app-title\" content=\"Flow\">\u003cmeta data-mint-head name=\"msapplication-TileColor\" content=\"#0b7a5a\">\u003clink data-mint-head rel=\"canonical\" href=\"https://docs.flow.engineer/api-reference/testing/write-as-the-simulated-contact\">\u003clink data-mint-head rel=\"alternate\" type=\"application/xml\" href=\"/sitemap.xml\">\u003clink data-mint-head rel=\"alternate\" type=\"text/markdown\" href=\"/api-reference/testing/write-as-the-simulated-contact.md\">\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@graph\":[{\"@type\":\"Organization\",\"@id\":\"https://docs.flow.engineer/#organization\",\"name\":\"Flow\",\"url\":\"https://docs.flow.engineer\"},{\"@type\":\"WebSite\",\"@id\":\"https://docs.flow.engineer#website\",\"name\":\"Flow\",\"url\":\"https://docs.flow.engineer\",\"publisher\":{\"@id\":\"https://docs.flow.engineer/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https://docs.flow.engineer/api-reference/testing/write-as-the-simulated-contact#webpage\",\"url\":\"https://docs.flow.engineer/api-reference/testing/write-as-the-simulated-contact\",\"name\":\"Write as the simulated contact\",\"description\":\"Test keys only. Posts one inbound turn from the simulated contact of a simulated conversation. It goes through the same inbound path as a real channel's message: Flow stores it, then delivers it as a message.received event by webhook, on the stream and in the event log, in order with the conversation's other events. A webhook answer's reply works as usual. The answer comes before delivery; the message.received event carries the message ID given here. content is what the contact sends, as a kind object with kind: basic (text and/or media), voice, location, contact_card or button_reply (a tap on a reply button of a buttons or carousel message your agent sent, or on a row of a list, given as its message_id and button_id: Flow fills in the label). Media and voice take the file_id of a file uploaded with POST /v0/files (no url); the contact's message refers to that same file (its file_id, url, mime_type and size_bytes): Flow does not copy it again. The channel's rules apply: what it cannot carry from a person (a document over SMS, a location over SMS, a file larger than the channel fetches from a person) answers 422 unsupported_content, and text over the channel's limit answers 400 invalid_request. On SMS, the keywords work as from a real phone, matched against the whole text. STOP (or STOPALL, UNSUBSCRIBE, CANCEL, END, QUIT, REVOKE, OPT OUT, OPTOUT) opts the simulated contact out against the simulated sender: the contact's opted_out_at is set and opted_out_via is keyword, and sends to them answer 403 permission with channel_code contact_opted_out. START (or UNSTOP, YES) lifts it. The turn is still delivered as message.received. A keyword reply is never recorded as a message, so the transcript shows no automatic reply to STOP, START or HELP. On WhatsApp, each turn opens the conversation's 24-hour window, as a real contact's message does. A conversation that is not simulated answers 403 permission with channel_code not_simulated; a live key answers 403 permission with channel_code test_mode_only. In a HIPAA account (Account.hipaa), a turn on a simulated telegram, whatsapp or imessage conversation (one made before the flag was set) answers 403 permission with channel_code hipaa_channel_not_covered (param conversation_id).\",\"dateModified\":\"2026-10-24T16:17:30.743Z\",\"isPartOf\":{\"@id\":\"https://docs.flow.engineer#website\"},\"breadcrumb\":{\"@id\":\"https://docs.flow.engineer/api-reference/testing/write-as-the-simulated-contact#breadcrumb\"}},{\"@type\":\"BreadcrumbList\",\"@id\":\"https://docs.flow.engineer/api-reference/testing/write-as-the-simulated-contact#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"HTTP API\",\"item\":\"https://docs.flow.engineer/unified-messaging-api/api-reference/introduction\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Simulated conversations\",\"item\":\"https://docs.flow.engineer/api-reference/testing/create-a-simulated-conversation\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Write as the simulated contact\",\"item\":\"https://docs.flow.engineer/api-reference/testing/write-as-the-simulated-contact\"}]},{\"@type\":[\"TechArticle\",\"APIReference\"],\"@id\":\"https://docs.flow.engineer/api-reference/testing/write-as-the-simulated-contact#apireference\",\"headline\":\"Write as the simulated contact\",\"name\":\"Write as the simulated contact\",\"description\":\"Test keys only. Posts one inbound turn from the simulated contact of a simulated conversation. It goes through the same inbound path as a real channel's message: Flow stores it, then delivers it as a message.received event by webhook, on the stream and in the event log, in order with the conversation's other events. A webhook answer's reply works as usual. The answer comes before delivery; the message.received event carries the message ID given here. content is what the contact sends, as a kind object with kind: basic (text and/or media), voice, location, contact_card or button_reply (a tap on a reply button of a buttons or carousel message your agent sent, or on a row of a list, given as its message_id and button_id: Flow fills in the label). Media and voice take the file_id of a file uploaded with POST /v0/files (no url); the contact's message refers to that same file (its file_id, url, mime_type and size_bytes): Flow does not copy it again. The channel's rules apply: what it cannot carry from a person (a document over SMS, a location over SMS, a file larger than the channel fetches from a person) answers 422 unsupported_content, and text over the channel's limit answers 400 invalid_request. On SMS, the keywords work as from a real phone, matched against the whole text. STOP (or STOPALL, UNSUBSCRIBE, CANCEL, END, QUIT, REVOKE, OPT OUT, OPTOUT) opts the simulated contact out against the simulated sender: the contact's opted_out_at is set and opted_out_via is keyword, and sends to them answer 403 permission with channel_code contact_opted_out. START (or UNSTOP, YES) lifts it. The turn is still delivered as message.received. A keyword reply is never recorded as a message, so the transcript shows no automatic reply to STOP, START or HELP. On WhatsApp, each turn opens the conversation's 24-hour window, as a real contact's message does. A conversation that is not simulated answers 403 permission with channel_code not_simulated; a live key answers 403 permission with channel_code test_mode_only. In a HIPAA account (Account.hipaa), a turn on a simulated telegram, whatsapp or imessage conversation (one made before the flag was set) answers 403 permission with channel_code hipaa_channel_not_covered (param conversation_id).\",\"url\":\"https://docs.flow.engineer/api-reference/testing/write-as-the-simulated-contact\",\"mainEntityOfPage\":{\"@id\":\"https://docs.flow.engineer/api-reference/testing/write-as-the-simulated-contact#webpage\"},\"dateModified\":\"2026-10-24T16:17:30.743Z\",\"publisher\":{\"@id\":\"https://docs.flow.engineer/#organization\"},\"isPartOf\":{\"@id\":\"https://docs.flow.engineer#website\"}}]}\u003c/script>\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@type\":\"WebSite\",\"name\":\"Flow\",\"creator\":{\"@type\":\"Organization\",\"name\":\"Mintlify\",\"url\":\"https://mintlify.com\"}}\u003c/script>","siteName":"Flow","nav":{"global":null,"tabs":[{"tab":"Framework","pages":[{"title":"Framework","href":"/framework"}]},{"tab":"Messaging API","groups":[{"group":"Get started","pages":[{"title":"Unified Messaging API","sidebarTitle":"Introduction","icon":"comments","href":"/unified-messaging-api"},{"title":"Send your first reply","sidebarTitle":"Quickstart","icon":"rocket","href":"/unified-messaging-api/quickstart"},{"title":"Keys and sign-in","sidebarTitle":"Keys \u0026 sign-in","icon":"key","href":"/unified-messaging-api/get-a-key"},{"title":"Build with a coding agent","sidebarTitle":"For coding agents","icon":"robot","href":"/unified-messaging-api/coding-agents"},{"title":"Build for real conversations","sidebarTitle":"Real conversations","icon":"users","href":"/unified-messaging-api/guides/real-people"},{"title":"Messaging MCP server","sidebarTitle":"Development MCP","icon":"plug","href":"/unified-messaging-api/mcp"},{"title":"Pricing and allowances","sidebarTitle":"Pricing","icon":"credit-card","href":"/unified-messaging-api/pricing"}]},{"group":"Observe","pages":[{"title":"Observe conversations","sidebarTitle":"Overview","icon":"eye","href":"/unified-messaging-api/observe/index"},{"title":"Connect Messaging to Observe","sidebarTitle":"Messaging integration","icon":"comments","href":"/unified-messaging-api/observe/flow-messaging"},{"title":"Send traces to Observe","sidebarTitle":"Traces","icon":"chart-line","href":"/unified-messaging-api/observe/traces"},{"title":"Trace your agent framework","sidebarTitle":"Framework integrations","icon":"diagram-project","href":"/unified-messaging-api/observe/frameworks"}]},{"group":"Test before you ship","pages":[{"title":"Test your integration","sidebarTitle":"Testing","icon":"flask","href":"/unified-messaging-api/trust"}]},{"group":"Concepts","pages":[{"title":"Connect a sender","sidebarTitle":"Senders","icon":"phone","href":"/unified-messaging-api/concepts/senders"},{"title":"Conversations and contacts","sidebarTitle":"Conversations","icon":"comments","href":"/unified-messaging-api/concepts/conversations"},{"title":"Send text, media and buttons","sidebarTitle":"Message kinds","icon":"shapes","href":"/unified-messaging-api/concepts/content-types"},{"title":"Receive events","sidebarTitle":"Events \u0026 webhooks","icon":"webhook","href":"/unified-messaging-api/concepts/events-and-webhooks"},{"title":"Stream replies","sidebarTitle":"Streaming replies","icon":"water","href":"/unified-messaging-api/concepts/streaming-replies"},{"title":"Sending rules","sidebarTitle":"Sending rules","icon":"shield-check","href":"/unified-messaging-api/concepts/send-gate"},{"title":"Test and live mode","sidebarTitle":"Test \u0026 live mode","icon":"toggle-on","href":"/unified-messaging-api/concepts/test-and-live-mode"},{"title":"Retry without duplicate sends","sidebarTitle":"Idempotency","icon":"repeat","href":"/unified-messaging-api/concepts/idempotency"},{"title":"Retention and deletion","sidebarTitle":"Retention \u0026 deletion","icon":"database","href":"/unified-messaging-api/concepts/data-retention"},{"title":"API versions","sidebarTitle":"Versioning","icon":"code-branch","href":"/unified-messaging-api/concepts/versioning"},{"title":"Rate limits","sidebarTitle":"Rate limits","icon":"gauge","href":"/unified-messaging-api/concepts/rate-limits"}]},{"group":"Channel guides","pages":[{"title":"Telegram","sidebarTitle":"Telegram","icon":"telegram","href":"/unified-messaging-api/guides/telegram-agent"},{"title":"iMessage","sidebarTitle":"iMessage","icon":"message","href":"/unified-messaging-api/guides/imessage-agent"},{"title":"WhatsApp","sidebarTitle":"WhatsApp","icon":"whatsapp","href":"/unified-messaging-api/guides/whatsapp-agent"},{"title":"Local development","sidebarTitle":"Local development","icon":"laptop-code","href":"/unified-messaging-api/guides/local-development"},{"title":"Go live","sidebarTitle":"Going live","icon":"flag-checkered","href":"/unified-messaging-api/guides/going-live"},{"title":"Messaging recipes","sidebarTitle":"Recipes","icon":"book-open","href":"/unified-messaging-api/guides/recipes"}]},{"group":"Agent frameworks","pages":[{"title":"Vercel AI SDK","sidebarTitle":"Vercel AI SDK","icon":"code","href":"/unified-messaging-api/frameworks/vercel-ai-sdk"},{"title":"OpenAI Agents SDK","sidebarTitle":"OpenAI Agents SDK","icon":"code","href":"/unified-messaging-api/frameworks/openai-agents-sdk"},{"title":"Claude Agent SDK","sidebarTitle":"Claude Agent SDK","icon":"code","href":"/unified-messaging-api/frameworks/claude-agent-sdk"},{"title":"LangChain","sidebarTitle":"LangChain","icon":"link","href":"/unified-messaging-api/frameworks/langchain"},{"title":"Mastra","sidebarTitle":"Mastra","icon":"code","href":"/unified-messaging-api/frameworks/mastra"}]},{"group":"Compare","pages":[{"title":"Choose a messaging API","sidebarTitle":"Choose an API","icon":"scale-balanced","href":"/unified-messaging-api/compare/choosing-a-messaging-api"},{"title":"Flow and Photon","sidebarTitle":"Flow vs Photon","icon":"scale-balanced","href":"/unified-messaging-api/compare/flow-vs-photon"}]},{"group":"HTTP API","pages":[{"group":"Overview","expanded":false,"pages":[{"title":"HTTP API","sidebarTitle":"API overview","icon":"code","href":"/unified-messaging-api/api-reference/introduction"}]},{"group":"Messages","expanded":false,"pages":[{"openapi":"unified-messaging-api/openapi.yaml POST /v0/conversations/{conversation_id}/basic","title":"Send a basic message (text, or media with a caption)","href":"/api-reference/messages/send-a-basic-message-text-or-media-with-a-caption"},{"openapi":"unified-messaging-api/openapi.yaml POST /v0/conversations/{conversation_id}/voice","title":"Send a voice note","href":"/api-reference/messages/send-a-voice-note"},{"openapi":"unified-messaging-api/openapi.yaml POST /v0/conversations/{conversation_id}/buttons","title":"Send text with buttons","href":"/api-reference/messages/send-text-with-buttons"},{"openapi":"unified-messaging-api/openapi.yaml POST /v0/conversations/{conversation_id}/list","title":"Send a list to pick one option from","href":"/api-reference/messages/send-a-list-to-pick-one-option-from"},{"openapi":"unified-messaging-api/openapi.yaml POST /v0/conversations/{conversation_id}/carousel","title":"Send a carousel of cards","href":"/api-reference/messages/send-a-carousel-of-cards"},{"openapi":"unified-messaging-api/openapi.yaml POST /v0/conversations/{conversation_id}/template","title":"Send a WhatsApp template","href":"/api-reference/messages/send-a-whatsapp-template"},{"openapi":"unified-messaging-api/openapi.yaml POST /v0/conversations/{conversation_id}/contact-card","title":"Send a contact card","href":"/api-reference/messages/send-a-contact-card"},{"openapi":"unified-messaging-api/openapi.yaml POST /v0/conversations/{conversation_id}/location","title":"Send a place","href":"/api-reference/messages/send-a-place"},{"openapi":"unified-messaging-api/openapi.yaml POST /v0/conversations/{conversation_id}/effect","title":"Send text with an iMessage effect","href":"/api-reference/messages/send-text-with-an-imessage-effect"},{"openapi":"unified-messaging-api/openapi.yaml POST /v0/conversations/{conversation_id}/reaction","title":"React to a message","href":"/api-reference/messages/react-to-a-message"},{"openapi":"unified-messaging-api/openapi.yaml POST /v0/messages","title":"Start a conversation","href":"/api-reference/messages/start-a-conversation"},{"openapi":"unified-messaging-api/openapi.yaml GET /v0/messages/{message_id}","title":"Get a message","href":"/api-reference/messages/get-a-message"},{"openapi":"unified-messaging-api/openapi.yaml PATCH /v0/messages/{message_id}","title":"Edit a sent message","href":"/api-reference/messages/edit-a-sent-message"},{"openapi":"unified-messaging-api/openapi.yaml DELETE /v0/messages/{message_id}","title":"Unsend a message","href":"/api-reference/messages/unsend-a-message"}]},{"group":"Conversations","expanded":false,"pages":[{"openapi":"unified-messaging-api/openapi.yaml GET /v0/conversations","title":"List conversations","href":"/api-reference/conversations/list-conversations"},{"openapi":"unified-messaging-api/openapi.yaml GET /v0/conversations/{conversation_id}","title":"Get a conversation","href":"/api-reference/conversations/get-a-conversation"},{"openapi":"unified-messaging-api/openapi.yaml GET /v0/conversations/{conversation_id}/messages","title":"List a conversation's messages","href":"/api-reference/conversations/list-a-conversations-messages"},{"openapi":"unified-messaging-api/openapi.yaml POST /v0/conversations/{conversation_id}/typing","title":"Show the typing indicator","href":"/api-reference/conversations/show-the-typing-indicator"},{"openapi":"unified-messaging-api/openapi.yaml POST /v0/conversations/{conversation_id}/read","title":"Mark messages as read","href":"/api-reference/conversations/mark-messages-as-read"}]},{"group":"Events","expanded":false,"pages":[{"openapi":"unified-messaging-api/openapi.yaml GET /v0/events","title":"List events","href":"/api-reference/events/list-events"},{"openapi":"unified-messaging-api/openapi.yaml GET /v0/events/{event_id}","title":"Get an event","href":"/api-reference/events/get-an-event"},{"openapi":"unified-messaging-api/openapi.yaml GET /v0/stream","title":"Open the live event stream (WebSocket)","href":"/api-reference/stream/open-the-live-event-stream-websocket"},{"openapi":"unified-messaging-api/openapi.yaml webhook event","title":"An event delivered to your endpoint","href":"/api-reference/webhook-endpoints/an-event-delivered-to-your-endpoint"}]},{"group":"Webhook endpoints","expanded":false,"pages":[{"openapi":"unified-messaging-api/openapi.yaml GET /v0/webhook_endpoints","title":"List webhook endpoints","href":"/api-reference/webhook-endpoints/list-webhook-endpoints"},{"openapi":"unified-messaging-api/openapi.yaml POST /v0/webhook_endpoints","title":"Create a webhook endpoint","href":"/api-reference/webhook-endpoints/create-a-webhook-endpoint"},{"openapi":"unified-messaging-api/openapi.yaml GET /v0/webhook_endpoints/{webhook_endpoint_id}","title":"Get a webhook endpoint","href":"/api-reference/webhook-endpoints/get-a-webhook-endpoint"},{"openapi":"unified-messaging-api/openapi.yaml PATCH /v0/webhook_endpoints/{webhook_endpoint_id}","title":"Update a webhook endpoint","href":"/api-reference/webhook-endpoints/update-a-webhook-endpoint"},{"openapi":"unified-messaging-api/openapi.yaml DELETE /v0/webhook_endpoints/{webhook_endpoint_id}","title":"Delete a webhook endpoint","href":"/api-reference/webhook-endpoints/delete-a-webhook-endpoint"},{"openapi":"unified-messaging-api/openapi.yaml POST /v0/webhook_endpoints/{webhook_endpoint_id}/rotate_secret","title":"Rotate a webhook endpoint's signing secret","href":"/api-reference/webhook-endpoints/rotate-a-webhook-endpoints-signing-secret"}]},{"group":"Files","expanded":false,"pages":[{"openapi":"unified-messaging-api/openapi.yaml POST /v0/files","title":"Upload a file","href":"/api-reference/files/upload-a-file"},{"openapi":"unified-messaging-api/openapi.yaml GET /v0/files/{file_id}","title":"Download a file","href":"/api-reference/files/download-a-file"}]},{"group":"Senders and templates","expanded":false,"pages":[{"openapi":"unified-messaging-api/openapi.yaml GET /v0/senders","title":"List senders","href":"/api-reference/senders/list-senders"},{"openapi":"unified-messaging-api/openapi.yaml POST /v0/senders","title":"Request a dedicated sender","href":"/api-reference/senders/request-a-dedicated-sender"},{"openapi":"unified-messaging-api/openapi.yaml GET /v0/senders/{sender_id}","title":"Get a sender","href":"/api-reference/senders/get-a-sender"},{"openapi":"unified-messaging-api/openapi.yaml GET /v0/templates","title":"List templates","href":"/api-reference/templates/list-templates"},{"openapi":"unified-messaging-api/openapi.yaml POST /v0/templates","title":"Create a template","href":"/api-reference/templates/create-a-template"},{"openapi":"unified-messaging-api/openapi.yaml GET /v0/templates/{template_id}","title":"Get a template","href":"/api-reference/templates/get-a-template"},{"openapi":"unified-messaging-api/openapi.yaml DELETE /v0/templates/{template_id}","title":"Delete a template","href":"/api-reference/templates/delete-a-template"},{"openapi":"unified-messaging-api/openapi.yaml DELETE /v0/senders/{sender_id}","title":"Disconnect a sender","href":"/api-reference/senders/disconnect-a-sender"}]},{"group":"Contacts and app","expanded":false,"pages":[{"openapi":"unified-messaging-api/openapi.yaml GET /v0/contacts","title":"List contacts","href":"/api-reference/contacts/list-contacts"},{"openapi":"unified-messaging-api/openapi.yaml GET /v0/contacts/{contact_id}","title":"Get a contact","href":"/api-reference/contacts/get-a-contact"},{"openapi":"unified-messaging-api/openapi.yaml GET /v0/app","title":"Get the calling app","href":"/api-reference/app/get-the-calling-app"},{"openapi":"unified-messaging-api/openapi.yaml DELETE /v0/contacts/{contact_id}","title":"Delete a contact and their data","href":"/api-reference/contacts/delete-a-contact-and-their-data"}]},{"group":"Onboarding","expanded":false,"pages":[{"openapi":"unified-messaging-api/openapi.yaml POST /v0/sandbox/keys","title":"Get a test key without an account","href":"/api-reference/onboarding/get-a-test-key-without-an-account"},{"openapi":"unified-messaging-api/openapi.yaml POST /v0/device/authorizations","title":"Start a sign-in from an agent or CLI (device flow)","href":"/api-reference/onboarding/start-a-sign-in-from-an-agent-or-cli-device-flow"},{"openapi":"unified-messaging-api/openapi.yaml POST /v0/device/token","title":"Poll a device sign-in for its key","href":"/api-reference/onboarding/poll-a-device-sign-in-for-its-key"}]},{"group":"Simulated conversations","expanded":false,"pages":[{"openapi":"unified-messaging-api/openapi.yaml POST /v0/test/conversations","title":"Create a simulated conversation","description":"Test keys only. Creates a conversation with a **simulated contact**, an end\nuser that exists only inside Flow, so an agent or a test suite can play the\nperson's side with no phone and no real channel. Write as the contact with\n`POST /v0/test/conversations/{conversation_id}/inbound`; your agent receives\neach turn exactly like a real `message.received` and replies with its normal\nsend. Read both sides with `GET /v0/test/conversations/{conversation_id}/transcript`.\n\nThe conversation is on your app's **simulated sender** for the channel\n(`kind` `simulated`), which Flow creates on first use. It never touches a\nnetwork: sends to the simulated contact pass the real send gate and end\n`sent`. Simulated channels: `telegram`, `whatsapp`, `imessage` and `sms`.\nThe channel's rules hold as on a real sender: a simulated iMessage line,\nlike Flow's lines today, only replies, so the contact writes first; a\nsimulated Telegram bot, like a real one, cannot write to a contact who has\nnot written to it, so a send before the contact's first turn is accepted\n(`202`) and then fails as it does on real Telegram (`message.failed`,\n`channel_error`, `channel_code` `403`); on SMS\nand iMessage a buttons message answers `422 unsupported_content`, as on a\nreal line.\n\nOn WhatsApp, the contact's turns go through the real WhatsApp inbound path,\nso each turn opens the 24-hour window. Outside it, including before the\ncontact's first turn, only a `template` can be sent (`409 outside_window`).\nButtons take up to 3 reply buttons (a `list` up to 10 rows); reactions and\n`reply_to` work; edits and unsends answer `422 unsupported_content`.\n\nOn SMS, the rules that concern the contact hold too. Before the contact's\nfirst turn, a send to them is a start that needs their recorded consent:\nwithout `consent` (on `POST /v0/messages` with `to.contact`), it answers\n`403 permission` with `channel_code` `consent_required`, as a real SMS start\ndoes. A turn with the text STOP opts the contact out and START lifts it (see\n`POST /v0/test/conversations/{conversation_id}/inbound`). A simulated SMS\nsender keeps no quiet hours, so a test's outcome never depends on the time of\nday, and it sends media and voice as MMS.\n\nCreating the conversation sends no event. The conversation starts with its\nfirst message: the contact's first turn (`via` `inbound`), or your first send\nto the contact, such as a WhatsApp template or an SMS send with consent\n(`via` `outbound`). `conversation.started` comes before that message's\n`message.received` or `message.sent`.\n\nOn iMessage, `group` makes a simulated group conversation instead (`type`\n`group`, with the participants as simulated contacts), so you can test how\nyour agent handles groups: each turn names its writer in `from`, and the\ngate applies the group rules (reactions refused with `channel_code`\n`group_unsupported`, as are typing and read receipts). Each call with\n`group` makes a new group.\n\nGive `contact` to choose the simulated contact's address; giving an address\nthat already has a simulated conversation returns that conversation (`200`).\nWithout `contact`, Flow makes a new contact with a made-up address (phone\nnumbers start `+1555`), so each call is a fresh conversation, for example one\nper test case. Simulated contacts never match real ones, even with the same\naddress.\n\nSimulated traffic is free: it does not count against the sandbox allowance.\nAn app has at most 500 simulated conversations: past that, a new one answers\n`403 permission` with `channel_code` `simulated_conversation_limit` (an\naddress that has one still returns it with `200`), so reuse conversations\nacross test runs. The `/v0/test/` endpoints take at most 120 requests a\nminute per key (`429 rate_limited`), on top of the plan's request rate\nlimit. A live key answers `403 permission` with `channel_code`\n`test_mode_only`.\n\nA HIPAA account (`Account.hipaa`) can simulate only the channels its\nbusiness associate agreement covers (SMS), as it can connect senders only\nthere: `telegram`, `whatsapp` or `imessage` answers `403 permission` with\n`channel_code` `hipaa_channel_not_covered` (`param` `channel`).\n","href":"/api-reference/testing/create-a-simulated-conversation"},{"openapi":"unified-messaging-api/openapi.yaml POST /v0/test/conversations/{conversation_id}/inbound","title":"Write as the simulated contact","href":"/api-reference/testing/write-as-the-simulated-contact"},{"openapi":"unified-messaging-api/openapi.yaml GET /v0/test/conversations/{conversation_id}/transcript","title":"Read a simulated conversation, both ways","description":"Test keys only. Lists a simulated conversation's messages, both directions,\n**oldest first**, so a test can page forward with `after` (the last message\nID it has) to see what your agent sent back to the simulated contact. Each\nentry is a `Message`; a sent message's `content` is exactly what the\nsimulated contact's phone shows, since Flow converts nothing.\n\nA conversation that is not simulated answers `403 permission` with\n`channel_code` `not_simulated`; a live key answers `403 permission` with\n`channel_code` `test_mode_only`.\n","href":"/api-reference/testing/read-a-simulated-conversation-both-ways"}]},{"group":"Errors","expanded":false,"pages":[{"title":"invalid_request","href":"/unified-messaging-api/errors/invalid_request"},{"title":"authentication","href":"/unified-messaging-api/errors/authentication"},{"title":"permission","href":"/unified-messaging-api/errors/permission"},{"title":"not_found","href":"/unified-messaging-api/errors/not_found"},{"title":"idempotency_conflict","href":"/unified-messaging-api/errors/idempotency_conflict"},{"title":"outside_window","href":"/unified-messaging-api/errors/outside_window"},{"title":"unsupported_content","href":"/unified-messaging-api/errors/unsupported_content"},{"title":"new_contact_limit","href":"/unified-messaging-api/errors/new_contact_limit"},{"title":"sender_throttled","href":"/unified-messaging-api/errors/sender_throttled"},{"title":"file_blocked","href":"/unified-messaging-api/errors/file_blocked"},{"title":"rate_limited","href":"/unified-messaging-api/errors/rate_limited"},{"title":"channel_error","href":"/unified-messaging-api/errors/channel_error"},{"title":"not_implemented","href":"/unified-messaging-api/errors/not_implemented"},{"title":"api_error","href":"/unified-messaging-api/errors/api_error"}]}]}]}]},"title":"Write as the simulated contact - Flow","buildId":"dpl_849ArpqvBiCczQiT1Y1stNGpbqHT","deploymentId":"dpl_849ArpqvBiCczQiT1Y1stNGpbqHT","dynamics":["c2aa737661","2b790eda92","b0f84f53ff","a9bb46feb3"],"chunks":["OpenApiReferenceProvider-Bbp7I60D.js","utils-C3RIvJha.js","cloneDeep-izs3Hzxb.js","_Uint8Array-BVIqJ90l.js","isObjectLike-C8vmzPEN.js","isArray-Dcu1dQ5t.js","utils-DqD-vLt8.js","toString-BX-iyh8G.js","isSymbol-CSH9U2Gm.js","addUniqueKeysToSchema-B31Loprg.js","snippetPresets-DKpk-b8t.js","style-to-object-BY3JEAdk.js","v4-DIgz8zy6.js","ApiExamples-Cj65AhOR.js","isEqual-BfaiBbcl.js","usePlaygroundInputsStore-BXtn5gcC.js","shallow-RY2b03Cp.js","getApiPlaygroundDisplay-B0jeiPk3.js","CodeSnippet-CYgBjjwn.js","CodeSnippets-DOngDrYB.js","Page-n7I5XbKb.js","getPastedJson-DRQbJx-T.js","icon-CQiQCicY.js","MethodPill-BKTLdNtu.js","DuplicateIcon-B9jpAdat.js","createIcon-DCddRrXv.js","DynamicMarkdown-a8hUKrw6.js","SectionHeading-eECVV2eH.js","axios-vUo_bRtp.js","Markdown--GF7ZIpf.js","clsx-DZLdJUrt.js","lib--scc4DXi.js","lib-B8HbPgcC.js","html-void-elements-mCSPHezU.js","dist-2LoAhjKH.js","katex-YMM-FMeD.js","marked.esm-CZWythHk.js","AssistantPageLink-Zwc0ZncM.js"]}