Registering an endpoint
- Add an endpoint to the app in the Developer section and pick the events it receives. An app can have up to ten endpoints.
- The URL must be HTTPS on a public address. Private and internal addresses are refused.
- Each endpoint has its own signing secret, starting whsec_, shown when you create or rotate it.
- Changing an endpoint URL later needs an administrator to approve, because it redirects school data.
- Send test event delivers a sample of any event, marked with the header Klasstack-Test-Event: true, so you can build the receiver before real traffic.
The payload
{
"id": "evt_0123456789abcdef0123456789abcdef",
"type": "student.created",
"occurred_at": "2026-09-01T08:00:00.000Z",
"api_version": "v1",
"data": { "id": 812 }
}data is shaped exactly as the matching read route returns it, so an event never carries a field the API would not. The reference lists every event schema.
Verifying the signature
Every delivery carries Klasstack-Signature: t=<unix seconds>,v1=<hex>. v1 is HMAC-SHA256, keyed with the endpoint whsec_ secret, over the timestamp, a full stop and the raw request body. Compute it yourself, compare in constant time, and reject a timestamp more than 300 seconds old.
# header: Klasstack-Signature: t=1788249600,v1=5f2b...
signed_payload="1788249600.$raw_body"
expected=$(printf '%s' "$signed_payload" | openssl dgst -sha256 -hmac "$WHSEC_SECRET" -hex | cut -d' ' -f2)
# accept only if expected equals v1 and the timestamp is within 300 secondsRetries
- Any 2xx answer acknowledges the event. Answer quickly and do the work afterwards: a delivery times out after 10 seconds.
- A timeout, a network error, 408, 429 or any 5xx is retried with exponential backoff and jitter, starting at about 30 seconds and capped at 4 hours between attempts, for up to 10 attempts.
- A Retry-After header on your 429 or 503 is honoured, up to one hour.
- Any other status, including a redirect, is not retried.
- Deliveries can arrive out of order and more than once. Dedupe on id: a retry or a replay sends the same id.
Delivery log and replay
The Developer section keeps a log of every delivery with its attempts and your answers. Replay any delivery from there once your endpoint is fixed. A replay carries the original event id.
Every event
An endpoint receives an event only while the app holds the read scope listed next to it. This table is generated from the API reference.
| Event | Scope needed | When it fires |
|---|---|---|
| student.created | students:read | A student joined the roll: enrolled, or an applicant admitted. |
| student.updated | students:read | An on-roll student's profile changed. |
| student.withdrawn | students:read | A student left the roll (withdrawn, graduated or expelled). Carries the id only. |
| guardian.linked | guardians:read | A guardian was linked to an on-roll student. |
| class.enrolment_changed | students:read | An on-roll student moved to another class or arm. |
| attendance.marked | attendance:read | Attendance was marked or changed. One event per register write. |
| result.published | results:read | A subject's results were published for a term. |
| assignment.created | assignments:read | An assignment was published to its class. |
| timetable.updated | timetable:read | A class timetable was published. |
| payment.succeeded | fees:read | An online fee payment settled through the school provider. Never sent for a manual payment. |
| fee_account.updated | fees:read | A settled online payment changed an on-roll student's fee account. Account totals only, no items. |
| withdrawal.approved | moneyOut:read | A platform admin approved a queued withdrawal; the payout is in flight. |
| withdrawal.rejected | moneyOut:read | A queued withdrawal was declined by a platform admin or at the end of the approval window; funds returned. |
| withdrawal.cancelled | moneyOut:read | The school cancelled a withdrawal still awaiting approval; funds returned. |
| withdrawal.completed | moneyOut:read | The provider confirmed a withdrawal's payout. |
| withdrawal.failed | moneyOut:read | The provider failed a withdrawal's payout; funds returned. |
| withdrawal.reversed | moneyOut:read | The provider returned a completed withdrawal's payout; funds credited back. |