Session Lifecycle Webhooks
Receive signed HTTP events when consumers start or end a chat session, including the URL query params they arrived with.
Session webhooks let you receive a signed HTTP POST to your own endpoint whenever a consumer starts or ends a chat session on your Chipp app. This lets you track conversation starts and ends deterministically, without depending on the AI to call a custom action.
Session webhooks are the deterministic complement to {{system.query.*}} variables. The variables let your AI agent pass attribution data to a custom action at the right moment in the conversation. Session webhooks give you the same data automatically, at session start, with no model involvement.
When to Use Session Webhooks
Use session webhooks when you need reliable, automatic notification that a session started or ended, such as:
- Logging every session start to your analytics or CRM (no matter what the AI does)
- Binding a tokenized URL parameter (like
?rp_token=abc123) to a session record in your system - Triggering a workflow when a user opens the chat
For actions that depend on what happened in the conversation (a form submitted, a product selected, a service requested), use custom actions instead, since those let the AI decide when to fire based on context.
Configure the Webhook
- Open your app in the Chipp builder.
- Go to Access in the left navigation.
- Scroll to the Session Webhooks section.
- Enter your endpoint URL (must be
https://). - Choose which events to receive:
session.created,session.ended, or both. - Click Save.
On first save, Chipp generates a signing secret and shows it once. Copy it immediately and store it somewhere secure, like an environment variable. You will not be able to retrieve it again. To replace it later, use the rotate button.
Send a Test Event
After saving, click Send test event. Chipp posts a synthetic session.created payload flagged with "test": true to your endpoint so you can confirm it receives and verifies the signature before relying on it in production.
Payload Shape
Both event types share the same envelope. Fields specific to session.ended are noted.
{
"event": "session.created",
"applicationId": "550e8400-e29b-41d4-a716-446655440000",
"sessionId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"source": "web",
"consumerId": "c3f8b2a0-1234-5678-abcd-ef0123456789",
"externalId": "user-from-crm-456",
"queryParams": [
{ "name": "rp_token", "value": "abc123" },
{ "name": "ab_variant", "value": "B" }
],
"startedAt": "2026-07-09T14:22:31.000Z",
"timestamp": "2026-07-09T14:22:31.412Z"
}For session.ended, the payload also includes endedAt:
{
"event": "session.ended",
"endedAt": "2026-07-09T14:45:02.000Z",
...
}Field Reference
| Field | Type | Description |
|---|---|---|
event | string | "session.created" or "session.ended" |
applicationId | UUID | Your app’s ID |
sessionId | UUID | The chat session ID (stable, matches the Conversations tab) |
source | string | How the session was initiated: "web", "widget", "whatsapp", "slack", etc. |
consumerId | UUID or null | The consumer’s ID, or null for anonymous sessions before sign-in |
externalId | string or null | The value of ?chipp_external_id= from the URL (colon-free values only, see below) |
queryParams | array | All captured URL query params as [{name, value}] pairs (excludes Chipp internal params like bt and session) |
startedAt | ISO 8601 | When the session was created |
endedAt | ISO 8601 or absent | When the session ended (only on session.ended) |
timestamp | ISO 8601 | When this webhook was fired |
URL Query Param Capture
When a consumer opens your Chipp app with URL query parameters, those parameters are captured on the session automatically. They appear in the webhook payload under queryParams as an array of {name, value} objects.
For example, if a consumer arrives via:
https://your-app.on.chipp.ai?rp_token=abc123&ab_variant=BThe webhook payload will include:
"queryParams": [
{ "name": "rp_token", "value": "abc123" },
{ "name": "ab_variant", "value": "B" }
]The externalId Field
The ?chipp_external_id= query parameter is special. Chipp promotes it to the first-class externalId field on the session record, which also appears in the Conversations tab and all data exports.
Restrictions: the value must not contain a colon (:) so it cannot collide with Chipp’s internal channel namespaces (like webhook:, whatsapp:, voice:). If the value contains a colon it is still captured in queryParams.chipp_external_id but not promoted to externalId.
The same variables are also available inside the agent at {{system.external_id}} and {{system.query.rp_token}}. Session webhooks fire these values deterministically at session start, before the first AI turn.
Verifying Signatures
Chipp signs every webhook payload with HMAC-SHA256 using your signing secret. The signature is sent in the X-Chipp-Signature header as sha256=<hex>.
Always verify the signature before acting on a webhook payload. This prevents replay attacks and ensures the request came from Chipp.
Node.js Verification Example
import crypto from "node:crypto";
function verifyChippSignature(rawBody, signatureHeader, secret) {
const expected = "sha256=" + crypto
.createHmac("sha256", secret)
.update(rawBody)
.digest("hex");
// Use a constant-time comparison to prevent timing attacks
const a = Buffer.from(signatureHeader);
const b = Buffer.from(expected);
if (a.length !== b.length) return false;
return crypto.timingSafeEqual(a, b);
}
// Express example
app.post("/session-webhook", express.raw({ type: "application/json" }), (req, res) => {
const sig = req.headers["x-chipp-signature"];
if (!verifyChippSignature(req.body, sig, process.env.CHIPP_WEBHOOK_SECRET)) {
return res.status(401).json({ error: "Invalid signature" });
}
const event = JSON.parse(req.body);
// Handle event...
res.json({ ok: true });
});Python Verification Example
import hmac
import hashlib
def verify_chipp_signature(raw_body: bytes, signature_header: str, secret: str) -> bool:
expected = "sha256=" + hmac.new(
secret.encode(),
raw_body,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature_header, expected)Always verify against the raw request body bytes, not a parsed JSON object. Re-serializing the parsed object can produce different byte ordering, causing verification to fail.
Request Headers
Chipp includes these headers on every webhook request:
| Header | Value |
|---|---|
Content-Type | application/json |
X-Chipp-Signature | sha256=<hmac-sha256-hex> |
X-Chipp-Event | session.created or session.ended |
User-Agent | Chipp-Session-Webhook/1.0 |
Reliability Notes
session.created
Fires reliably for every new session, regardless of channel or auth mode. Anonymous web sessions, widget sessions, WhatsApp, Slack, and email sessions all produce a session.created event.
session.ended
Fires when a session is explicitly ended or deleted, for example when a consumer clears their history or a session is cleaned up server-side. Abandoned web sessions do not trigger session.ended because a user closing a browser tab is not detectable server-side. Plan accordingly: use session.created for start-of-conversation attribution, and treat the absence of session.ended as normal for web sessions.
Delivery
Chipp makes a best-effort delivery with a 10-second timeout. Failed deliveries are not automatically retried. If your endpoint is unavailable, the event is lost. Design your endpoint to be idempotent (use sessionId + event as a dedup key) since at-least-once delivery means duplicates are possible in rare conditions.
Your endpoint must return a 2xx status within 10 seconds. Slow responses that time out are logged at warn level and treated as delivery failures.
Rotating the Signing Secret
To rotate your signing secret:
- Click the rotate button (circular arrow) next to the signing secret field.
- Copy the new secret immediately. It is shown only once.
- Update your endpoint to accept the new secret.
During the rotation window, requests signed with the old secret will fail verification at your endpoint. Brief downtime is expected. Rotate during low-traffic periods if that is a concern.
Related
- Custom actions - let the AI call your endpoints based on conversation context
- System variables - access
{{system.query.NAME}}and{{system.external_id}}inside the agent - Inbound webhooks - the reverse direction: send messages into your app from external systems