Endpoint ·
POST /conversation/actionssend_json sends a request to your system —your ERP, your CRM, your backend— with content
you write, without a model drafting it. It’s the same “Call your system” action the
business team configures in an action set from the panel; here it runs on its own, inside an
action group.
If you want, an AI employee reads what your system answers and keeps the conversation going
with the customer. For example: register the order and have the employee confirm to the
customer the number your system returned.
How it differs from a JSON-type AI employee:
- The JSON employee drafts the body with a model from the conversation, and marks it with
is_test: truewhen it runs in the Tester. send_jsonalways sends the body you wrote, byte for byte. The dispatch doesn’t go through a model or consume tokens (the continuation, if you configure one, does), and it carries no test marker.
url— your system’s URL,http://orhttps://(required, up to 2,000 characters). No username or password in the URL: authentication goes inheaders.method—GET,POST,PUTorPATCH.POSTif omitted.headers— request headers; for example, your system’s authentication. Names allow letters, digits and hyphens, andHost,Content-Length,Transfer-Encoding,Connection,Keep-Alive,UpgradeandExpectaren’t allowed; each value must be Latin-1 (no ASCII control characters except tab): otherwise the request is rejected with400 INVALID_REQUEST. Up to 50 headers, with names of up to 256 characters and values of up to 8,192; together, names and values add up to at most 16,384.query_params— parameters added to the query string ofurl. One with the same name as a parameter inurlreplaces its first occurrence, in place, and the repeated ones are dropped; the other pairs are sent as is, without being re-encoded (withquery_params, empty segments, like&&or a trailing&, are omitted). Up to 50 parameters, with the same name and value limits asheaders, and the URL that gets sent —encoded, with its parameters, and with the path as the request encodes it— can’t exceed 8,192 characters: a path withñor accents counts more than it shows. Past any limit, the request is rejected with400 INVALID_REQUEST.body— the body, as JSON text (up to 10,000 characters). Not allowed withGET.wrap_key— wraps the body under that key (up to 100 characters): with"data", your system receives{"data":<body>}. Requiresbody.response_processor—{ ai_employee, instructions }: the AI employee that reads the response and, optionally, what to do with it (up to 500 characters).
send_json doesn’t trigger the AI, but it counts toward the limit of 3 AI-triggering actions
per group, with or without response_processor: its response can continue the conversation. In a
list, this object goes in the actions array of a POST /conversation/actions — see Assembling
an action list.
The body is text, not an object
body is a string with JSON inside. That’s how it goes out byte for byte: your system
receives exactly that text, with keys in your order and numbers as you wrote them —150.50
doesn’t become 150.5, and an integer longer than 16 digits doesn’t lose precision—. With wrap_key, that same text goes untouched inside the wrapper: your system receives {"<wrap_key>":<body>}. If it isn’t
valid JSON, the request is rejected with 400 INVALID_REQUEST and no action in the group runs.
It’s fixed: it carries no variables or conversation data, and the request carries no platform
headers either. If your system needs to know which conversation it’s about, include that yourself
in the body or in query_params.
With a body, the request carries Content-Type: application/json; to replace it, pass it in
headers under that same name. With GET there’s no body: the data goes in the URL or in
query_params.
Your system’s response doesn’t fail the action
Your system answering with an error, answering late or not answering at all is not asend_json failure: the action counts as successful, doesn’t trigger stop_on_error and doesn’t
escalate the conversation. The action only fails because of something on our side, or if
response_processor doesn’t resolve (see below). In the panel’s Activity log it shows as successful,
with a note that your system answered with an error or didn’t answer, or that we didn’t call it because
its URL is blocked or couldn’t be found.
What’s worth knowing about the delivery:
- Your system has 30 seconds to respond. If it doesn’t respond in time, or there’s a network
error, the request is retried once, identical: if your system processed it but was slow to
answer, it receives it twice. If the request creates something —an order, a charge—, include
your own identifier in the
bodyand deduplicate by it. - Resending the group sends it again.
POST /conversation/actionsdoesn’t deduplicate: if you retry a group withsend_json, your system receives the request again. The same advice applies. - An error response is not retried, and a redirect is not followed.
- The URL must resolve to a public address. A private or local IP is blocked and the request isn’t sent.
Continue the conversation with the response
Withresponse_processor, whatever your system answers can be read by an AI employee to keep
writing to the customer. The same rules apply as for the JSON-type AI employee’s response,
described in What your endpoint answers can continue the
conversation:
- The employee must be operational and active. If the name doesn’t resolve to one, the action
fails with
AI_EMPLOYEE_NOT_FOUND—orAMBIGUOUS_AI_EMPLOYEE, if the name matches more than one— and the request isn’t sent. It isn’t checked in the preflight: the group is accepted and the failure shows up when it runs. - It can be classic or v2 (new engine). A v2 reads the response with its powers: if one of them sends data to your system again, that response also counts toward the chain limit below. If v2 employees are turned off on the platform, the response isn’t processed and an error card shows up in the conversation. In a test conversation that card doesn’t show up.
- Only a
2xxresponse with a body is read, and the body is shown to a model: answer only with what the customer could end up learning. - The continuation runs afterwards, in the background and with no guaranteed timeframe.
- With another AI action in the group, the customer may get two replies. If the same group
includes
run_ai,ai_assistanceorassign_ai_employeewithrun: true, that action replies to the customer and the continuation writes again with what your system answered. Both can run at the same time, not necessarily one after the other. In a campaign, also, if the continuation takes the conversation first, the AI action’s message may not go out. If you want a single reply, let the continuation give it. - The chain has a cap. If the continuation dispatches to your system again, that response can
continue the conversation too. A standalone
send_jsonin an action group starts the chain from the beginning, so it gets two continuations. How many fit in each case is in that same webhooks section.
The action itself is also being enabled in stages. Until it’s enabled on the platform, a group
that includes it is rejected whole with
400 INVALID_REQUEST, before anything runs; if the
platform couldn’t confirm whether it’s enabled, it responds 500 FETCH_FAILED, and you can retry.Availability: continuing the conversation with the response is being enabled in stages at
the platform level, not account by account. Until it’s enabled, your system’s response is logged
and doesn’t continue the conversation.
send_json in a campaign: there it sends one request per
recipient —see how they arrive—.
Executable example
Asend_json on a conversation identified by phone and channel: it registers an order in your
system, and an AI employee confirms to the customer the number your system returns.
Request
Response
202 Accepted — the group was accepted and runs in the background. The per-action result does
not travel in the response (query it later — see How it
runs).