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:
The channel has a webhook URL configured (see Setup).
The visitor has a verified identity. They opened the chat with an
identity(oremail) and a validuserHash(see Identity Verification (Secure User Login)). Guests and unverified identity claims are never notified. Otherwise anyone could claim someone else'sidentityand have their own conversation's text pushed to that person's phone.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.identityis the value you passed asidentity(oremail). Use it to find the user's devices.message.previewis at most 120 characters. Non-text messages read "Sent an attachment" or "Sent a message".unreadCountis 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
pingevent ({ "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
2xxquickly, within 5 seconds, and do the push asynchronously.Network errors, timeouts,
408,429and5xxare retried twice (after about 2 s and 10 s). Other4xxresponses aren't retried.Retries reuse the same
X-Klink-Deliveryvalue, 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.