Enterprise: the API, webhooks and MCP server are live for schools on the Enterprise plan. The owner allows every key and AI connection. Ask about Enterprise

On this page

Webhooks / Events

Webhook events

When something happens at your school, Swum sends a signed POST to your endpoint asynchronously. Delivery can be retried and may arrive out of order.

Add an endpoint

  1. In the Swum app, open Settings, Developers & Integrations, Webhooks.
  2. Tap Add Endpoint and paste a public HTTPS address on port 443. Private, loopback, metadata/internal addresses and redirects are rejected.
  3. Pick the events you want and, if you like, only some pools.
  4. Copy the signing secret (whsec_…). You need it to check each event.

For private connectors, use the dedicated app key and its app-owned subscription routes. General API keys and OAuth agents cannot create these subscriptions. Then use Send Test Event to verify delivery with the real signing secret. Endpoints are owner-managed and may be restricted to selected locations; records without a permitted location are not delivered to location-restricted endpoints.

What an event looks like

What Swum sendsPOST
POST /swum/events HTTP/1.1
Host: hooks.example.test
Content-Type: application/json
Swum-Event-Id: 8b04e26e…
Swum-Event-Type: lesson.booked
Swum-Signature: t=1792936800,v1=5257a869e7ec…

{
  "id": "8b04e26e…",
  "type": "lesson.booked",
  "created": "2026-10-25T14:00:00.000Z",
  "owner_id": "owner_example",
  "data": {
    "family_id": "family_example",
    "lesson_id": "lesson_example",
    "swimmer_ids": ["swimmer_example"],
    "status": "booked",
    "location_id": "pool_example"
  }
}
  • id: the event's id. The same on every retry.
  • type: what happened. Also in the Swum-Event-Type header.
  • created: the source event time, in UTC.
  • owner_id: the school owner id.
  • data: the short version of the record. Fetch the full one from the API if you need it.

Check Swum-Signature before you trust it. Here's how.

Event types

There are 17 supported event types. Team events require team-area access on app keys; subscriber scopes and locations still apply. client.created and family.created can describe the same family creation through different emitters. Choose the event appropriate to your workflow and keep downstream effects idempotent when consuming both.

EventWhenData
client.createdA school roster family is created; silent imports are skipped.family_id, name, status; optional location_id
lesson.bookedA real lesson becomes booked; unfinished checkout drafts are excluded.family_id, lesson_id, swimmer_ids, status; optional location_id
lesson.canceledAn authoritative lesson cancellation archive is created.family_id, lesson_id, status; optional location_id
enrollment.createdA new swimmer appears on a class occurrence’s participant record.family_id, swimmer_id, name, class_id, instance_id, status; optional location_id
payment.succeededAn owner payment source transitions to successful payment.family_id, payment_id, status, currency; optional invoice_id, amount_minor, location_id
payment.failedAn owner payment source transitions to failed payment.family_id, payment_id, status, currency; optional invoice_id, amount_minor, location_id
attendance.markedA class check-in or supported parent/staff absence is recorded.family_id, class_id, instance_id, status; swimmer_ids for check-in, swimmer_id/name for absence; optional location_id/source
waiver.signedA sealed waiver signing envelope is created.family_id, swimmer_ids, waiver_id, document_id, status; optional location_id
skill.completedThe school progression record marks a skill complete.family_id, swimmer_id, name, level_id, skill_id, status; optional location_id
level.advancedA progress level or participant-group placement advances.family_id, swimmer_id, status; level_id/previous_level_id or group_id/previous_group_id/source; optional name/location_id
family.createdAn integration family-create mutation commits; distinct from the general client.created roster event.family_id, swimmer_id, class_id, request_id, status, location_id; unused ids/status may be empty
swimmer.updatedAn integration swimmer-create or swimmer-update mutation commits.family_id, swimmer_id, class_id, request_id, status, location_id; unused ids/status may be empty
enrollment.requestedThe integration records a canonical family enrollment request; this is not enrollment confirmation.family_id, swimmer_id, class_id, request_id, status, location_id
meet.entry.createdAn individual canonical Team entry is created; family confirmation/payment are separate.meet_id, family_id, swimmer_id, status, location_id
meet.entry.paidAn immutable family meet request first becomes paid with canonical payment evidence.request_id, meet_id, family_id, swimmer_ids, status, amount_minor, currency, location_id
meet.results.importedA meet records a new results import id; repeated writes of the same import do not emit again.meet_id, status, location_id
swim.best_timeA newly created non-DQ recorded swim is marked newBest; status also indicates a new cut when recorded.time_id, meet_id, family_id, swimmer_id, course, distance, stroke, seconds, status, location_id

Good habits

  • Answer with a 2xx within 10 seconds, then do the slow work.
  • Store event ids you have handled and skip repeats. A retry can arrive after a success.
  • Events can arrive out of order. Use created, or fetch the record, to get the latest.
TipPayloads include selected ids, names and status fields, never contact details or medical data. Silent imports suppress their importing write. Events and delivery logs are retained for 30 days. Private Zapier, Make and n8n packages use app-owned subscriptions and platform-specific verification. See Private Connectors; they are not public marketplace listings.