Skip to main content
Each delivery is a JSON object with a common envelope. The event-specific fields live under data.

Common envelope

FieldTypeDescription
idstringUnique delivery ID, e.g. dlv_01abc…. Same value as the X-Quippy-Delivery-Id header. Use it to dedupe on retries.
typestringThe event type — e.g. exam.completed. Same as the X-Quippy-Event header.
createdISO 8601 stringTimestamp of this delivery attempt.
institutionIdstringYour institution’s ID. Handy if you run one receiver for many tenants.
testboolean (optional)Present and true only for synthetic test deliveries.
dataobjectEvent-specific payload — see below.

Events

{
  "id": "dlv_01abc...",
  "type": "exam.completed",
  "created": "2026-04-20T10:15:30.123Z",
  "institutionId": "inst_acme",
  "data": {
    "examId": "exm_01...",
    "sessionId": "ses_01...",
    "studentId": "usr_01...",
    "score": {
      "percentage": 87,
      "totalScore": 87,
      "maxScore": 100,
      "correct": 26,
      "total": 30
    },
    "submittedAt": "2026-04-20T10:15:29.998Z"
  }
}
Fires once per submission, after the session is scored. studentId may be null for guest / link-based exam modes.
Coming soon. The event type exists in the subscription API — you can select it when registering an endpoint — but the emit site in the grading pipeline is not yet wired up. Subscribing today will not result in any deliveries for this event. Watch for a changelog entry when it ships.
{
  "id": "dlv_01abc...",
  "type": "user.provisioned",
  "created": "2026-04-20T10:15:30.123Z",
  "institutionId": "inst_acme",
  "data": {
    "userId": "usr_01...",
    "email": "jane@acme.com",
    "displayName": "Jane Doe",
    "role": "member",
    "accessLevel": "user",
    "provisionedAt": "2026-04-20T10:15:29.998Z"
  }
}
Fires when an admin invites a user via POST /api/v2/users/invite (or the Members page in the admin portal). For SSO-auto-provisioned users, it fires on their first successful SSO sign-in.
{
  "id": "dlv_01abc...",
  "type": "user.deprovisioned",
  "created": "2026-04-20T10:15:30.123Z",
  "institutionId": "inst_acme",
  "data": {
    "userId": "usr_01...",
    "email": "jane@acme.com",
    "displayName": "Jane Doe",
    "deactivatedAt": "2026-04-20T10:15:29.998Z"
  }
}
Fires when an admin soft-deletes a user from the institution.
{
  "id": "dlv_01abc...",
  "type": "subscription.updated",
  "created": "2026-04-20T10:15:30.123Z",
  "institutionId": "inst_acme",
  "data": {
    "userId": "usr_01...",
    "stripeSubscriptionId": "sub_1Nx...",
    "status": "active",
    "cancelAtPeriodEnd": false,
    "currentPeriodEnd": "2026-05-20T10:15:29.000Z",
    "planId": "plan_pro_monthly"
  }
}
Fires when Stripe tells Quippy a subscription has changed — active, past_due, canceled, etc. planId may be null if the plan isn’t recorded on our side.
{
  "id": "dlv_01abc...",
  "type": "webhook.test",
  "created": "2026-04-20T10:15:30.123Z",
  "test": true,
  "institutionId": "inst_acme",
  "data": {
    "message": "This is a synthetic test delivery from Quippy.",
    "at": "2026-04-20T10:15:30.000Z"
  }
}
Fires exactly once when you click Send test on an endpoint’s detail page. Test deliveries are not retried on failure — they’re a one-shot check.

Delivering to multiple endpoints

If you subscribe two endpoints to the same event, each gets its own delivery row with a distinct id — they don’t share attempts or retry state.