Skip to main content

Country rules enumeration model

The country-rules register answers one question per destination: “what does this country demand of a sender on this channel?” The answer is never a single country record — it is one row per (country code, channel), because per-country rules are per-country per-channel facts. Brazil’s Anatel rules govern SMS; Meta’s WABA policies govern WhatsApp; the two do not overlap, and a single “Brazil rule” would falsely merge them. This page covers the enumeration shape, the per-field enums the row exposes, and how the rows feed the send-time gate in the policy scan pipeline.
The register is a global, ops-curated reference map — you and every other tenant read the same rows. Every decision the rows inform — whether to launch into a market, which sender type to register, what to put in your country’s compliance pack — stays tenant-owned. Orbit curates the reference; you select the posture. This page is not legal advice.

1. The enumeration shape — six channels, one row each

The register is keyed by (country_code, channel) — ISO-3166 alpha-2 plus one of six supported channels. A country that matters to your launches has up to six rows, and each row stands alone: Read the enumeration through GET /api/v1/compliance/country-rules?channel=<channel> — the channel query parameter (default sms) selects the lane, and an optional region parameter narrows the set. The dashboard console at Settings → Compliance → Country rules renders the same list one channel tab at a time, so what you read on screen is the same enumeration your integration reads over the API. Provenance rides each row. Feeds (Telnyx country coverage, the MEF and GSMA sender-ID registries, iconectiv for US 10DLC, Meta’s WABA catalog, the ITU regulator index) stamp last_synced_at; an ops hand-edit stamps last_reviewed_at — and a refresh never overwrites the free-text fields ops curated. sync_source names the last feed that touched the row, so a “Synced from MEF; reviewed by ops two days later” reading is reconstructable row by row.

2. What one row carries — field by field

Each row exposes a fixed field set. Five of them are closed enums — the actionable ranges, not free text — and the rest are curated strings: The closed enums are the part to trust mechanically — plan registration work against registration, plan receipt handling against dlr_support, plan two way flows against two_way. The curated strings demand a read before the first send into the country; they carry the nuance no enum holds.

3. Where the rows meet the send — the gate stays conservative

Two of the enum fields feed the pre-send scan directly. When a send has resolved to a single destination country, the pre-send pipeline passes that country’s row for the send’s channel into the policy scanner, and the scanner’s country gate evaluates the row against the send’s resolved sender type and registration state:
  • The send’s resolved sender type is checked against the row’s sender_types[] — but only when both sides are explicit. An empty sender_types array, or an unresolvable sender type, means the sender-type half of the gate is skipped.
  • A registration: "required" row can flag a send from an unregistered sender; recommended and none never block.
The gate is deliberately conservative, and the conservatism has a shape worth memorizing:
  1. Uncatalogued countries are never blocked. No row for the resolved (country, channel) — or no single destination country resolvable — is a zero-violation pass.
  2. No recorded restriction is not a deny. An empty sender_types skips the sender-type half rather than failing the send.
  3. Only explicit mismatches fire. The gate blocks on a recorded restriction the send actually violates; it never extrapolates one.
The practical reading: the register can tell you a genuine no (sender type not permitted, registration required and absent) only when the row exists and the restriction is recorded. Everywhere else it steps aside and leaves the posture decision where the [tenant-owned compliance stance] puts it — with you.

4. Reading the map for a launch — a worked enumeration

Before a first send into Brazil on SMS and WhatsApp, enumerate both channels explicitly rather than inferring one from the other:
Filter the returned rows to BR; the SMS row gives you the Anatel sender-ID regime (sender_types, registration, sender_rules) and the STOP demand (stop_requirement). Then repeat with channel=whatsapp — the WhatsApp row’s sender_types: ["waba"] and registration posture come from Meta’s catalog, not from anything the SMS row said. The two rows are deliberately separate facts: the SMS plan you derive (sender-ID registration, throughput against default_tps, DLR handling against dlr_support) and the WhatsApp plan (WABA standing, two_way for conversational flows) each stand on their own row, and a single merged “Brazil record” would have hidden exactly the difference the launch needed. Fold what you read into the country pack for the launch — pair it with your quiet-hours window, campaign limits, and evidence binder per the country rules console guide, and re-read the row when a feed refresh stamps a newer last_synced_at.

See also