Skip to content

Outbound Webhooks ​

The Developer API lets you subscribe to events that happen inside Kinderpedia so your external system can react in real-time. When a lead is created, a stage moves, a note is added — your endpoint gets POSTed within seconds.

Distinct from the inbound webhooks that Kinderpedia uses internally for things like DocuSign Connect, Meta, WeChat (those are to Kinderpedia from external services, not from Kinderpedia to your systems).

Subscribing ​

Open Developer → Webhooks tab → Add subscription.

Fields:

  • Destination URL — your endpoint (must be HTTPS in production).
  • Events — checkbox list of what to subscribe to.
  • Secret — auto-generated. Used for HMAC signature validation. Shown once; if lost, you have to regenerate.
  • Description (optional).

Click Create. From then on, every matching event POSTs to your URL.

Events available ​

Current event list:

EventWhen firedPayload data
lead.createdNew lead added (form, manual, import, API)Full lead object
lead.updatedLead fields modifiedLead object + changed_fields
lead.archivedLead archived (recoverable){ lead_id, reason }
lead.unarchivedLead restored{ lead_id }
lead.deletedLead permanently deleted{ lead_id }
stage.transitionedLead moved between stages{ lead_id, from_stage, to_stage }
note.createdNote added to a leadFull note object + lead_id
activity.createdActivity addedFull activity object + lead_id
activity.completedActivity marked complete{ lead_id, activity_id, completed_at }
form.submittedPublic form submission lands{ lead_id, form_id, submission_data }

More events ship continuously; the OpenAPI document at /api/v1/openapi.json is the canonical list.

Payload shape ​

Every payload follows the same envelope:

json
{
  "id": "01H8…",
  "event": "lead.created",
  "occurred_at": "2026-05-12T14:32:18+00:00",
  "kindergarten_id": 12,
  "data": {
    "id": 4321,
    "student_name": "Anna Smith",
    "parent_email": "[email protected]",
    ...
  }
}
FieldMeaning
idUnique delivery ID. Use this to dedupe on your side.
eventEvent type (use this to route).
occurred_atISO-8601 timestamp of the event.
kindergarten_idSchool the event belongs to. Always your school.
dataEvent-specific payload.

HMAC signatures ​

Every POST carries:

X-Kinderpedia-Event: lead.created
X-Kinderpedia-Delivery: 01H8…
X-Kinderpedia-Signature: sha256=<hex>

The signature is hmac_sha256(secret, raw_body) — your subscription's secret as the HMAC key, the raw request body as the message. Compare constant-time against the supplied signature.

Verifying (Node.js) ​

js
const crypto = require('crypto')

function verify(rawBody, signatureHeader, secret) {
  const expected = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex')
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signatureHeader)
  )
}

Verifying (Python) ​

python
import hmac, hashlib

def verify(raw_body: bytes, signature_header: str, secret: str) -> bool:
    expected = 'sha256=' + hmac.new(
        secret.encode(),
        raw_body,
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature_header)

Always validate before processing. If you skip validation, anyone with knowledge of your URL can spoof events.

Delivery semantics ​

  • At-least-once. Network flakes or your endpoint returning 5xx will be retried.
  • Async retry queue — deliveries are queued and worked off in the background, so your endpoint being slow never blocks the in-app action that triggered the event.
  • Retry schedule: 1 minute → 5 minutes → 25 minutes. Max 3 retries.
  • Terminal codes: 2xx = success (no retry). 4xx = permanent failure (no retry; dropped to the dead-letter UI). 5xx + network errors = retry.

So if your endpoint is down for an hour, you may get a few duplicates after it comes back up. Use the delivery id to dedupe.

Acknowledgement ​

Just return 2xx. Body content doesn't matter. We treat any 200-299 as success.

If you have heavy work to do (database writes, AI processing, etc.), respond 200 immediately and queue the work. Webhook delivery has a 30s timeout — sustained delays will retry-storm.

Make.com / n8n / Zapier ​

Both Make.com and n8n have webhook trigger nodes that accept inbound POSTs. The flow:

  1. In Make.com / n8n: create a webhook trigger. Copy the auto-generated URL.
  2. In Kinderpedia Developer → Webhooks: paste the URL, pick events, save.
  3. Save and grab the secret (Kinderpedia shows it once).
  4. Configure Make/n8n to validate the secret on each call (most have built-in HMAC support).

Failures ​

Failures go to the Failed deliveries tab in Developer. See Dead-letter UI for management.

Per-school isolation ​

Subscriptions are scoped to your school. Events from other schools never POST to your endpoint — even if you somehow share your URL with another school, their subscription would need its own configuration to start delivering.

Disabling vs deleting ​

The subscriptions list has a toggle to disable a subscription (no deliveries fire, but the config is preserved) and a delete button (configuration removed entirely). Disable is the safer choice when temporarily pausing.

Workflow webhooks vs Developer webhooks ​

Note: the SPA's Workflow Builder also has a "webhook" action type — but those use a different table (external_connections) and a different dispatcher. They're for workflow-internal use, not for general subscribing.

Developer API webhooks (this page) fire on system events regardless of workflow. Workflow webhooks fire only when a specific workflow runs to a "send webhook" step.

You can use either or both, depending on need:

  • "Sync every new lead to my CRM" → Developer webhook on lead.created
  • "Send a custom notification to Slack when Stage X is reached on Pipeline Y" → Workflow webhook

Best practices ​

  • Validate HMAC every time. No exceptions.
  • Dedupe by id. Store seen delivery IDs for 24h.
  • Respond fast (200 within seconds). Queue heavy work.
  • Subscribe to the minimum events you need. Don't subscribe to everything "just in case."
  • Use separate subscriptions for separate use cases. Easier to disable one without affecting the others.

Kinderpedia Admissions Help Center