Quickstart
Six calls from key to delivered message. The paths and field names never change; the nouns and bodies come from the pack. Switch packs to see the same six calls in another vertical, or compare two side by side.
Every endpoint, in the Political party pack’s words, with a Record an event request for each of its 7 event types. Bodies come from the pack’s schemas, each response is validated against its JSON Schema, and the requests run in order: a create saves its id for the ones after it.
- 01
Get a key
Utilitay AccountOpen your organisation in Utilitay Account, then the Developers tab. Create a Raven key with contacts:write, subjects:write, events:write and messages:read, reaching the instance you will call. The key is shown once; send it as a bearer token on every request.
Headeron every requestAuthorization: Bearer utl_live_k3Jh9QpL2mZx7VbN4cRt8WyS1dFg6HaU0eIo5PqA3zK1a2B3c
utl_live_ prefix · 43-character body · 6-character checksum. Roll or revoke it in the same tab. - 02
Create a subject
subjects:writeThe subject is what events are about. The fixed fields are display_name and external_ref; everything inside attributes comes from the pack schema, and unknown keys are refused.
MembershipsubjectPOST/subjects{ "display_name": "Musu's Membership", "external_ref": "MEM-2026-001", "attributes": { "wing": "Youth", "level": "Executive", "branch": "Example", "standing": "Member", "engagement": "Active" } } - 03
Create a contact
contacts:writeThe contact is the person who receives messages. A name and a phone number in E.164 are enough; WhatsApp or SMS is chosen per message by your rules. Where an instance takes contacts with no phone number, an email address and your own reference stand in its place.
PersoncontactPOST/contacts{ "name": "Hawa Kollie", "phone_e164": "+231881872270", "attributes": { "gender": "Female", "address": "Example" } } - 04
Link them
contacts:writeA link says which contacts hear about a subject. It upserts on the pair. The relationship field is required when the pack offers a choice, and filled in for you when it offers one value.
POST/links{ "contact_id": "0d6c1e7a-3b52-4f8e-a1d9-6c2b7e4f8a31", "subject_id": "8a3f5d21-9c6e-4b17-8e02-1d4a7c9b3f56", "is_primary": true }One relationship value (Self), so relationship is optional here. - 05
Record an event
events:writeThis is the call that makes Raven send. It validates the payload against the type, dedupes on your reference and the type’s dedupe fields, judges it against your rules, and queues the messages. The response tells you what fired.
Dues duedues_duePOST/events{ "type_key": "dues_due", "subject_id": "8a3f5d21-9c6e-4b17-8e02-1d4a7c9b3f56", "event_ref": "dues_due-2026-09-14", "payload": { "due_on": "2026-09-14", "period": "Sep 2026", "paid_at": "2026-09-14", "amount_cents": 50000, "balance_cents": 12000 } }201 · one fire per rule judged · sends: how many messages that made - 06
Read the message
messages:readMessages are read only. Each carries the channel, the body as it went and a delivery status that moves as WhatsApp and SMS report back.
GET/messages?subject_id=8a3f5d21…Returns a bare array. Status: queued → sending → sent → delivered → read.
/events · /messages
subject_id · contact_id · type_key
Every key inside attributes and payload
Whether relationship is required