Skip to main content
This is the beta webhook API, available in open beta. For the existing webhook system, see Legacy webhooks.
This guide takes you from zero to a verified, production-shaped webhook delivery in five minutes. Read Overview for the underlying delivery semantics, payload anatomy, and versioning policy.

Before you start

You’ll need:
  • A Quo Public API key with permission to manage webhooks.
  • An HTTPS endpoint you control. For local development, use a tunnel such as ngrok or cloudflared.
  • A runtime that gives you the raw, unparsed request body. This is required for signature verification — see Validate webhook signatures for framework-specific notes.
You can send a real, signed test event before any code is written using POST /webhooks/:id/events/test. See step 4 below.

1. Create the webhook

Pin the subscription to the current API version with x-quo-api-version. The version is recorded once when the webhook is created and used for every subsequent delivery.
curl https://api.quo.com/webhooks \
  -X POST \
  -H "Authorization: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "x-quo-api-version: 2026-03-30" \
  -d '{
    "url": "https://example.com/webhooks/quo",
    "events": ["message.received"],
    "label": "Quickstart webhook"
  }'
Save the key field from the response — that’s your whsec_… signing secret. Treat it like a credential: store it as an environment variable, never in source.

2. Receive the delivery

Each delivery sets three headers and a JSON body. Verify the signature against the raw bytes of the body, then parse.
HeaderPurpose
webhook-idStable delivery identifier. Use as your idempotency key.
webhook-timestampUnix seconds when Quo signed the request.
webhook-signatureSpace-separated v1,<base64-signature> entries.

3. Verify and handle the event

The Svix SDK accepts Quo’s headers and whsec_… key format unchanged. Reject any delivery whose timestamp is more than five minutes off from your server clock to defeat replay.
import { Webhook } from "svix"
import express from "express"

const app = express()
const secret = process.env.QUO_WEBHOOK_KEY ?? ""

app.post(
  "/webhooks/quo",
  express.raw({ type: "application/json" }),
  (req, res) => {
    const deliveryId = req.header("webhook-id")!
    const headers = {
      "webhook-id": deliveryId,
      "webhook-timestamp": req.header("webhook-timestamp")!,
      "webhook-signature": req.header("webhook-signature")!,
    }

    const wh = new Webhook(secret)
    const event = wh.verify(req.body, headers) as { id: string; type: string }

    if (alreadyProcessed(deliveryId)) return res.status(200).end()
    markProcessed(deliveryId)

    handle(event)
    res.status(200).end()
  }
)
Return 200 to acknowledge. Any non-2xx response triggers a retry.

4. Send a test event

Trigger a real, signed delivery to your endpoint without waiting for a real call or message. The response also includes the sample payload inline so you can confirm what your endpoint received.
curl https://api.quo.com/webhooks/{id}/events/test \
  -X POST \
  -H "Authorization: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "x-quo-api-version: 2026-03-30" \
  -d '{ "eventType": "message.received" }'
If verification fails, the most common cause is a framework that parsed the JSON before your handler saw the bytes. See Validate webhook signatures.

5. Inspect deliveries

Every delivery is recorded. Use these endpoints to debug a missing or failing event:
  • GET /webhooks/:id/events — recent deliveries with status.
  • GET /webhooks/:id/events/:eventId — request body, all attempts, response codes.
  • POST /webhooks/:id/events/:eventId/retry — manually retry a failed delivery.

Next steps