vars, secrets, agent_override and userdata. An inbound phone call, an inbound WhatsApp call or a new WhatsApp or Telegram chat has no such request. The inbound webhook is that request: before the session starts, Talqing asks your server for its configuration, and applies the answer exactly as POST /v1/calls/outbound would.
It is asked only by agents that turn it on (inbound_webhook: true), and only the agent that answers counts. It is never asked for web calls, API chats, outbound calls or media streams, which each have a request of their own. Nor for a WhatsApp or Telegram chat started with an agent you posted or sent in a batch: that request already said what runs.
Set it up
One webhook per workspace.PUT sets or changes its URL, which must be https:// and resolve to a public address. The signing secret is generated on the first PUT and returned once; later PUTs change only the URL.
inbound_webhook: true in the agent’s config (Ask my server before each inbound session in the agent’s Prompt & voice card; the URL itself is set on Webhooks). Publishing such an agent is refused until the workspace has a webhook, and removing the webhook (DELETE /v1/inbound-webhook) is refused with 409 while a published agent still asks it.
The request
POST to your URL, Content-Type: application/json, User-Agent: talqing-inbound/1:
A chat asks once, when it opens; later messages in the same chat do not ask again, so an agent that needs live state reads it with a tool. A chat stays open until it is ended, so its answer stands for that long.
Verify it
This differs from the event webhooks, which sign the body alone: a request you answer must not be replayable, so reject a timestamp more than five minutes old.
The answer
Return2xx with a JSON object. Every field is optional, {} included, and an unknown field is an error.
metadata is shown on the call and chat, carried by session.started and session.completed, and filters GET /v1/calls and GET /v1/chats (?metadata=tenant:t1, repeatable, all must match). It is kept when the session’s content is deleted.
Refusals
Any of these refuses the session with the close reasoninbound_webhook_failed, and sessions.error.message says which:
- Phone call: the caller hears no greeting and is hung up while still ringing; livekit-sip sends the carrier
486 Busy Here. The refused call is listed in Calls with its reason. - WhatsApp call: the call is hung up before the agent says anything.
- Chat: the message that opened it is kept, marked with the error, and nothing is sent. The chat is closed; the person’s next message opens a new chat, which asks again.
session.refused. Every ask, answered or not, is on the session’s timeline as inbound_webhook with its status code, time, the request sent and what came back: a JSON answer with each secrets value replaced by "[redacted]", or the first 2 KB of anything else.
Test it
POST /v1/inbound-webhook/test with {channel, number, peer} sends a sample session.request signed as a real one is (from = peer, to = number) and returns what came back: {status_code, duration_ms, valid, errors, response}. valid checks the answer’s shape; an agent_override is checked against an agent only on a real session.
Secrets
Workspace and session secrets.
Close reasons
Every way a session ends.