Talki

Platform

Webhooks

Talki sends HTTPS POST requests to your endpoint when ticket events occur. Use webhooks to sync with your CRM, trigger Slack alerts, update analytics pipelines, or run any automation based on support activity.

Registering an endpoint

bash
# In Admin → Webhooks → Add endpoint
# Enter your HTTPS URL and select which events to subscribe to.
# Talki will show you a signing secret — store it in your env vars.

HTTPS required

Webhook endpoints must use HTTPS. HTTP endpoints are rejected. For local development, use a tunnel like ngrok or cloudflared.

Event types

EventWhen fired
ticket.createdFired when a new ticket is created via any channel.
message.sentFired when a customer or agent sends a message in any ticket.

Payload format

Every webhook delivery is a POST with a JSON body and these request headers:

NameTypeDescription
X-Talki-SignaturestringHMAC-SHA256 signature of the raw body, prefixed with "sha256=". Use this to verify authenticity.
X-Talki-EventstringThe event type (e.g. "ticket.created").
X-Talki-DeliverystringA unique ID for this delivery attempt. Use for idempotency and deduplication.

ticket.created example

http
POST /your-webhook-endpoint HTTP/1.1
Content-Type: application/json
X-Talki-Signature: sha256=a3f8c2d1...
X-Talki-Event: ticket.created
X-Talki-Delivery: del_abc123

{
  "event":    "ticket.created",
  "createdAt": "2024-03-15T14:23:00Z",
  "data": {
    "ticket": {
      "id":        "tkt_abc123",
      "orgId":     "org_xyz",
      "status":    "open",
      "channel":   "widget",
      "subject":   "Can't log in to my account",
      "createdAt": "2024-03-15T14:23:00Z",
      "customer": {
        "name":  "Alice Johnson",
        "email": "alice@example.com"
      }
    }
  }
}

message.sent example

json
{
  "event":    "message.sent",
  "createdAt": "2024-03-15T14:58:00Z",
  "data": {
    "ticket": { "id": "tkt_abc123", "status": "open", ... },
    "message": {
      "id":        "msg_002",
      "role":      "customer",
      "text":      "Hi, I still can't log in.",
      "createdAt": "2024-03-15T14:58:00Z"
    }
  }
}

Signature verification

Talki signs every webhook with HMAC-SHA256 using your endpoint's signing secret. The signature is in the X-Talki-Signature header, prefixed with sha256=. Always verify signatures before processing events.

Node.js / Express

javascript
const crypto = require('crypto');

app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['x-talki-signature'];
  const [, receivedHex] = signature.split('=');  // "sha256=abc123..."

  const expectedHex = crypto
    .createHmac('sha256', process.env.TALKI_WEBHOOK_SECRET)
    .update(req.body)   // raw Buffer
    .digest('hex');

  const valid = crypto.timingSafeEqual(
    Buffer.from(receivedHex),
    Buffer.from(expectedHex),
  );
  if (!valid) return res.status(401).send('Invalid signature');

  const event = JSON.parse(req.body);
  console.log('Received event:', event.event);

  // Handle events
  if (event.event === 'ticket.created') {
    // e.g. create CRM record, send Slack notification
  }

  res.status(200).send('ok');
});

Python / Flask

python
import hmac, hashlib, os, json
from flask import Flask, request

app = Flask(__name__)

@app.route('/webhook', methods=['POST'])
def webhook():
    signature = request.headers.get('X-Talki-Signature', '')
    _, received_hex = signature.split('=')

    expected_hex = hmac.new(
        os.environ['TALKI_WEBHOOK_SECRET'].encode(),
        request.get_data(),
        hashlib.sha256,
    ).hexdigest()

    if not hmac.compare_digest(received_hex, expected_hex):
        return 'Invalid signature', 401

    event = json.loads(request.get_data())
    print('Event:', event['event'])
    return 'ok', 200

Use the raw body

Compute the HMAC over the raw request bytes, not the parsed JSON object. JSON parsers may reorder keys, change whitespace, or modify values — this will break signature verification. In Express, use express.raw() middleware.

Retry policy

Talki considers a delivery successful when your endpoint returns HTTP 2xx within 10 seconds. If the delivery fails (non-2xx, timeout, or connection error), Talki retries with exponential backoff:

AttemptDelay
1st retry30 seconds after failure
2nd retry5 minutes after 1st retry
3rd retry30 minutes after 2nd retry
FinalMarked as failed — visible in Admin → Webhooks → Delivery log

Idempotency

Due to retries, your endpoint may receive the same event more than once. Use the X-Talki-Delivery header as an idempotency key to deduplicate events in your database.