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.
pl_… secret. It’s shown once — store it securely.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.
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.
| Endpoint | Scope | Query params | Returns |
|---|---|---|---|
GET /children | children:read | limit, offset | Active + archived children: name, DOB, room, start date, allergies, dietary, SEND, funded entitlement. |
GET /attendance | attendance:read | limit, offset, from, to | Sign-in / sign-out sessions per child per day. Filter by date range. |
GET /invoices | invoices:read | limit, offset | Per-child billing roll-up: total due, total paid, outstanding, weeks billed / paid. |
GET /rota | rota:read | limit, offset, from, to | Staff rota rows per ISO week. Filter by week_start range. |
GET /waiting-list | waiting-list:read | limit, offset | Enquiry pipeline: child + parent name, email, phone, stage, status. |
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
}?limit= (1–500, default 100) and ?offset= (default 0). Responses include total so you can page through the full set.429 with a Retry-After header. Every call is logged for the manager to audit usage.401 missing / invalid / revoked / expired token · 403 token lacks the endpoint’s scope · 404 unknown resource · 429 rate limited. Errors return JSON: { "error": "…" }.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.
| Event | Fires when | Payload fields |
|---|---|---|
child.signed_in | A child is signed in (any source: kiosk, staff, parent). | attendance_id, child_id, date, session, signed_in_at |
child.signed_out | A child is signed out. | attendance_id, child_id, date, session, signed_out_at |
enquiry.created | A new waiting-list enquiry is created. | enquiry_id, child_name, child_dob, parent_name, parent_email, parent_phone, stage_id, status |
incident.logged | An accident / incident is recorded. | incident_id, child_id, record_type, date, severity, riddor_reportable |
invoice.paid | A weekly fee is paid in full (Direct Debit, card or manual). | child_id, week_mon, amount, paid_method, provider_payment_id, paid_at |
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"
}
}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.
Mint a scoped token, hit five endpoints, subscribe to five events. No gatekeeping, no Enterprise upsell — it’s in the product.