Compliance
Suppression lists, opt-out, contact hours, disclosures, consent, retention, erasure and the audit log.
Jelliu places calls and sends messages on your behalf, so every outbound contact passes through one compliance gate before it happens. This page describes that gate, the controls you can configure through the API, and the records Jelliu keeps so you can show what happened and why.
Jelliu gives you controls and evidence; it does not decide which laws apply to your business. The defaults below are starting points taken from the platform’s configuration, not legal advice.
How it works
The gate reads your per-country configuration, chosen from the prefix of the contact’s phone number. Numbers with no recognizable prefix (including email-only contacts) use the DEFAULT profile.
Endpoints
Routes marked full are restricted to workspace administrators or the owner, and an API key stands in for them only with the full scope. See API keys.
Opt-out means total silence
When a person asks not to be contacted, Jelliu stops contacting them on that channel’s identifier and sends nothing further, not even a confirmation. What protects the person is the suppression itself, not an acknowledgement.
The opt-out keywords are the same on every text channel: stop, stopall, stop all, cancel, unsubscribe, quit, end, baja, cancelar, darme de baja, dar de baja, no molestar. Keywords count only when they are the whole message, so “quiero cancelar mi cita” is answered normally.
After an opt-out:
- sends to the contact fail (for example
403 CONTACT_OPTED_OUTon WhatsApp,403 EMAIL_ERRORon email, or423from the compliance gate); - inbound messages from the person are not answered automatically;
- campaigns skip the contact.
See WhatsApp and Email for channel details.
Suppression list
The suppression list holds phone numbers your workspace may not contact. It is keyed by number, not by contact, so it survives deleting and re-importing a contact, and the compliance gate checks it before every call and WhatsApp message regardless of any other setting. Jelliu’s own opt-out paths write to it, and you can load lists into it.
Import a registry or your own list
National do-not-call registries such as Colombia’s Registro de Números Excluidos or the US National Do Not Call Registry do not offer a per-number API, so you download the list and import it.
1 to 200,000 numbers. Spaces, dashes, dots and parentheses are removed; what remains must be E.164 (+ and country code). Local-format numbers are rejected, not guessed.
registry:rne_co (Colombia), registry:dnc_us (United States), registry:repep_mx (Mexico), registry:nmp_br (Brazil) or customer_list.
Up to 200 characters, stored on every new row, for example RNE export 2026-09-01.
Imports are idempotent. A number already on the list keeps its original reason and date, so re-importing a monthly export never overwrites the record of when someone first opted out.
JSON request bodies are limited to 1 MB. A registry export with hundreds of thousands of numbers does not fit in one request: split it into chunks, as in the examples.
Check the list and its freshness
stale lists registry sources whose most recent import is older than 31 days, the maximum age the US National Do Not Call rule allows for a scrub. It is a report, not an enforcement: calls are not blocked because a registry is stale. Schedule a monthly re-import to keep it empty.
Lift one suppression
Removing a number is done one number at a time and requires a written justification, which is stored in the audit log. There is no bulk removal.
justification must be 10 to 500 characters. The response is { "data": { "removed": true } }, or false if the number was not on the list.
Lifting a suppression does not change contacts already marked dnc. Update those contacts separately if the person has genuinely consented again.
Compliance configuration
Each workspace can store one configuration per country. Without one, the country defaults apply.
Fields
Country defaults
Every default checks the do-not-call list and has AI disclosure off. GET /api/compliance/defaults/{countryCode} returns the full defaults, including the country’s consent_script and an ai_disclosure_note explaining what is known about AI-disclosure rules there.
Read and change a configuration
GET /api/compliance/configs lists your stored configurations; GET /api/compliance/configs/{countryCode} returns one, or { "data": null } when none is stored.
PUT /api/compliance/configs creates or replaces the configuration for country_code. country_code, require_recording_consent, max_call_attempts_per_day, max_call_attempts_total, allowed_call_hours and data_retention_days are required.
DELETE /api/compliance/configs/{countryCode} removes a stored configuration (204), so the country defaults apply again. It returns 404 if none exists.
data_retention_days on a country configuration also moves your workspace’s retention window: it becomes the shortest value across all your country configurations. Setting 30 days for one country shortens retention for all your data.
Allowed contact hours
Hours are evaluated in the configuration’s timezone, with two exceptions:
- if your workspace has a timezone set (
PATCH /api/settingswith{ "timezone": "America/Bogota" }), that timezone is used instead; - if the resolved timezone is
UTC,America/Bogotais used.
A window whose end is earlier than its start spans midnight. Campaigns can narrow the hours further with their own schedule; see Campaigns.
Attempt limits count calls. A contact who has reached today’s call limit is also blocked on WhatsApp and email until the next day.
Check before you contact
POST /api/compliance/check-call runs the gate without contacting anyone:
phoneNumber must be E.164. contactId is optional; without it the attempt limits and the contact’s own status are not checked. Possible reasons:
Recording notice and AI disclosure
Recording notice. When require_recording_consent is on for the callee’s country (it is on in every default), the notice is added to the opening of the call, using consent_script or a built-in sentence in the call’s language. If the agent’s greeting already announces recording, it is not repeated. If the configuration cannot be read, the notice is included anyway.
AI disclosure. Off by default in every country, because no national rule in the supported countries requires it. It is decided in this order:
- the agent’s own
discloseAisetting, when set (see Agents); require_ai_disclosureon the country configuration, when set;- the country default, which is
false.
When it applies, the call opens with a sentence such as Hola, soy un asistente de inteligencia artificial de Clínica Norte.
Some rules are state-level or foreign and cannot be derived from a phone prefix. For example, California requires AI-voice calls to say so at the start, and the EU AI Act requires telling people they are interacting with an AI. If your agents may reach people covered by such rules, turn disclosure on for those agents.
Consent records
Consent records store evidence that a contact agreed to be contacted or to have data processed.
Grant consent (returns 201 with { "data": { "success": true } }). Granting again for the same type revokes the previous active record and creates a new one:
DELETE /api/compliance/consentwith{ "contactId", "consentType" }revokes it.GET /api/compliance/consent/{contactId}returns the full history, newest first.GET /api/compliance/consent/check/{contactId}/{consentType}returns{ "data": { "hasConsent": true } }when a granted, unexpired record exists.
Workspaces configured to require consent before calling skip, in campaigns, any contact without an active voice_call consent.
Data retention
Retention is one number of days for your workspace, from 1 to 3,650 (365 when never set). A daily sweep at 03:00 UTC applies two windows derived from it:
Read and set retention
contentRetentionDays and tierCapDays are null when the plan has no content limit. Set a new window with PUT /api/compliance/retention and { "data_retention_days": 180 }; the response has the same shape. The value is also copied to every stored country configuration.
POST /api/compliance/retention/enforce runs the sweep for your workspace immediately and returns what it did:
Shortening retention destroys data on the next sweep, and enforcing it destroys data immediately. Neither can be undone.
Right to erasure
POST /api/compliance/erase/{contactId} permanently erases a person’s data from your workspace:
- the contact is anonymized (name and phone replaced with
[ERASED], email, WhatsApp number and metadata removed) and deleted; - the contact’s calls keep their metrics for analytics, but transcripts and summaries are stripped of personal data, and the conversations are deleted at the voice provider;
- the contact’s messages are replaced with
[ERASED]; - reservations and appointments matching the person are anonymized;
- consent records are marked
erased; - personal data in matching audit log entries is redacted, and each redaction is signed so the audit chain still verifies.
Erasure is irreversible and restricted to the workspace owner, or a full key. It is not available through the MCP server. Erasure does not add the person’s number to the suppression list; if they also asked not to be contacted, import the number as customer_list.
Audit log
Jelliu records who changed what in your workspace: creating, changing and deleting agents, campaigns, contacts imports, webhooks, integrations and connected apps, WhatsApp senders and templates, phone numbers, billing changes, identity and SSO settings, exports, compliance configuration, retention, consent, suppressions and erasure. Actions made with an API key are attributed to apikey: followed by the key ID. Sensitive values in changes are redacted before they are stored.
Entries are hash-chained: each row’s hash covers its content and the previous row’s hash, so any alteration is detectable.
The audit log is available to the workspace owner, or a full key, on every plan.
Query entries
GET /api/audit/{id} returns one entry with its full changes.
Verify the chain
GET /api/audit/verify-chain recomputes the hash chain, optionally between from and to (ISO 8601). The response is not wrapped in data:
When valid is false, firstInvalidRow names the first bad row and reason is hash_mismatch, link_broken or unattested_redaction. anchoredMidChain: true is normal with a from date or after old rows were removed: the first row links to one outside the range, so that single link cannot be checked.
Stream it to your SIEM
Subscribe a webhook to audit.log_recorded to receive each entry as it is written, and filter it by action prefix with auditActions. The event is not included in "*": name it explicitly. See Webhooks.
Data processing agreement
Acceptance of the Data Processing Agreement is recorded per workspace, normally when the owner completes onboarding.
POST /api/legal/accept-dpa records acceptance of the current version (optionally { "version": "..." }). DELETE /api/legal/dpa revokes it and requires the header X-Confirm-Revoke: yes. Both are owner-only. For a countersigned DPA, contact support@jelliu.com.
Data residency
GET /api/settings/data-residency returns your workspace’s residency commitment and the processing chain behind it:
Jelliu’s database and compute run in the United States. A us commitment is available; an EU region is not offered because there is no EU deployment. Committing to a region is a one-time decision made by the owner in the dashboard; API keys cannot make it.
Encryption of stored credentials
Credentials you give Jelliu for connected systems, such as accounting, payment and social-media tokens, are encrypted at the application level before they are stored, with AES-256-GCM and a key derived per record. For transport security, tenant isolation and webhook signing, see Security.

