Auth
Every endpoint accepts the brand API key two ways:
- In the URL:
/api/public/contacts/<API_KEY> - Bearer header:
Authorization: Bearer <API_KEY>
Find the key under Brand settings → API. CORS is open — these endpoints can be called from a browser.
Contacts
Upsert
POST /api/public/contacts/<API_KEY>
{
"email": "[email protected]",
"first_name": "Alice",
"tags": ["signup", "free-trial"]
}Idempotent by email. Tags are additive — existing tags survive.
Update
PUT /api/public/contacts/<API_KEY>/update
{
"email": "[email protected]",
"status": "unsubscribed"
}Delete
DELETE /api/public/contacts/<API_KEY>/[email protected]Hard delete. Cascades to events, send history, list memberships.
Move between tags/lists
POST /api/public/contacts/<API_KEY>/move
{
"email": "[email protected]",
"add_to": ["paid"],
"remove_from": ["free-trial"]
}Transactional
POST /api/public/transactional/<API_KEY>/send
{
"template_id": "tpl_welcome",
"to": "[email protected]",
"variables": { "name": "Alice" }
}See Transactional API for full details.
Sequences
POST /api/public/sequences/<API_KEY>/enroll
{
"sequence_id": "seq_xxx",
"email": "[email protected]"
}Manually enroll a contact. Returns 200 if enrolled, 409 if already in the sequence.
Forms
POST /api/public/forms/<slug>/submit
{
"email": "[email protected]",
"fields": { "company": "Acme", "size": "10-50" }
}No API key required — forms are public by design.
Rate limits
Per-brand: 100 req/min on writes, 600 req/min on reads. Trips return
429 Too Many Requests with Retry-After.