The application field
Pass your external_id as application:
- in the JSON body of
POST /v1/verify/start,POST /v1/verify/check,POST /v1/verify/{id}/resend,POST /v1/verify/{id}/cancelandPOST /v1/whatsapp/messages; - as a query parameter,
?application=store_88, onGET /v1/verify/{id},GET /v1/whatsapp/messages/{id}andGET /v1/whatsapp/templates, since a GET has no body.
service is accepted identically everywhere. A key can only check, read, resend or cancel a verification started under the same application; another application’s id answers 404 not_found.
Idempotency-Key is scoped to your key and the application together, so the same key under two applications is two sends.
What blocks a send
Checked in this order, before the endpoint’s own logic. The first three are the partner-key branch of the shared ladder on the Errors page.
Everything after that is the ordinary ladder: request shape, channel and destination, the per-destination hourly cap, the guard, your balance (
402 insufficient_credits is your balance, not the merchant’s), then the send itself.
Reading what was sent
Two logs list what your key sent across every application, newest first:GET /v1/partner/verifications for codes and GET /v1/partner/messages for WhatsApp messages. Each row is the public shape of GET /v1/verify/{id} or GET /v1/whatsapp/messages/{id} plus application_id and your external_id; a verification also carries the key mode it was sent under (partner for your key), the lang and the price_halalas it was charged at.
- Filters. Both take
application(yourexternal_id; one that is not yours answers404 not_found),reference(exact),fromandto(ISO 8601, oncreated_at). Verifications addstatus(pending,verified,failed,expired,canceled, read by the clock likeGET /v1/verify/{id}) andmode; messages addstatus(accepted,sent,delivered,read,failed) andtemplate. A value outside the vocabulary answers400 invalid_requestwith the field named. - Paging. Keyset, no totals:
limitis 1 to 100 (50 when omitted),next_cursorpoints after the last row you received whether or not more rows exist now, andhas_moresays whether another page is waiting. Pass it back ascursor; an empty page answersnull. Rows created in the same second are ordered by id, so a cursor never skips or repeats one. - Archived applications keep their rows, with their
external_id.
GET /v1/partner/usage reports sends, verified codes and spend per application over a window.
Rate limit
The per-key limit of 120 requests per minute counts every call your key makes, sends and partner-API calls together. Spread bursts across a minute, or ask support for a higher limit.The sandbox key
A partner sandbox key (tk_partner_test_..., created in the partner console under Integration) is the same account and the same endpoints with the sandbox limits, so the code you write against it is the code you ship:
- One destination. Sends reach only the phone the partner account’s owner verified at signup (the Integration page shows it). Anything else answers
422 sandbox_unverified_destination. - A visible stamp. Every message carries the
[TEST]/[تجريبي]stamp, and is charged like a live send. - Daily caps instead of the lifetime one: 20 sends per application and 200 per account a day (Riyadh time), answering
429 sandbox_quota_exceeded. - Applications in review can send, so a merchant can be tested before it is approved; rejected, suspended, disabled and archived ones cannot (
403 account_not_active). - SMS only. The WhatsApp endpoints answer
403 live_key_required. - Reads work, writes do not. Every partner management write (provisioning, updates, suspend, archive, owners, the webhook, retries) answers
403 live_key_requiredwith the sandbox key, so a key that leaks into a test runner can change nothing. Provision test merchants with the live key, then send with the sandbox key.
mode: "partner_test", and GET /v1/partner/profile answers key.mode accordingly. One sandbox key is active at a time; rotating it revokes the old one at once.
WhatsApp with a partner key
The WhatsApp endpoints takeapplication too: GET /v1/whatsapp/templates with ?application=, POST /v1/whatsapp/messages with it in the body, GET /v1/whatsapp/messages/{id} with ?application=. Review gates codes only: the WhatsApp endpoints answer for an application in review as for an active one, provided a number is connected.
Who connects the number depends on who runs the application:
- Your own application (no owner): you connect it from the partner console, on the application’s WhatsApp tab, through Meta’s Embedded Signup under your own Meta business. Review does not hold WhatsApp back; only a rejected or suspended application cannot connect.
- A handed-over application: the owner connects their own WhatsApp Business number from their dashboard. You keep sending through it with
application, and you manage its templates and the agents on its team from the partner console. See Application owners.
message.* events reach your webhook once you subscribe to them, and the messages log lists every message; GET /v1/whatsapp/messages/{id} reads one back. The number, the templates and the team are managed over the API too: see WhatsApp for your applications. Read a message’s status back with GET /v1/whatsapp/messages/{id}.