Webhook Integration

Receive push notifications when decks change or cards are due for review.

3 min read

Webhooks let your integration receive real-time events instead of polling. When a deck is updated or cards become due, Chamelingo POSTs a signed JSON payload to your endpoint.

Supported Events#

EventFired when
review.dueCards are due for spaced repetition review
deck.updatedA deck is created or modified
deck.deletedA deck is deleted

Register a Webhook#

bash
curl -X POST https://api.chamelingo.com/api/v1/webhooks \
  -H "Authorization: Bearer ck_live_YOUR_KEY_HERE" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://my-bot.example.com/chamelingo-webhook",
    "events": ["deck.updated", "review.due"]
  }'

The response includes a secret — save it immediately, it's shown only once:

JSON
{
  "id": "wh_abc...",
  "url": "https://my-bot.example.com/chamelingo-webhook",
  "events": ["deck.updated", "review.due"],
  "secret": "a1b2c3d4e5f6...",
  "_note": "Save the secret — it will not be shown again."
}

Payload Format#

Payloads arrive as JSON POST requests:

JSON
{
  "event": "deck.updated",
  "timestamp": "2026-03-20T12:00:00.000Z",
  "data": {
    "deck_id": "clxyz123",
    "action": "created"
  }
}

Headers#

HeaderValue
Content-Typeapplication/json
X-Chamelingo-Signaturesha256=<HMAC-SHA256 hex digest>
X-Chamelingo-EventEvent name (e.g., deck.updated)

Verify Signatures#

Always verify the X-Chamelingo-Signature header to ensure payloads are authentic.

TypeScript
import crypto from "crypto";

function verifyWebhook(
  rawBody: string,
  signature: string,
  secret: string,
): boolean {
  const expected =
    "sha256=" +
    crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected),
  );
}

// In your Express handler:
app.post("/chamelingo-webhook", (req, res) => {
  const signature = req.headers["x-chamelingo-signature"];
  if (!verifyWebhook(req.rawBody, signature, WEBHOOK_SECRET)) {
    return res.status(401).send("Invalid signature");
  }
  // Process event...
  res.status(200).send("OK");
});

Test Your Endpoint#

Send a test event to verify connectivity:

bash
curl -X POST https://api.chamelingo.com/api/v1/webhooks/WEBHOOK_ID/test \
  -H "Authorization: Bearer ck_live_YOUR_KEY_HERE"
Warning

Webhook URLs must be HTTPS. Private/internal IP addresses (localhost, 10.x, 172.16-31.x, 192.168.x) are blocked for security.

Manage Webhooks#

bash
# List all webhooks
curl https://api.chamelingo.com/api/v1/webhooks \
  -H "Authorization: Bearer ck_live_YOUR_KEY_HERE"

# Delete a webhook
curl -X DELETE https://api.chamelingo.com/api/v1/webhooks/WEBHOOK_ID \
  -H "Authorization: Bearer ck_live_YOUR_KEY_HERE"

Maximum 10 active webhooks per user account.