Outbound API
These APIs let a human agent or AI send messages to a customer, and end or block a consultation. All of them require the BrandWrite scope.
Common:
- Base URL:
https://api.talkbridge.io - Headers:
Authorization: Bearer blumnb-...,Content-Type: application/json brandKey: the Brand Key of the target brand — shown in the "Integration keys" card in Developer mode of TalkBridge Center, or inbrandsfromGET /api/agent/me(details inchannels[].key)- Response envelope:
{ "ok": boolean, "code": string, "message": string }— see Error Codes for the full list
Send text
POST /api/agent/send
| Field | Type | Required | Description |
|---|---|---|---|
brandKey | string | ✅ | Brand (channel) key |
userKey | string | ✅ | Target customer key |
text | string | ✅ | Text to send |
curl -X POST https://api.talkbridge.io/api/agent/send \
-H "Authorization: Bearer blumnb-..." -H "Content-Type: application/json" \
-d '{ "brandKey": "mybrand", "userKey": "Ab3d_x9Kq2Lm", "text": "Hello" }'
{ "ok": true, "code": "0", "message": null, "serial": "bw-1757400000000-a1b2" }
serial is the serial number of this send — the same value is used in Cancel a send (delete) and in the agent echo on your inbound webhook.
If Kakao does not accept the message, the response is HTTP 200 with ok: false. code is Kakao's result code as-is — for example, replying to a chat whose consultation session has ended returns -502.
{ "ok": false, "code": "-502", "message": "이미 종료된 상담이라 전송되지 않았어요. 고객이 다시 메시지를 보내면 새 상담으로 이어져요.", "serial": "bw-1757400000000-a1b2" }
See send failure codes for each code's meaning and whether to retry. Rich and attachment sends use the same codes.
Send rich message
Sends Kakao rich message bubbles such as buttons, lists, and image cards.
POST /api/agent/send/rich
| Field | Type | Required | Description |
|---|---|---|---|
brandKey | string | ✅ | Brand key |
userKey | string | ✅ | Target customer key |
rich | string | ✅ | The rich envelope JSON below, passed as a string |
rich is a JSON object serialized to a string. Invalid JSON is rejected with 400 BAD_RICH.
Rich envelope schema
user_key, sender_key, and serial_number are filled in by the server — do not put them in the envelope.
| Field | Required | Description |
|---|---|---|
chat_bubble_type | ✅ | TEXT · IMAGE · WIDE · ITEM_LIST · WIDE_ITEM_LIST · CAROUSEL_FEED · PERSONAL (personal-info consent — prefer the dedicated API) · KAKAO_CERT (KakaoTalk identity verification — coming soon; currently rejected with UNSUPPORTED_BUBBLE) |
message | conditional | Bubble text. Up to 1,000 characters for TEXT, 76 for WIDE. At least one of message and attachment is required |
attachment.image.img_url | conditional | Image URL — required for IMAGE and WIDE. Use the url returned by Upload attachments |
attachment.buttons[] | Buttons. { "type": "WL", "name": "…", "url_pc": "…", "url_mobile": "…" } (WL = web link) | |
attachment.quick_replies[] | Quick replies (up to 10). See Quick replies for the format | |
attachment.item · attachment.carousel · attachment.coupon | conditional | item for WIDE_ITEM_LIST, carousel for CAROUSEL_FEED (2–6 cards), coupon |
auto_answer | System auto-reply (S1–S5) — TEXT only |
Example ① TEXT + buttons:
{
"chat_bubble_type": "TEXT",
"message": "Please check your order",
"attachment": { "buttons": [ { "type": "WL", "name": "View order", "url_pc": "https://example.com/orders", "url_mobile": "https://example.com/m/orders" } ] }
}
Example ② IMAGE (an uploaded image):
{ "chat_bubble_type": "IMAGE", "attachment": { "image": { "img_url": "https://…/photo.jpg" } } }
Example ③ WIDE (image + short text + button):
{
"chat_bubble_type": "WIDE",
"message": "New products for September",
"attachment": {
"image": { "img_url": "https://…/banner.jpg" },
"buttons": [ { "type": "WL", "name": "Learn more", "url_pc": "https://example.com/new", "url_mobile": "https://example.com/m/new" } ]
}
}
The response is the same as for a text send and includes serial.
Quick replies (quick_replies)
Buttons laid out horizontally below the bubble. When the customer taps one, a preset text is sent or a link opens. Up to 10 per message.
Format of a single button:
| Field | Max length | Required | Description |
|---|---|---|---|
name | 14 chars | ✅ | Button label |
type | 2 chars | ✅ | Button type — see below |
url_mobile | 300 chars | By type | URL to open on mobile (http/https only) |
url_pc | 300 chars | URL to open on PC (http/https only) | |
scheme_android | 300 chars | By type | Android app scheme |
scheme_ios | 300 chars | By type | iOS app scheme |
url_target | "out" to open in an external browser | ||
extra | 100 chars | Metadata passed with the button (BK, BT) | |
chat_event | 50 chars | Bot event to call when switching to a bot (BT) | |
biz_form_key | By type | Kakao Business Form key (BF) |
type and the fields each one requires:
type | Action | Required fields |
|---|---|---|
BK | Sends the button label as the customer's message | None |
WL | Opens a web link | url_mobile |
AL | Opens an app scheme or web link | At least 2 of scheme_android, scheme_ios, url_mobile |
BT | Switches to bot consultation (ends the consultation, then calls the bot event) — only for channels using a Kakao i Open Builder chatbot | None |
BF | Opens a Kakao Business Form | biz_form_key |
Example ④ TEXT + quick replies:
{
"chat_bubble_type": "TEXT",
"message": "How can we help you?",
"attachment": {
"quick_replies": [
{ "type": "BK", "name": "Track order" },
{ "type": "BK", "name": "Returns" },
{ "type": "WL", "name": "FAQ", "url_mobile": "https://example.com/m/faq", "url_pc": "https://example.com/faq" }
]
}
}
buttons[])Quick replies quick_replies[] | Buttons buttons[] | |
|---|---|---|
| Label length | 14 chars | 28 chars (may be truncated depending on bubble and device) |
| URL / scheme length | 300 chars | 1,000 chars |
| Count | Up to 10 | 2–5 depending on bubble type |
Available type | BK, WL, AL, BT, BF | The above plus MD (sends label + body) and AC (add channel) |
If a limit is exceeded, Kakao rejects the message and the send returns a failure. Check the 14-character limit especially when reusing a button label as a quick reply.
Send attachments
Sends images and files directly to the customer. The server uploads them to Kakao and assembles the bubbles (image = rich IMAGE or a regular photo, file = plain FILE). Unlike Upload attachments, which only returns a URL, this API delivers to the customer.
POST /api/agent/send/attachments # multipart/form-data
| Part | Required | Description |
|---|---|---|
brandKey | ✅ | Brand key |
userKey | ✅ | Target customer key |
text | Text to send along — with rich images it goes into the first bubble; with regular photos (imageStyle=plain) it is sent as a separate text bubble before the photos | |
files | ✅ | Files (multiple allowed). jpg, png and gif are sent as image bubbles; other formats (including webp and heic) are sent as file bubbles (detected by Content-Type or extension) |
imageStyle | Image bubble style — rich (default) or plain. See the table below |
imageStyle | What the customer sees | Limits |
|---|---|---|
rich (default) | An image with a caption inside a white card. Tapping it does not open the full-size image | Caption required ("이미지" is added if empty) · aspect ratio 3:4–2:1 · width ≥ 500px — outside that, the server pads with white margins |
plain | A regular photo bubble, the same as photos customers send. Tapping opens the full-size image | No caption · no aspect-ratio or width limits · up to 5MB (larger images are scaled down by the server) |
For photos the customer needs to see in detail, such as drafts or print proofs, plain is recommended.
curl -X POST https://api.talkbridge.io/api/agent/send/attachments \
-H "Authorization: Bearer blumnb-..." \
-F "brandKey=mybrand" -F "userKey=Ab3d_x9Kq2Lm" -F "text=Here is the material you requested" \
-F "files=@photo.png" -F "files=@manual.pdf"
# As a regular photo bubble
curl -X POST https://api.talkbridge.io/api/agent/send/attachments \
-H "Authorization: Bearer blumnb-..." \
-F "brandKey=mybrand" -F "userKey=Ab3d_x9Kq2Lm" -F "imageStyle=plain" -F "text=Here is the draft" \
-F "files=@draft.jpg"
{ "ok": true, "results": [ { "kind": "image", "ok": true, "code": "0", "serial": "bw-…" }, { "kind": "file", "ok": true, "code": "0", "serial": "bw-…" } ] }
Each bubble gets its own serial. ok is true if at least one bubble succeeded, and failed items (ok: false) carry a message explaining why. An image that is still over 5MB after the server scales it is not sent to Kakao; only that item fails with IMAGE_TOO_LARGE. To delete them, see Cancel a send (delete).
Cancel a send (delete)
Deletes a sent message within 24 hours of sending. Text, rich, image and file messages are all deleted with this one API, and the unit of deletion is one bubble.
POST /api/agent/delete
| Field | Type | Required | Description |
|---|---|---|---|
brandKey | string | ✅ | Brand key |
userKey | string | ✅ | Target customer key |
serial | string | one of | The serial from the send response — deletes exactly that bubble (recommended) |
text | string | one of | When you don't know the serial, finds the recent send by its exact body text |
withinSeconds | number | Lookback window for the text search (seconds, 10–86400; default 180) |
{ "ok": true, "code": "0", "message": null, "serial": "bw-1757400000000-a1b2" }
Rules to know first
- You can delete only up to 24 hours after sending.
- The bubble disappears from the customer's chat room and a "This message has been deleted" notice is left in its place. It is not a silent removal.
- The consultation count is not restored. Deleting is not the same as cancelling the send.
- Personal-data consent and KakaoTalk verification bubbles, and messages sent by the customer, cannot be deleted.
- Deleting a bubble that was already deleted returns
ok: falsewith a message saying it is already deleted. - Store the
serialwhen you send. Every send response and the webhookagentsignal carry it. Looking up bytextis a fallback for when you lost theserial.
1. Text and rich messages
A text send or rich send is a single bubble, so you delete it with the one serial in the response.
# Send → store the serial from the response
curl -X POST https://api.talkbridge.io/api/agent/send \
-H "Authorization: Bearer blumnb-..." -H "Content-Type: application/json" \
-d '{ "brandKey": "mybrand", "userKey": "Ab3d_x9Kq2Lm", "text": "We have confirmed your order" }'
# → { "ok": true, "code": "0", "serial": "bw-1757400000000-a1b2" }
# Delete
curl -X POST https://api.talkbridge.io/api/agent/delete \
-H "Authorization: Bearer blumnb-..." -H "Content-Type: application/json" \
-d '{ "brandKey": "mybrand", "userKey": "Ab3d_x9Kq2Lm", "serial": "bw-1757400000000-a1b2" }'
If you don't have the serial, you can pass "text": "We have confirmed your order" instead. If you sent the same text several times, the most recent one is deleted.
2. A single image
When you send one image with Send attachments, results[] has one item; delete it with that serial. Any text you sent with it becomes the image bubble's caption, so it is the same bubble — deleting it removes the image and the caption together.
{ "ok": true, "results": [ { "kind": "image", "ok": true, "code": "0", "serial": "bw-1757400000100-c583" } ] }
curl -X POST https://api.talkbridge.io/api/agent/delete \
-H "Authorization: Bearer blumnb-..." -H "Content-Type: application/json" \
-d '{ "brandKey": "mybrand", "userKey": "Ab3d_x9Kq2Lm", "serial": "bw-1757400000100-c583" }'
If you sent a caption, you can also delete with "text": "<the caption you sent>".
3. Several images (a group)
Even when you send several images at once, each image is its own bubble in the customer's chat room, and results[] has a serial per image. You also delete once per bubble — a single call does not delete the whole group.
{ "ok": true, "results": [
{ "kind": "image", "ok": true, "code": "0", "serial": "bw-1757400000200-36e8" },
{ "kind": "image", "ok": true, "code": "0", "serial": "bw-1757400000201-2ecb" }
] }
# Delete the whole group — one call per successful item's serial
for s in bw-1757400000200-36e8 bw-1757400000201-2ecb; do
curl -X POST https://api.talkbridge.io/api/agent/delete \
-H "Authorization: Bearer blumnb-..." -H "Content-Type: application/json" \
-d "{ \"brandKey\": \"mybrand\", \"userKey\": \"Ab3d_x9Kq2Lm\", \"serial\": \"$s\" }"
done
- You can also delete only the images you choose; the other bubbles stay.
- Looking up by
textworks only for the first image: the caption goes on the first image and later images get a default caption. Delete groups byserial. - If some images fail,
okis stilltrueas long as at least one was sent. Failed items (ok: false) never reached the customer, so there is nothing to delete;messageexplains why (for example, an unsupported image — very small or very tall images are not supported). - If you send a file together with text, the file bubble and the text bubble go out separately (
kind: "file"andkind: "text"). Delete each by its ownserial.
Confirming a deletion
- Webhook: each successful deletion sends one
kind: "deleted"signal. Itsserialis the bubble that was deleted — deleting a whole group sends one per image. Deletions made from another agent tool arrive the same way. - Retrieval: in recent messages the original outbound item gets
deleted: true. For a group, it is set once every image has been deleted.
Failure codes
code | Meaning | What to do |
|---|---|---|
NOT_FOUND | No recent send matched text | Increase withinSeconds or pass serial |
NOT_DELETABLE | Personal-data consent or KakaoTalk verification bubble | Cannot be deleted |
EXPIRED | More than 24 hours since sending | Cannot be deleted |
| Other codes | Already deleted, etc. | Follow the message |
End consultation
A human agent actively ends an in-progress consultation session. If a closing greeting is provided, it is sent first, then the session ends.
POST /api/agent/end
| Field | Type | Required | Description |
|---|---|---|---|
brandKey | string | ✅ | Brand key |
userKey | string | ✅ | Target customer key |
greeting | string | Closing greeting to send before ending (optional) |
End + hand off to bot
Ends the consultation and hands off to a bot scenario.
POST /api/agent/end-with-bot
| Field | Type | Required | Description |
|---|---|---|---|
brandKey | string | ✅ | Brand key |
userKey | string | ✅ | Target customer key |
botEvent | string | Bot event to hand off to (optional) |
Request personal-info consent
Sends Kakao's personal-info consent bubble to the customer. When the customer agrees, their nickname and phone number are delivered as a webhook personal_info event. Kakao channels only.
POST /api/agent/personal-info/request
| Field | Type | Required | Description |
|---|---|---|---|
brandKey | string | ✅ | Brand key |
userKey | string | ✅ | Target customer key |
curl -X POST https://api.talkbridge.io/api/agent/personal-info/request \
-H "Authorization: Bearer blumnb-..." -H "Content-Type: application/json" \
-d '{ "brandKey": "mybrand", "userKey": "Ab3d_x9Kq2Lm" }'
# → { "ok": true, "code": "0", "serial": "bw-1757400000000-a1b2" }
okin the response is the result of sending the bubble. The consent result arrives by webhook, not in this response.- The bubble's text and button are fixed by Kakao and cannot be changed. It can be sent once per consultation session, and consent is valid only within 3 days of sending.
- Like any other send, it counts toward your consultations (no extra usage if you are already serving that consultation that day).
- This bubble cannot be removed with Cancel a send.
| Failure code | Meaning |
|---|---|
UNSUPPORTED_CHANNEL | Not a Kakao channel |
-520 | Consent was already requested in this consultation (once per session) — you can send it again once the customer starts a new consultation |
EMPTY | userKey is empty |
TalkBridge relays the consent result (nickname and phone number) without storing it. There is no way to receive it again or look it up, so connect your webhook before you send the request and store the data as soon as it arrives. Discord and Slack integrations are only told that consent was given — the values are not sent there.
Block / unblock customer
POST /api/agent/block
POST /api/agent/unblock
| Field | Type | Required | Description |
|---|---|---|---|
brandKey | string | ✅ | Brand key |
userKey | string | ✅ | Target customer key |
Upload attachments
Upload the image or file you want to attach to an outbound message first, to obtain its URL and key.
POST /api/agent/upload/image # multipart: brand, file, imageType?(rich|plain)
POST /api/agent/upload/file # multipart: brand, file, fileType(file|audio|video)
Upload an image with the imageType that matches where you will send it. A URL uploaded for the other kind is rejected when sending.
imageType | Used for | Limits |
|---|---|---|
rich (default) | The image of a rich message | Aspect ratio 3:4–2:1 · width ≥ 500px · up to 5MB |
plain | Send a regular photo | Up to 5MB |
Image upload example:
curl -X POST https://api.talkbridge.io/api/agent/upload/image \
-H "Authorization: Bearer blumnb-..." \
-F "brand=mybrand" -F "imageType=plain" -F "file=@photo.jpg"
{ "ok": true, "code": "0000", "url": "http://dn-m.talk.kakao.com/.../photo.jpg", "imageType": "plain" }
Use the returned url in the image field of a rich message (rich) or with Send a regular photo below (plain).
- Only jpg, png and gif can be uploaded. The server checks the file content itself, so renamed files (e.g. a webp saved as
.jpg) and corrupted files return400 UNSUPPORTED_IMAGE. plainis prepared by the server the same way as Send attachments (photo orientation fixed; scaled down above 5MB or a 4096px long side).richis passed as uploaded (no padding or scaling). Above 5MB it returns413 IMAGE_TOO_LARGE. To also fit the aspect ratio and send right away, use Send attachments.
Send a regular photo
Sends a url obtained with imageType=plain as a regular photo bubble. Tapping it opens the full-size image. It cannot carry a caption — send one separately with Send text if needed.
POST /api/agent/send/image
| Field | Type | Required | Description |
|---|---|---|---|
brandKey | string | ✅ | Brand key |
userKey | string | ✅ | Target customer key |
imageUrl | string | ✅ | The url returned by upload/image (imageType=plain). External image URLs are not accepted by Kakao and are rejected with 400 BAD_URL |
curl -X POST https://api.talkbridge.io/api/agent/send/image \
-H "Authorization: Bearer blumnb-..." -H "Content-Type: application/json" \
-d '{ "brandKey": "mybrand", "userKey": "Ab3d_x9Kq2Lm", "imageUrl": "http://dn-m.talk.kakao.com/.../photo.jpg" }'
{ "ok": true, "code": "0", "message": null, "serial": "bw-…" }
When an outbound send succeeds, a kind: "agent" signal comes back to your inbound webhook twice (an acceptance signal with phase: "accepted", then the journal entry) — an echo of your own send. To avoid infinite loops, filter out kind == "agent" events in your webhook handler. See Inbound payload for details.