Skip to main content
Endpoint · POST /conversation/actions
send_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: true when it runs in the Tester.
  • send_json always 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.
Fields
  • url — your system’s URL, http:// or https:// (required, up to 2,000 characters). No username or password in the URL: authentication goes in headers.
  • method — GET, POST, PUT or PATCH. POST if omitted.
  • headers — request headers; for example, your system’s authentication. Names allow letters, digits and hyphens, and Host, Content-Length, Transfer-Encoding, Connection, Keep-Alive, Upgrade and Expect aren’t allowed; each value must be Latin-1 (no ASCII control characters except tab): otherwise the request is rejected with 400 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 of url. One with the same name as a parameter in url replaces its first occurrence, in place, and the repeated ones are dropped; the other pairs are sent as is, without being re-encoded (with query_params, empty segments, like && or a trailing &, are omitted). Up to 50 parameters, with the same name and value limits as headers, 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 with 400 INVALID_REQUEST.
  • body — the body, as JSON text (up to 10,000 characters). Not allowed with GET.
  • wrap_key — wraps the body under that key (up to 100 characters): with "data", your system receives {"data":<body>}. Requires body.
  • 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 a send_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 body and deduplicate by it.
  • Resending the group sends it again. POST /conversation/actions doesn’t deduplicate: if you retry a group with send_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.
Tests from the Tester reach your system as real ones. send_json also dispatches from the Tester —the panel’s, or this API on a test conversation—, because without your system’s real response there would be nothing for the AI employee to read.Unlike the JSON-type AI employee, the request carries no test marker: it goes out identical to a production one. If you don’t want a test to touch your system, point the action at a test URL while you test.

Continue the conversation with the response

With response_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 —or AMBIGUOUS_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 2xx response 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_assistance or assign_ai_employee with run: 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_json in 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.
The business team can also use send_json in a campaign: there it sends one request per recipient —see how they arrive—.

Executable example

A send_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).
string
Always processing: confirms the group was accepted and is running.
integer
How many actions in the array were admitted for execution — not how many succeeded.