DEVELOPERS · API & WEBHOOKS

Build on ParentLink. Self-serve API, no sales call.

A documented REST API over your nursery’s data — children, attendance, invoices, rota, waiting list — plus outbound webhooks that fire the instant something happens. Scoped bearer tokens, per-call audit trail, fair rate limits, HMAC-signed events. Most UK competitors lock this behind their Enterprise tier; we ship it self-serve.

QuickstartWebhooksOpenAPI spec ↓
Read-only & scoped — least privilege per integration Per-token rate limit + full audit log Webhooks signed with HMAC-SHA256
QUICKSTART

Three steps to your first call

1
Mint a token
In ParentLink, go to Settings → API tokens (manager / admin only), click Mint new token, pick the scopes the integration needs, and copy the pl_… secret. It’s shown once — store it securely.
2
Call the API
Send the token as a bearer header against your project’s API base URL. Every response is scoped to that token’s nursery — a token can never read another nursery’s data.
3
Subscribe to events
For push instead of poll, add a webhook in Settings → Webhooks, choose the events, and verify the signature on each delivery. Jump to webhooks ↓

Authentication

All requests use a bearer token. The base URL is your Supabase project’s functions domain — your nursery’s admin can confirm it, or read it from the curl snippet shown when you mint a token.

# Base URL
https://<your-project>.functions.supabase.co/api-v1

# Authenticated request
curl https://<your-project>.functions.supabase.co/api-v1/children \
  -H "Authorization: Bearer pl_XXXXXXXX..."

Hitting the root (/api-v1) with no resource returns a discovery document listing every endpoint and its required scope — handy for exploration.

ENDPOINTS

Five read-only resources

v1 is read-only by design — the lower-risk surface that covers the integrations nurseries actually ask for (accounting sync, payroll, council reporting, website “spaces” widgets, CRM dashboards). Each endpoint requires its own scope, so a Xero connector only ever sees invoices and a payroll tool only ever sees the rota.

EndpointScopeQuery paramsReturns
GET /childrenchildren:readlimit, offsetActive + archived children: name, DOB, room, start date, allergies, dietary, SEND, funded entitlement.
GET /attendanceattendance:readlimit, offset, from, toSign-in / sign-out sessions per child per day. Filter by date range.
GET /invoicesinvoices:readlimit, offsetPer-child billing roll-up: total due, total paid, outstanding, weeks billed / paid.
GET /rotarota:readlimit, offset, from, toStaff rota rows per ISO week. Filter by week_start range.
GET /waiting-listwaiting-list:readlimit, offsetEnquiry pipeline: child + parent name, email, phone, stage, status.

Sample response

List endpoints return a consistent envelope with the rows plus pagination metadata.

GET /api-v1/children?limit=2

{
  "data": [
    {
      "id": "c1a2…",
      "nursery_id": "n9f8…",
      "room_id": "r3…",
      "first_name": "Ava",
      "last_name": "Walsh",
      "dob": "2023-05-20",
      "start_date": "2025-03-06",
      "active": true,
      "allergies": "Eggs",
      "dietary": "Egg-free",
      "send": false,
      "funded_entitlement": { "hours": 15 }
    }
  ],
  "total": 27,
  "limit": 2,
  "offset": 0
}
CONVENTIONS

Pagination, limits & errors

Pagination
?limit= (1–500, default 100) and ?offset= (default 0). Responses include total so you can page through the full set.
Rate limits
Per-token, defaulting to 60 requests/minute (configurable when you mint). Over the limit returns 429 with a Retry-After header. Every call is logged for the manager to audit usage.
Tenant scoping
A token is bound to one nursery at mint time. Queries are always filtered to that nursery — there is no cross-tenant access even with a valid token and the right scope.
Errors
401 missing / invalid / revoked / expired token · 403 token lacks the endpoint’s scope · 404 unknown resource · 429 rate limited. Errors return JSON: { "error": "…" }.
WEBHOOKS

Push, not poll

Add an endpoint at Settings → Webhooks, choose the events you care about, and ParentLink POSTs a signed JSON payload the moment each one fires. Delivery is asynchronous with automatic retry and exponential backoff; an endpoint that fails repeatedly is auto-disabled so a dead URL never churns. Re-enable it once the receiver is fixed.

Events

EventFires whenPayload fields
child.signed_inA child is signed in (any source: kiosk, staff, parent).attendance_id, child_id, date, session, signed_in_at
child.signed_outA child is signed out.attendance_id, child_id, date, session, signed_out_at
enquiry.createdA new waiting-list enquiry is created.enquiry_id, child_name, child_dob, parent_name, parent_email, parent_phone, stage_id, status
incident.loggedAn accident / incident is recorded.incident_id, child_id, record_type, date, severity, riddor_reportable
invoice.paidA weekly fee is paid in full (Direct Debit, card or manual).child_id, week_mon, amount, paid_method, provider_payment_id, paid_at

Delivery shape

Every delivery is the same envelope. The event-specific fields live under data.

POST  (your endpoint URL)
Content-Type: application/json
X-ParentLink-Event: invoice.paid
X-ParentLink-Delivery: 7c1e…           # unique per delivery
X-ParentLink-Signature: sha256=<hex>   # see "Verifying" below
X-ParentLink-Timestamp: 1759050842      # unix seconds, when this attempt was signed
X-ParentLink-Signature-V2: t=1759050842,sha256=<hex>
User-Agent: ParentLink-Webhooks/1

{
  "id": "7c1e…",
  "event": "invoice.paid",
  "nursery_id": "n9f8…",
  "created_at": "2026-06-07T09:14:02Z",
  "data": {
    "child_id": "c1a2…",
    "week_mon": "2026-06-01",
    "amount": 90,
    "paid_method": "dd",
    "provider_payment_id": "PM0001…",
    "paid_at": "2026-06-07T09:14:02Z"
  }
}

Verifying the signature

Compute HMAC-SHA256(secret, rawRequestBody) as lowercase hex and compare it (constant-time) to the value after sha256= in the X-ParentLink-Signature header. The secret is shown once when you create or rotate the endpoint. Always verify against the raw body bytes — not a re-serialized object.

To refuse replays, verify X-ParentLink-Signature-V2 instead: compute HMAC-SHA256(secret, timestamp + "." + rawRequestBody), where the timestamp is the t= value, compare it to the sha256= value, and reject a delivery whose timestamp is more than five minutes old.

// Node.js (Express) — verify a ParentLink webhook
import express from 'express'
import crypto from 'crypto'

const WEBHOOK_SECRET = process.env.PARENTLINK_WEBHOOK_SECRET // whsec_…

const app = express()
// Capture the RAW body for signature verification.
app.use(express.raw({ type: 'application/json' }))

app.post('/hooks/parentlink', (req, res) => {
  const sig = req.header('X-ParentLink-Signature') || ''
  const expected = 'sha256=' + crypto
    .createHmac('sha256', WEBHOOK_SECRET)
    .update(req.body)            // req.body is a Buffer (raw)
    .digest('hex')

  const ok = sig.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))
  if (!ok) return res.status(401).send('bad signature')

  const event = JSON.parse(req.body.toString('utf8'))
  // … handle event.event / event.data …
  res.sendStatus(200)   // 2xx = delivered; anything else triggers retry
})

Respond 2xx to acknowledge. Any non-2xx (or a timeout past 10s) is treated as a failure and retried with backoff. Deliveries are at-least-once — dedupe on X-ParentLink-Delivery if your handler isn’t idempotent.

FAQ

Common questions

Is the API read-only?
v1 is read-only (GETs). Writes are higher-risk and deferred — tell us the workflow you need and we'll prioritise it. Webhooks already cover the common 'react to a change' use case without write access.
Who authenticates — the nursery or the parent?
The nursery. Tokens are minted by a manager / admin and scoped to that nursery's own data. This is not a parent-level API — it's for the nursery's own integrations (accounting, payroll, website, internal dashboards).
What can I build?
Accounting sync (read invoices into Xero / QuickBooks), payroll (attendance + rota → hours), a 'spaces available' widget on the nursery website, council / local-authority attendance feeds, a kitchen board that reacts to sign-ins with allergies, and multi-site dashboards across nurseries.
How do I rotate a leaked token?
Revoke it in Settings → API tokens (takes effect immediately — the token starts returning 401) and mint a new one. For webhooks, use Rotate secret on the endpoint; the old signing secret stops validating at once.
What are the rate limits?
Per-token, default 60 requests/minute, configurable 1–1000 when you mint. A 429 includes Retry-After. For high-volume sync, prefer webhooks over tight polling.
Do webhooks guarantee delivery?
At-least-once with retry + exponential backoff, then the delivery is marked dead after several attempts. Endpoints that fail many times in a row auto-disable. Watch deliveries (status, code, retry time) in Settings → Webhooks.

Ship your integration this afternoon.

Mint a scoped token, hit five endpoints, subscribe to five events. No gatekeeping, no Enterprise upsell — it’s in the product.

Start free trialOpenAPI spec