Skip to main content
WEBHOOK
A chat agent’s reply doesn’t always come back as the response to your request. Osvi delivers it to a URL you configure whenever there’s no live request to answer inline:
A session’s most recent messages decide where its replies go: if the customer has been messaging over a chat WebSocket connection, replies are delivered as events on that socket instead of to this webhook. This webhook is used for sessions driven through the REST endpoints (POST /v1/chat/inbound / /inbound/async).

Configuring a delivery URL

Set this under the chat agent’s Settings tab, in the Delivery card:
Deliveries to this URL are not signed. There’s no HMAC signature or shared secret built in — if you need to verify a request came from Osvi, check for a custom header you configured above.
You can’t remove an active agent’s delivery URL while it’s still active — deactivate the agent first. This prevents replies the agent already owes a customer from having nowhere to go.

The envelope

Every delivery is a POST with a JSON body describing one reply, or a turn’s completion: If the reply is an interactive message, the envelope also carries id, type: "interaction", created_at, interaction_id, tool_type, schema_version, presentation, interaction_status, interaction_revision, and (once answered) selected_option_id/response_payload — see Interactive messages for what those mean.

How a turn ends

A turn can produce more than one reply. They arrive as separate envelopes, in delivery order, and are always followed by exactly one final envelope with turn_complete: true — that’s your signal the turn is fully done and nothing more is coming for it. A turn that produces no reply at all — most commonly because your message arrived while another turn for the same session was still running and got folded into it — still sends that final envelope on its own, with content: "" and turn_complete: true. Every turn you get accepted for is guaranteed exactly one completion signal, even one with nothing to say.

Delivery, retries, and ordering

  • Respond with any 2xx status within 15 seconds to acknowledge a delivery.
  • A non-2xx response, a timeout, or a connection error is retried automatically — up to 6 attempts total, spaced a few seconds apart at first and up to about a minute apart later. Design your endpoint to be safe to receive more than once (see idempotency_key above).
  • If every attempt fails, Osvi stops retrying that delivery — there’s no further automatic redelivery after the 6th attempt.
  • Deliveries for a turn are sent in order and are not retried out of order; a retry of an earlier delivery is not sent again once a later one for the same turn has succeeded.
  • Deactivating the agent, or removing its delivery URL, stops future delivery attempts — replies already in flight are not guaranteed to arrive after that point.

Body

application/json
session_id
string<uuid>

The chat session this reply belongs to.

Example:

"4718a326-417b-4dda-90d0-21fd65a11cb8"

agent_uuid
string

The agent that produced this reply.

Example:

"agent_IsZ3Q6Sf_60Eh26XQMGbz-R_og"

customer_id
string | null

Your identifier for the customer, when known.

Example:

"+15551112222"

content
string

The reply text. Empty when this envelope only carries a turn-completion marker (see turn_complete).

Example:

"Sure — I can help with that. What's your order number?"

role
enum<string>

Always assistant — this webhook only delivers replies, never customer messages.

Available options:
assistant
Example:

"assistant"

actor_kind
enum<string>

Who authored the content — the agent itself, or a human operator (a Tell, a plain support message, or a takeover reply). Omitted when not applicable.

Available options:
ai,
operator
Example:

"ai"

ts
integer

Unix timestamp (seconds) of delivery.

Example:

1749034825

turn_id
string

Identifies the turn this reply belongs to — present when the reply was produced by a turn (e.g. one accepted via Send Message Async).

Example:

"9c3e2f1a4b5d4e6f8a9b0c1d2e3f4a5b"

reply_index
integer

Zero-based position of this reply among the (possibly several) replies of its turn, when the reply was produced by a turn. Not every reply carries one; rely on delivery order rather than this field to sequence replies.

Example:

0

idempotency_key
string

Echoes the idempotency_key from the inbound request that produced this turn, when one was set — use it to correlate this delivery with your original request.

Example:

"wh-2f8a1c-attempt-1"

turn_complete
boolean

true only on the last envelope of a turn, marking it finished. A turn that produced no visible reply (e.g. one merged into another turn) still sends exactly one envelope with content: "" and turn_complete: true, so your integration always sees a definite end to every accepted turn.

Example:

true

mode
enum<string>

Session mode at turn completion. Sent alongside turn_complete.

Available options:
ai,
support
Example:

"ai"

help_requested
boolean

Whether the agent has escalated the conversation for a human operator. Sent alongside turn_complete.

Example:

false

id
string

Present only when this reply is an interactive message: the message id.

Example:

"3f9c2b1a-8e4d-4c2a-9b1e-2d3f4a5b6c7d"

type
enum<string>

Present only when this reply is an interactive message.

Available options:
interaction
Example:

"interaction"

created_at
string<date-time>

Present only when this reply is an interactive message.

Example:

"2026-06-04T11:40:25.690486Z"

interaction_id
string<uuid>

Present only when this reply is an interactive message. See Interactive messages.

Example:

"b6a3e6c2-2d61-4a4b-9c9a-5a2d9f6a2b10"

tool_type
enum<string>

Present only when this reply is an interactive message.

Available options:
ui_buttons,
ui_carousel,
ui_date_picker
Example:

"ui_buttons"

schema_version
integer

Present only when this reply is an interactive message.

Example:

2

presentation
object

Present only when this reply is an interactive message — the content to render. Shape depends on tool_type; see Interactive messages.

Example:
interaction_status
enum<string>

Present only when this reply is an interactive message. pending while waiting for an answer, accepted once answered, cancelled if superseded (e.g. the customer sent a plain message instead), expired if it timed out unanswered.

Available options:
pending,
accepted,
cancelled,
expired
Example:

"pending"

interaction_revision
integer

Present only when this reply is an interactive message.

Example:

1

selected_option_id
string | null

Present only once an interactive message has been answered.

Example:

null

response_payload
object | null

Present only once an interactive message has been answered with a structured response.

Example:

null

Response

200

Respond with any 2xx status within 15 seconds to acknowledge receipt. A non-2xx response, a timeout, or a connection error is retried automatically — up to 6 attempts in total, spaced a few seconds to about a minute apart — after which Osvi stops retrying that delivery. Deliveries are not retried if the agent's delivery URL was removed or the agent was deactivated in the meantime.