Skip to main content

Content Retrieval

The inbound webhook delivers signals only (payload). When you receive a kind: "message" signal, fetch the actual conversation content with the retrieval API.

Scope of this document

This page covers the retrieval API for fetching conversation content after receiving an inbound signal (kind: "message"). As the content model expands to attachments, rich messages, and so on, they will be added to this page.

Recent messages for a specific customer​

GET /api/agent/rooms/{userKey}/messages?brand=mybrand&max=20
  • Authentication: Authorization: Bearer blumnb-... (BrandRead or higher)
  • max: 1–300 (default 100). Returned newest first.
curl "https://api.talkbridge.io/api/agent/rooms/Ab3d_x9Kq2Lm/messages?brand=mybrand&max=5" \
-H "Authorization: Bearer blumnb-..."
{
"ok": true,
"brand": "mybrand",
"userKey": "Ab3d_x9Kq2Lm",
"messages": [
{
"seq": 42,
"userKey": "Ab3d_x9Kq2Lm",
"sessionId": "ses_...",
"kind": "message",
"text": "I'd like to ask about a refund",
"timestampUnixMs": 1751850000000
}
]
}

messages[] items:

FieldTypeDescription
seqnumberJournal sequence (corresponds to seq in the webhook signal)
userKeystringCustomer key
sessionIdstringSession identifier
kindstringmessage / reference / expired / agent / ended / deleted
textstringMessage body
timestampUnixMsnumberTime received (ms)
serialstringOnly on outbound (agent) and deletion (deleted) items — same value as the serial in the send response and webhook signals
deletedbooleantrue only on a deleted outbound message. Absent otherwise

Mapping a signal to its content​

When you receive a message signal, the text of the first kind: "message" item in the response above is the customer message that just arrived (results are newest first).

Consultation room list​

Returns a summary of in-progress consultation rooms (per customer).

GET /api/agent/rooms?brand=mybrand&max=50
{
"ok": true,
"brand": "mybrand",
"rooms": [
{
"userKey": "Ab3d_x9Kq2Lm",
"lastSeq": 42,
"lastText": "I'd like to ask about a refund",
"lastKind": "message",
"lastTimestampUnixMs": 1751850000000,
"count": 7,
"ended": false
}
]
}

Conversation history (convId)​

Consultations are grouped into conversations (convId) per session window. Use these when you need per-conversation aggregation or the full transcript rather than the per-customer recent messages above. It is the same path the CLI history command uses, and because Inquiry.id in the knowledge graph is <convId>#<turn>, you can build that key from REST alone.

GET /api/agent/conversations?brand=mybrand
[
{
"brand": "mybrand",
"conversations": [
{ "convId": "ad05d3f3…", "userKey": "Ab3d_x9Kq2Lm", "seq": 17, "sessionId": "ses_…", "ended": false,
"lastActivity": "2026-09-09T01:24:33+00:00", "last": "How much does TalkBridge cost?", "count": 4 }
]
}
]
GET /api/agent/conversation?brand=mybrand&convId=ad05d3f3…
{
"convId": "ad05d3f3…", "userKey": "Ab3d_x9Kq2Lm", "seq": 17, "sessionId": "ses_…", "ended": false,
"lines": [
{ "role": "customer", "text": "How much does TalkBridge cost?", "at": "2026-09-08T01:23:10+00:00" },
{ "role": "agent", "text": "Hello! Our plans are …", "at": "2026-09-08T01:25:02+00:00" }
]
}
  • Authentication: BrandRead or higher. lines[].role is one of customer, agent, bot, system.
  • If there is no such conversation: 404 with NO_CONV.

Images · attachments​

When a customer sends a photo, video, voice message, or file with KakaoTalk's upload feature, the body (text) carries it as [type] Kakao CDN URL. The type is photo, video, audio, or file.

[photo] https://talk.kakaocdn.net/…/….jpeg

Photos sent together as a bundle arrive as one message, with one line per photo in the body (separated by a newline, \n). If the customer typed a caption with the attachment, it follows the URL on the same line, after a space.

[photo] https://talk.kakaocdn.net/…/i_6f66….jpeg
[photo] https://talk.kakaocdn.net/…/i_cdfc….jpeg
[photo] https://talk.kakaocdn.net/…/i_6d1c….jpeg
{ "kind": "message", "text": "[photo] https://talk.kakaocdn.net/…/i_6f66….jpeg\n[photo] https://talk.kakaocdn.net/…/i_cdfc….jpeg" }
  • Handling: Attachments are also handled as a message event plus content retrieval. Split the body into lines, then read the [type] and URL from each line. If you read only the first line, you miss the second and later photos of a bundle. You can also tell the type apart by the extension (photo jpg·png / video mp4 / voice m4a·mp3 / file pdf, and so on).
  • lines[].text in the conversation history (/api/agent/conversation) uses the same format.
  • Other URLs in the body are not attachments. Links a customer types into the chat arrive as the same body text. Parse the URL and confirm exactly that the host is talk.kakaocdn.net.

Example — extract only the photos from the body (Node.js)​

Runs on Node.js 18+ (built-in fetch) with no external packages. It fetches a customer's recent messages, splits each body into lines, and keeps only [photo] lines whose host is the Kakao CDN.

const API_BASE = 'https://api.talkbridge.io';
const TOKEN = process.env.BRIDGE_TOKEN; // "blumnb-..."

// One line = "[type] URL" or "[type] URL caption"
const LINE = /^\[(photo|video|audio|file)\] (\S+)(?: (.*))?$/;

/** Pull every attachment out of a message body (text) — a photo bundle spans several lines. */
function parseAttachments(text) {
const out = [];
for (const line of (text ?? '').split('\n')) {
const m = LINE.exec(line.trim());
if (!m) continue; // a plain text line
let url;
try { url = new URL(m[2]); } catch { continue; }
// Treat non-Kakao-CDN URLs as not attachments (links the customer typed · SSRF protection).
if (url.protocol !== 'https:' || url.hostname !== 'talk.kakaocdn.net') continue;
out.push({ type: m[1], url: url.href, comment: m[3] ?? '' });
}
return out;
}

/** Collect only the photo URLs from a customer's recent messages. */
async function fetchPhotoUrls(brand, userKey, max = 20) {
const res = await fetch(
`${API_BASE}/api/agent/rooms/${encodeURIComponent(userKey)}/messages?brand=${brand}&max=${max}`,
{ headers: { Authorization: `Bearer ${TOKEN}` } });
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const { messages } = await res.json();

return messages
.filter(m => m.kind === 'message') // customer messages only
.flatMap(m => parseAttachments(m.text)
.filter(a => a.type === 'photo')
.map(a => ({ seq: m.seq, url: a.url, comment: a.comment })));
}

fetchPhotoUrls('mybrand', 'Ab3d_x9Kq2Lm').then(photos => console.log(photos));
// [ { seq: 43, url: 'https://talk.kakaocdn.net/…/i_6f66….jpeg', comment: '' },
// { seq: 43, url: 'https://talk.kakaocdn.net/…/i_cdfc….jpeg', comment: '' } ]
  • Photos with the same seq were sent together as one bundle by the customer.
  • To also collect videos or files, change the a.type === 'photo' condition.
  • If you receive message signals by webhook, apply parseAttachments to the text of the item whose seq matches the signal.
Kakao CDN URLs are not permanent

Kakao does not guarantee how long these URLs are kept, and we have confirmed that a ten-day-old URL no longer opens. If you need attachments in your consultation records, download them right after you receive them and keep them in your own storage, and link your stored copy in the record instead of the URL.

  • When downloading, follow "When you download and keep attachments" in the Inbound server security checklist — if your server downloads a URL that is not on the Kakao CDN, it can be abused to make requests into your internal network (SSRF).
  • Attachments may contain customers' personal information. Manage them according to your privacy policy and retention period.

Handling summary by event​

kindRetrieval needed?Handling
message✅Fetch content → process text / image
reference✗Handle as "new consultation connected" — planted values are in the signal's reference · lastReference (Connect Metadata)
expired✗Handle as "session expired"
ended✗Handle as "consultation ended"
agent✗Ignore (self-send echo)