Skip to main content

API recipes: contact lifecycle

The contact-lifecycle cookbook. The canonical pages — Opt-outs API, Segments API, Frequency Caps API, Analytics API — document each endpoint; these recipes chain them into runnable loops: read, write, decide. All four loops stay on tenant-owned controls — opt-outs, segments, and caps are your rules, set and enforced by your key. Base URL is https://api.orbit.devotel.io/api/v1 throughout; authenticate with X-API-Key and a test key (dv_test_sk_…) for the sandbox.

1. Opt-outs: bulk suppress → export → re-opt-in one channel

A suppression file arrives from support and you need the round trip: opt the contacts out in one batch, export the suppression list as compliance evidence, then restore the one contact who re-consents by phone or email. contact identifies the address-book row by phone (E.164) or email — pick the id off GET /contacts first if your file lists raw addresses. Bulk opt-out — up to 500 rows per call, per-row outcomes isolated:
cURL
207 Multi-Status — the full-envelope batch result. succeeded / skipped / failed count independently, so one unknown phone number never rolls the rest back:
207
A failed fifth row would show ok: false, status: "failed", and an actionable error string — clean the named rows and resend only them; skipped rows (already opted out) never double-stamp the consent trail. Export the suppression list — compliance evidence, up to 10 000 contacts:
cURL
200
Omit channel to export every opt-out across channels. Past 10 000, page GET /contacts/optouts on its cursor instead — same rows, no cap. Re-opt-in the one contact who re-consents — delete the channel’s opt-out:
cURL
200 returns the updated contact row. The contact is eligible again on that channel subject to consent records — the delete clears the suppression, and any affirmative-consent requirement (a WhatsApp 24-hour window, RCS marketing consent) still has to be satisfied by your own capture. The round trip closes: every enlisted row, plus one restored. Labelled error envelope — a channel outside the canonical nine (case-insensitive, whitespace-trimmed) fails the whole request before any row is processed:
422
Nothing was written on a validation failure, so retrying the corrected body is always safe; an already-opted-out single-row write resolves as 200 with already_opted_out: true, not an error. Canonical page: Opt-outs API. Envelope reference: API error handling by example.

2. Segments: autopilot → evaluate → materialize-lookalike → members

The AI-assisted audience loop: describe the audience, persist the suggestion as a real segment, force a re-evaluation when data moves, then grow it — preview a lookalike expansion and materialize it as a static segment you can page members out of. Autopilot from a description — suggest rules + live preview in one round-trip, nothing persisted:
cURL
200
Persist the winner with POST /contacts/segments (the main cookbook’s task 15); membership materializes on create. Labelled error envelope — the model emits a rule the segmentation engine cannot validate, and the error names the field:
422
Retry with a tighter description; a 503 means no AI provider is configured on the deployment — fall back to the manual builder. Evaluate on demand — after data (not rules) changed, rebuild membership now instead of waiting for the periodic refresh:
cURL
200 returns the evaluation result — the matched count, duration, and the membership diff — in the standard envelope. Materialize a lookalike audience — promote a preview into a new static segment (no rules, auto_refresh off — the k-NN snapshot is a point-in-time cohort the refresh scheduler never wipes):
cURL
201 returns materialized: true plus the new segment — its id is a first-class segment you activate like any other. A preview with no candidates returns 200 with materialized: false and a reason, persisting nothing. Read members — page the materialized snapshot with the same cursor contract as any list:
cURL
Pass meta.pagination.cursor while has_more is true. The members list reads the last-materialized snapshot — call evaluate (above) first if you need the very latest audience. Canonical pages: Segments API (AI surfaces), Contacts API (saved-segment CRUD + membership).

3. Frequency caps: read the rules behind a send decision

Caps are the tenant’s rolling-window limits on how often one contact can be messaged. Two reads serve the “what applies to this contact?” question: the per-contact rules read, then the live per-recipient usage you branch a send decision on. Rule writes ride PATCH /frequency-caps/:id; reads ride GET /contacts/:id/frequency-caps (rules) and GET /contacts/:id/cap-status (live usage) — the decision loop below uses the rule IDs from the first read to target back-off per category. Read the rules that apply to the contact — flat plus grouped by channel, globals separated:
cURL
200
Read live usage for the “can I send?” decision — snapshot across every channel, with the consent state folded in:
cURL
Per cap that applies, the response carries max_count / current_count / sends_remaining and (once slots are exhausted) the next_slot_at ISO timestamp, plus the channel’s consent state — one read answers both “how many left” and “may we send at all”. Update a rule — widen the marketing SMS cap; rule writes are owner/admin/developer with frequency-caps:write:
cURL
Decide in your send gate:
Node.js — branch your composer on the snapshot
Labelled error envelope — a send that would exceed the cap:
429
Defer that recipient and continue the batch (the main cookbook’s error table has the retriable-vs-terminal split). Caps are exclusion enforcement, not a block on your whole send plan — suppression lists and opt-outs gate in parallel (Opt-outs round trip above). Canonical pages: Frequency Caps API, Contacts API.

4. Usage: fetch a day of metering for guardrails

The metering guardrail read: the home-overview usage chart’s same daily timeline, pulled for a one-day window so your own guardrails (budget dashboards, spend alerts, anomaly heuristics) sit on the numbers the platform charts. Read-only; an explicit start_date / end_date pair takes precedence over the days trailing window.
cURL
200
Feed totals into a budget check before you queue a batch, or row-by-row into a daily anomaly heuristic. For spend-shaped guardrails instead of volume, the billing surface’s spend series and burn rate (the main cookbook’s task 22) price the same window. Labelled error envelope — an explicit pair with a missing end:
400
A lone start_date (or end_date) is a 400 — the pair refines together, and the details issues list names the fix instead of silently falling back to the trailing days window. Canonical page: Analytics API → per-day usage timeline. Finer-grained metering (rate a batch, credit notes): Usage metering.

See also