Integration
One endpoint in, signed webhooks out.
For the person who will connect your policy administration system. The full reference is in the API docs. This page is the shape of it.
1. The events API
POST /api/v1/events with a tenant API key. Polisync records the contact and policy, runs the playbook and answers 202 with the event id.
Idempotent. Every event carries an idempotency_key. Send the same key again and you get the original event back with a 200, and no second message. Retry as often as your integration needs to.
POPIA guard. Polisync stores only what a message needs. A body containing keys such as id_number, idnumber, identity_number, national_id, passport, passport_number, account_number, bank_account is rejected with 422, at any depth, and only the key path is logged.
Amounts are integer cents in rand. The limit is 600 requests a minute per tenant.
Event types
debit_order_failedDebit order failedpremium_duePremium duepolicy_renewal_dueRenewal dueclaim_status_changedClaim status changeddocument_requestedDocument requestedpolicy_welcomePolicy welcomepayment_recoveredPayment recoveredpolicy_lapsedPolicy lapsed
{
"type": "debit_order_failed",
"idempotency_key": "dof-POL-00012345-2026-10-01",
"occurred_at": "2026-10-01T06:15:00+02:00",
"contact": {
"external_ref": "C-778812",
"first_name": "Thabo",
"wa_phone": "+27821234567",
"consent": {
"status": "opted_in",
"source": "policy_application",
"at": "2025-03-02T10:00:00+02:00"
}
},
"policy": {
"external_ref": "POL-00012345",
"product_type": "funeral",
"premium_amount": 18900,
"debit_day": 1,
"status": "active"
},
"payload": {
"amount": 18900,
"reason": "insufficient_funds"
}
}curl https://www.polisync.co.za/api/v1/events \
-H "Authorization: Bearer psy_…" \
-H "Content-Type: application/json" \
-d @event.json2. CSV upload
No integration yet? Export events from your PAS as a CSV and upload it under Integration in the dashboard. Polisync shows a preview, with every row validated by the same rules as the API, before anything is processed.
Up to 2 MB or 5 000 rows a file. Preview rows are held for 30 minutes and never written to disk.
Scheduled file drop (SFTP)
RoadmapPicking up a nightly file from your SFTP server is planned. Today, files are uploaded through the dashboard.
3. Signed webhooks back
Register an HTTPS endpoint and Polisync posts two kinds of event to it:
action.createdwhen a policyholder taps a button, replies or opts out;delivery.updatedwhen a message is sent, delivered, read, fails or is cancelled.
Each request carries X-Polisync-Signature: t=…,v1=…, an HMAC-SHA256 of the timestamp and the raw body. Reject anything older than five minutes. Use X-Polisync-Delivery to de-duplicate. A non-2xx response is retried five times, over about an hour and a half.
Webhook targets must be public HTTPS addresses. Private and internal ranges are refused, and the address is checked again on every send.
Prefer to poll? GET /api/v1/deliveries?since= returns everything that changed.
{
"id": "6f1c0e4e-…",
"type": "action.created",
"created_at": "2026-10-01T07:42:10+02:00",
"tenant": "example-insurance",
"data": {
"action_id": "9b2d…",
"type": "change_debit_date",
"status": "open",
"contact": {
"external_ref": "C-778812",
"first_name": "Thabo",
"wa_phone": "+27821234567"
},
"policy_ref": "POL-00012345",
"event_id": "…",
"delivery_id": "…",
"message": "Change debit date",
"payload": { "button": "change_debit_date", "text": "Change debit date" }
}
}// X-Polisync-Signature: t=1759297330,v1=5f2b…
[$t, $v1] = parse($header); // split on "," and "="
$expected = hash_hmac('sha256', "$t.$rawBody", $secret);
if (! hash_equals($expected, $v1) || time() - $t > 300) {
abort(401);
}Talk to the people who built it.
Bring your PAS vendor's event list. We will map it to Polisync events with you and set up a sandbox tenant to test against.