Skip to main content

Inbound payload

This is the format of the request the Gateway sends to your URL.

Request​

POST <your webhook URL>
Content-Type: application/json
X-Bridge-Signature: v0=<hex>
X-Bridge-Event: <kind>
X-Bridge-Timestamp: <unix seconds>
X-Bridge-Delivery-Id: <uuid>

Body — the "signal"​

{
"brand": "mybrand",
"userKey": "Ab3d_x9Kq2Lm",
"kind": "message",
"seq": 42
}
FieldTypeDescription
brandstringBrand (channel) key
userKeystringConsultation customer key. The same correlation key as userKey in the outbound API
kindstringEvent type (see the table below)
seqnumberMonotonically increasing sequence in the brand journal. The basis for ordering and idempotency
The body contains no message text

The payload is a signal that "something happened" and does not include the message body (text). If you need the actual conversation content, fetch it with the Content Retrieval API. The X-Bridge-Event header is identical to kind in the body.

Event types (kind)​

kindMeaningTypical handling
messageThe customer sent a messageFetch content → process / reply
referenceA new consultation was connected (session start)Handle session start
expiredThe session expiredHandle session end
endedThe consultation endedHandle session end
agentYour (or a human agent's) outbound send was applied (echo)Usually ignore
deletedA sent message was deleted (Cancel a send) — match it by serialMark that bubble as deleted in your UI
personal_infoThe customer consented to share personal info — the values are in the body (below)Store immediately
Filtering self-echo

kind: "agent" is the echo of an outbound message you sent. If your reply logic reacts to this event, you get an infinite loop — filter out agent.

The agent echo arrives twice​

Every outbound send produces two kind: "agent" signals. Tell them apart by their fields — do not infer from whether seq is present.

OrderWhenDistinguishing fieldsContent
① Acceptance signalRight after the outbound API returns successphase: "accepted" · serialNo seq. serial equals the one in the send response — you can match it exactly to an optimistically rendered bubble
② Journal entryAbout a second later, when it is written to the brand journalseq · serialSame shape as other events. Part of the (brand, seq) idempotency key. For an attachment send split into several bubbles, serials carries the full list
{ "brand": "mybrand", "userKey": "Ab3d_x9Kq2Lm", "kind": "agent", "phase": "accepted", "serial": "bw-1757400000000-a1b2" }
{ "brand": "mybrand", "userKey": "Ab3d_x9Kq2Lm", "kind": "agent", "seq": 43, "serial": "bw-1757400000000-a1b2" }

Recommended handling: do not store ①, use it only as a UI acknowledgement; deduplicate ② by seq. Ignoring both has no effect on the consultation flow. serial may be absent on ① with an older core, but phase is always present.

Message deleted (deleted)​

When a Cancel a send (delete) call succeeds, a kind: "deleted" signal arrives. Its serial is the serial of the deleted send, so you can match it against the agent signal or the send response to find the bubble.

{ "brand": "mybrand", "userKey": "Ab3d_x9Kq2Lm", "kind": "deleted", "seq": 45, "serial": "bw-1757400000000-a1b2" }

Clients that did not request the deletion (for example, another agent tool on the same brand) can use this signal to stay in sync. Deduplicate by (brand, seq) like any other event. Deleting does not restore the consultation count.

Sent when the customer agrees to a personal-info consent request. Unlike other signals, the values are in the body.

{
"brand": "mybrand", "userKey": "Ab3d_x9Kq2Lm", "kind": "personal_info",
"sessionId": "29153", "consentedAt": 1757400000000,
"personalInfo": { "phoneNumber": "821012345678", "nickname": "customer-nickname" }
}
FieldDescription
sessionIdThe Kakao consultation session in which consent was given
consentedAtWhen the customer consented (Unix ms)
personalInfo.phoneNumberKakao account phone number, with country code (e.g. 8210…). Empty string if not registered with Kakao
personalInfo.nicknameKakao profile nickname. Empty string if not registered
  • There is no seq. This signal is not written to the brand journal, so deduplicate by X-Bridge-Delivery-Id instead of (brand, seq).
  • TalkBridge relays these values without storing them. They do not appear in message retrieval and cannot be delivered again. If your webhook does not respond, delivery is retried briefly and then discarded — store the data as soon as it arrives.
  • You are responsible for storing, using and deleting this information. Describe the collected items and purpose in your own privacy policy.
  • Discord and Slack integrations receive only a "the customer consented to share personal info" notice, without the values.

Consultation connect metadata (extra)​

Values planted in a connect button or link, or through a chatbot handoff (extra), arrive inside the signal itself. Fields appear only when a value exists.

kindFieldContent
referencereference.extraThe value planted for this connection
referencelastReference.{extra, bot, botEvent, createdAt}The most recent metadata, which Kakao provides when there is none for this connection
messageextraThe value of a message the customer sent by tapping a button (BK/MD)
{ "brand": "mybrand", "userKey": "Ab3d_x9Kq2Lm", "kind": "reference", "seq": 44, "reference": { "extra": "goods_1024" } }

See Using Connect Metadata for how to plant values, the order of reading, and a routing example.

Test signal​

The Inbound test in the "Connection test" card of TalkBridge Center Developer mode sends the signal below with the same headers and the same signature as a real delivery.

{ "brand": "mybrand", "userKey": "tb-test-1a2b3c4d", "kind": "message", "seq": 0, "test": true }
  • test: true is present only on test signals. Real signals never carry this field.
  • userKey is a virtual customer (it starts with tb-test-), so content retrieval and replies do not work. Verify the signature and return 2xx, then skip your storage and reply logic.
  • seq is always 0, so it can collide with the (brand, seq) idempotency key — branch on test: true before idempotency handling.

Correlation​

  • userKey in the inbound signal = userKey in the reply send (POST /api/agent/send).
  • brand = brandKey in outbound and retrieval calls.
  • No session-level identifier is needed — a single userKey threads the conversation.