Messaging

Contacts & opt-out

A minimal consent ledger — one row per phone, per environment. Not a CRM. Opt-out blocks sends to that number until an explicit override is recorded.

Opt in / opt out

POST/v1/contacts/opt-in
POST/v1/contacts/opt-out
curl https://api.tyxter.com/v1/contacts/opt-out \
  -H "authorization: Bearer $TYXTER_API_KEY" \
  -H "content-type: application/json" \
  -d '{
    "phone": "+5511999999999",
    "source": "api",
    "reason": "Requested via Intercom ticket #4821"
  }'

Both endpoints are idempotent. Re-applying the same state no-ops and does not emit a second webhook.

Auto opt-out on inbound

The inbound worker scans the text body of every incoming message against the Brazilian opt-out keyword list (case-insensitive, trimmed):

parar, sair, cancelar, stop

A match transitions the contact to opted_out with source inbound_keyword and emits contact.opted_out. Every subsequent outbound send to that phone is blocked with 400 contact_opted_out until you explicitly opt them back in.

Honor opt-out requests promptly — both LGPD and Meta's Business Platform policies require it. The consent service is the single place where outbound sends are gated, so once you've opted a contact out, every code path is covered.

LGPD: data export and erasure

POST/v1/contacts/{'{id}'}/export

Returns the contact row plus a summary of every Message associated with the contact's phone (last 500, ordered descending). The response includes a generated_at timestamp — record it in your audit trail.

DELETE/v1/contacts/{'{id}'}

Tombstones the phone number to +erased-<suffix>, clears metadata and opt-in source, and redacts the payload and metadata of every associated Message. Returns the count of messages redacted and emits contact.erased.

Erasure is a soft scrub, not a hard delete. Contact ids are referenced by audit logs, outbox events, and usage records — rewriting those would rewrite immutable history. The PII is gone; the integrity chain stays intact.