Messaging

Phone numbers

Provision a Brazilian WhatsApp-ready number through Salvy, or connect one you already own. One row per number, one provider forever.

Two sources

SourcePathDDD required
salvyTyxter provisions the number via SalvyYes
byonYou bring your own Meta-connected numberNo

Source also determines control and exit behavior. See WABA and phone-number ownership before choosing a long-lived sender: BYON stays under your control, while a Salvy number is a Tyxter-managed rental with no published port-out guarantee.

List available regions

GET/v1/phone-numbers/available-regions

Returns DDDs Salvy currently has stock for, with the monthly BRL rental fee for each.

Provision (Salvy)

POST/v1/phone-numbers/provision
curl https://api.tyxter.com/v1/phone-numbers/provision \
  -H "authorization: Bearer $TYXTER_API_KEY" \
  -H "content-type: application/json" \
  -d '{ "ddd": "11", "display_name": "ACME Suporte" }'

Returns 202. The actual Salvy call runs on the provider.phone worker pool. Watch the status transitions:

requested  →  provisioning  →  provisioned  →  verifying  →  active

Salvy forwards the SMS verification code via webhook and we stash it on the row. Fetch with GET /v1/phone-numbers/:id to read verification_code and verification_code_received_at.

Register a Salvy number with Meta

Carrier activation and Meta registration are separate. A production Salvy number can be active while meta_phone_number_id is null; it is still pending Meta registration. Continue in the dashboard's WhatsApp connection flow.

  1. In Meta, choose SMS, not a voice call, for the verification method.
  2. The verification SMS reaches the Tyxter-provisioned number through Salvy. After Salvy receives it, the code appears in the Tyxter dashboard.
  3. Copy the code from the dashboard and enter it in Meta's registration wizard.

This is the Meta registration step for a Salvy number; POST /v1/phone-numbers/connect is for a number you already registered with Meta.

Connect (BYON)

POST/v1/phone-numbers/connect
curl https://api.tyxter.com/v1/phone-numbers/connect \
  -H "authorization: Bearer $TYXTER_API_KEY" \
  -H "content-type: application/json" \
  -d '{
    "phone": "+5511999999999",
    "meta_phone_number_id": "109...",
    "display_name": "ACME Vendas"
  }'

BYON numbers skip Salvy and jump straight to verifying. You complete WhatsApp verification on your side; Tyxter transitions the row to active when that finishes.

Meta display names

Every phone-number response, including list, retrieve, and mutation responses, includesverified_name, pending_name_review, and name_review.display_nameremains the customer-entered label for the row; it is not Meta's verified display name.

In production, verified_nameis populated only by Tyxter's scheduled Meta health sweep and is null before a completed sweep observes it. Sandbox returns a deterministic simulated verified name from its health snapshot. Reading either response never makes a provider call.

pending_name_review is the current pending Graph health observation, not a completed decision. It is either null or { requested_name: string | null; status: string | null; observed_at: ISO timestamp }. Its raw status is open Meta vocabulary, and observed_at is the same successful-sweep freshness fact as meta_health_synced_at. A successful complete production health sweep is authoritative and writes or clears the block. A completed callback can clear it immediately only when its requested name matches and its effective time is not older than observed_at; all other callbacks wait for the next sweep. A failed or partial refresh leaves its prior values and freshness untouched. Sandbox deterministically returns pending_name_review: null without calling Meta, and that null means no pending Graph observation — not that a review was approved.

name_review is null until Tyxter has durably received a completed callback decision. Once present it contains requested_name, decision, reason, and reviewed_at; requested_name and reason may be null. Treat decision as an open Meta string rather than a fixed enum. It is separate from both pending_name_review and the name_status in the existing health webhook snapshot.

A name-review update appears in the dashboard and can create an email notification when your notification preferences allow it. It does not add a signed customer webhook; API clients should read the phone resource for the current result.

Release a number

POST/v1/phone-numbers/{id}/release

Returns 202. Salvy numbers move to release_requested while the worker calls Salvy; the row reaches released when Salvy confirms by webhook. BYON numbers move directly to released.

Billing

The first time a number reaches active we emit usage_event.created for the phone.number.month meter. Recurring monthly billing for long-lived numbers is a scheduled follow-up — see the roadmap.

Webhooks

The pause/resume pair fires on the transition, not per message, so a ten-thousand-message broadcast produces exactly one paused. The resume is OBSERVED rather than scheduled: Tyxter reports it when a send to a new recipient next succeeds, so a paused number that stops sending stays paused and emits nothing. Never wait for a resume before sending again — sending is what produces it. Read the remaining allowance itself from remaining_messaging_allowance_estimate on the number. A linked Meta phone repeats its shared portfolio estimate there. An unlinked Meta phone uses the conservative per-phone fallback; sandbox follows its deterministic simulated phone tier. Read the per-message reason from status_reason on held messages.

The three health events each carry the full health snapshot — quality_rating, meta_quality_rating, messaging_tier, messaging_limit_tier, name_status, and meta_health_synced_at— plus the previous value of whichever dimension moved, so you never need a follow-up retrieve. They fire only on a real change: the first time a number’s health is read is not a change, and re-reading the same value emits nothing. Production only — a sandbox number never reaches Meta and takes a constant simulated health snapshot instead.

What to do when Tyxter paces a number

This is not a total-message limit. Meta limits the unique WhatsApp recipients a business can deliver messages to outsidean open customer-service window during a moving 24-hour period. Here, “new recipient” means a recipient who does not already hold a slot in that rolling period — not a phone number you have never contacted before.

When the allowance is full, Tyxter holds only sends that need another slot. Messages to a recipient with an active slot, and messages inside that recipient's customer-service window, keep flowing. Slots expire one by one, so there is no daily reset time; this is why a busy sender can alternate between paused and resumed as individual slots become available. The dashboard can show an earliest time that a slot may free up, but it is an estimate, not a promise that sending resumes at that time.

The effective pacing cap is Tyxter's safety threshold for the active allowance; it is not interchangeable with, and can deliberately be below, the raw Meta tier. For example, an effective cap of 237 against a Meta allowance of 250 means Tyxter is keeping a safety slice; it does not mean 237 messages were sent. Keep the known Meta health tier separate and do not infer one from the effective cap.

For linked Meta senders, the allowance belongs to the Business Portfolio and is shared by all of its business phone numbers. Moving traffic to another phone in the same portfolio does not create more capacity; another sender helps only when it is backed by a different allowance authority.

Watch the paused state on the dashboard phone roster and detail view, the paced label in message list and detail views, and the notification bell. A held message has status_reason: "messaging_limit_pacing"; see the message status_reason reference. It stays queued, retries automatically, and must not be resent or re-created — doing so can duplicate a customer message.

Subscribe webhook receivers to phone_number.messaging_limit_paused and phone_number.messaging_limit_resumed to observe the live transition. Existing endpoints keep their saved subscriptions, so endpoints created before these events may need both names added to subscribed_events. New endpoints created in the dashboard suggest the full event catalog, including both pacing events, by default.

For remediation, review the number's quality, verification, and current messaging-limit information in WhatsApp Manager and Meta's guidance. Follow Meta's Messaging Limits guidance and Account Quality; do not rely on a published promotion threshold because Meta can change its current criteria.

There is intentionally no transparent failover between Salvy and another provider. A number is always served by the provider that provisioned it. Fallback means releasing the row and provisioning a new one.