Consent Ledger
Record opt-ins, opt-outs, and check the current consent status of a phone number.
Consent Ledger
The consent ledger is an append-only record of opt-in and opt-out events keyed by phone_number and consent_type. The Messaging API checks the latest event for a recipient before sending (when consent gating is on).
Record opt-in
POST /v1/compliance/consentRequest body
| Field | Type | Required | Description |
|---|---|---|---|
phone_number | string | yes | Recipient phone number, E.164 format. |
consent_type | string | yes | Channel/category: sms, whatsapp, voice, marketing, transactional, etc. Free-form. |
source | string | no | Where the consent came from (web-form, import-2026-01, api-confirmed, etc.). Stored verbatim for audit. |
Example
curl -X POST https://api.tryvox.io/v1/compliance/consent \
-u $TRYVOX_AUTH_ID:$TRYVOX_AUTH_TOKEN \
-H "Content-Type: application/json" \
-d '{
"phone_number": "+919876543210",
"consent_type": "sms",
"source": "signup-form-v2"
}'Response
201 Created:
{
"phone_number": "+919876543210",
"consent_type": "sms",
"status": "opted_in",
"consented_at": "2026-04-09T10:30:00Z"
}Record opt-out
DELETE /v1/compliance/consentDELETE with a body — the request shape mirrors opt-in. The ledger appends an opted_out event; the prior opt-in stays in history.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
phone_number | string | yes | Recipient phone number, E.164. |
consent_type | string | yes | Same consent_type you used for the opt-in. |
source | string | no | Source of the opt-out (stop-keyword, support-ticket-1842, etc.). |
Example
curl -X DELETE https://api.tryvox.io/v1/compliance/consent \
-u $TRYVOX_AUTH_ID:$TRYVOX_AUTH_TOKEN \
-H "Content-Type: application/json" \
-d '{
"phone_number": "+919876543210",
"consent_type": "sms",
"source": "stop-keyword"
}'Response
200 OK:
{
"phone_number": "+919876543210",
"consent_type": "sms",
"status": "opted_out",
"consented_at": "2026-04-09T10:30:00Z",
"revoked_at": "2026-04-09T11:14:32Z"
}Check
Query the current effective consent for a single phone number / type pair.
GET /v1/compliance/consent?phone_number={e164}&consent_type={type}Example
curl -u $TRYVOX_AUTH_ID:$TRYVOX_AUTH_TOKEN \
"https://api.tryvox.io/v1/compliance/consent?phone_number=%2B919876543210&consent_type=sms"(URL-encode + as %2B in query parameters.)
Response
200 OK if a record exists:
{
"phone_number": "+919876543210",
"consent_type": "sms",
"status": "opted_in",
"consented_at": "2026-04-09T10:30:00Z"
}404 NOT_FOUND if no opt-in or opt-out has ever been recorded for this phone_number + consent_type pair. Treat "no record" as "no consent" — the Messaging API does.
Bulk check
Resolve consent for many numbers in a single call.
GET /v1/compliance/consent/bulk?phone_numbers={csv}&consent_type={type}Query parameters
| Parameter | Description |
|---|---|
phone_numbers | Comma-separated E.164 list. URL-encoded. Up to 200 numbers per call. |
consent_type | Single consent_type for the whole batch. |
Example
NUMBERS=$(printf '%%2B919876543210,%%2B919876543211,%%2B919876543212')
curl -u $TRYVOX_AUTH_ID:$TRYVOX_AUTH_TOKEN \
"https://api.tryvox.io/v1/compliance/consent/bulk?phone_numbers=$NUMBERS&consent_type=sms"Response
200 OK:
{
"data": [
{"phone_number": "+919876543210", "status": "opted_in", "consented_at": "2026-04-09T10:30:00Z"},
{"phone_number": "+919876543211", "status": "opted_out", "revoked_at": "2026-04-09T11:00:00Z"},
{"phone_number": "+919876543212", "status": "no_record"}
]
}Numbers with no ledger record are returned with status: "no_record" — they are not omitted. Use this to drive batch sends without a per-number lookup loop.
Errors
| Status | Code | Reason |
|---|---|---|
| 400 | VALIDATION_FAILED | Missing phone_number or consent_type; phone not E.164 |
| 401 | INVALID_CREDENTIALS | Auth ID / Auth Token bad |
| 404 | NOT_FOUND | (single Check only) no record for this pair |
| 413 | BATCH_TOO_LARGE | Bulk check requested more than 200 numbers |
Operational notes
- The ledger is global per tenant. It is not partitioned by API key Auth ID, so rotating keys does not orphan consent records.
- Records are durable. There is no record-level delete; opt-outs append, they don't erase. Compliance audits expect this trail.
- Consent gating is per-channel. Whether a particular send is blocked on missing consent depends on the channel's settings — manage the gate in the dashboard, not via this API.