For the complete documentation index, see llms.txt. This page is also available as Markdown.

Agent Reply Notifications (Webhook)

When an agent replies to a visitor who isn't looking at the chat, K-Link POSTs a signed event to your server. You then send the push notification yourself (FCM, APNs, SFMC, ...). K-Link never needs your users' device tokens: you already know which device belongs to which user id.

When an event is sent

All of these must be true:

  1. The channel has a webhook URL configured (see Setup).

  2. The visitor has a verified identity. They opened the chat with an identity (or email) and a valid userHash (see Identity Verification (Secure User Login)). Guests and unverified identity claims are never notified. Otherwise anyone could claim someone else's identity and have their own conversation's text pushed to that person's phone.

  3. The visitor isn't viewing the chat. The chat is closed, the page or app is in the background, or they're disconnected. A visitor who is reading the chat doesn't get a push for the message they're already looking at.

Setup

Use the K-Link public API with your kpt_ token. <channelId> is the same value you pass to the widget as scId.

# Set the URL. The first time, the response includes the signing secret — ONCE.
curl -X PUT "https://apigw.klinkcx.com/api/v1/webchat/channels/<channelId>/notification-webhook" \
  -H "Authorization: Bearer kpt_xxx" -H "Content-Type: application/json" \
  -d '{ "url": "https://hooks.example.com/klink" }'
# => { "requestId": "…", "url": "https://hooks.example.com/klink", "secret": "3f9a…64 hex…" }

# Send a signed test event ("ping") now and see your server's status code
curl -X POST "https://apigw.klinkcx.com/api/v1/webchat/channels/<channelId>/notification-webhook/test" \
  -H "Authorization: Bearer kpt_xxx"
# => { "requestId": "…", "outcome": "delivered", "status": 200 }

# Status (never returns the secret)
curl "https://apigw.klinkcx.com/api/v1/webchat/channels/<channelId>/notification-webhook" \
  -H "Authorization: Bearer kpt_xxx"

# Rotate the secret (the old one stops working immediately)
curl -X POST "https://apigw.klinkcx.com/api/v1/webchat/channels/<channelId>/notification-webhook/secret" \
  -H "Authorization: Bearer kpt_xxx"

# Disable notifications
curl -X PUT … -d '{ "url": null }'

The URL must be https and point to a public host.

The request

  • visitor.identity is the value you passed as identity (or email). Use it to find the user's devices.

  • message.preview is at most 120 characters. Non-text messages read "Sent an attachment" or "Sent a message".

  • unreadCount is the number of agent messages the visitor hasn't seen yet, including this one. Use it for the app icon badge. A message counts as seen once it has been on screen while the chat was open and visible.

  • A ping event ({ "event": "ping", "channelId": "…" }) is sent only by the test endpoint.

Verifying the signature

Compute HMAC-SHA256(secret, timestamp + "." + rawBody) over the raw request body bytes (before JSON parsing), and compare it with the signature in constant time. Reject timestamps older than 5 minutes; that stops replays.

Delivery and retries

  • Respond with any 2xx quickly, within 5 seconds, and do the push asynchronously.

  • Network errors, timeouts, 408, 429 and 5xx are retried twice (after about 2 s and 10 s). Other 4xx responses aren't retried.

  • Retries reuse the same X-Klink-Delivery value, so use it to skip duplicates.

  • Delivery is best-effort: in rare cases an event can be lost. Use it for notifications, not as the only record of a message. The full conversation is always in klink.cloud.

  • Redirects aren't followed.

Unread counts in the widget

The chat bubble's unread badge uses the same count, so it stays correct after the page reloads.