Your documentation URL. Your project key. Connected.
Once a project and its WhatsApp sender are set up, use one server-side key for verification codes, staff order alerts and balance checks. No internal WhatsApp session IDs or dashboard login are required for your application.
OpenAPI contract · Full text integration guide · Setup & human handoff
Existing keys beginning with mz_test_ can send real messages. The prefix does not select simulation. Keep the key on your server. Do not rotate an existing key or unlink a sender just to integrate.
Check readiness without sending or spending
curl --silent --show-error --max-time 20 'https://mz3b.com/api/v1/account/summary' \
-H "Authorization: Bearer $MZ3B_KEY"Read project.name, ready, reasons, whatsapp.connection and the masked sender. Actual, account-shared balances are in billing.paidCredits and billing.trial; sending costs are in billing.sendCost. Follow links.manage and links.topUp for account management.
This read does not send, reserve credit or restart WhatsApp. Readiness is point-in-time, not a delivery guarantee. If observation fails, show an unknown state, not a cached green badge. Poll every 30 seconds while visible, avoid overlapping requests, invalidate readiness after 45 seconds and respect Retry-After. Limit: 12 reads per account per minute.
Authentication & scopes
Authorization: Bearer YOUR_PROJECT_KEYRequired scopes: verifications:send, verifications:check, verifications:read, messages:send, messages:read, recipients:manage and account:read, according to the endpoint. The summary returns the key’s actual scopes. A scoped key receives 403 INSUFFICIENT_SCOPE outside its permissions. Existing unrestricted keys remain compatible.
/api/v1/verificationsSend a verification code
Use an E.164 recipient, genuine consent and an opaque application login-session identifier. Consent must be recent, within 10 minutes. Persist a new UUID idempotency key and the exact request before sending. Never fabricate consent or use a real phone number as placeholder data.
# Real send. Obtain consent and persist this operation BEFORE calling.
curl --silent --show-error --max-time 20 'https://mz3b.com/api/v1/verifications' \
-H "Authorization: Bearer $MZ3B_KEY" \
-H "Idempotency-Key: $PERSISTED_OPERATION_ID" \
-H "Content-Type: application/json" --data-binary @otp-request.json
# otp-request.json — replace placeholders with authorized values:
{
"to": "USER_APPROVED_E164",
"locale": "en",
"purpose": "authentication",
"consent": {
"granted": true,
"occurredAt": "ACTUAL_CONSENT_ISO_8601_TIME",
"reference": "login:unique-consent-reference"
},
"context": { "userSessionId": "opaque-user-session-id" }
}Older applications retain their previous to and locale contract. New protected applications require consent and context.userSessionId.
Accepted is not delivered
{
"id": "EXAMPLE_ATTEMPT_ID",
"status": "pending",
"channel": "whatsapp",
"expiresIn": 300
}Store the returned attempt ID. A pending response does not prove receipt. Read GET /api/v1/verifications/{id} to inspect the original attempt without a new send.
/api/v1/verifications/:id/checkCheck the code entered by the user
curl --silent --show-error --max-time 20 \
"https://mz3b.com/api/v1/verifications/$ATTEMPT_ID/check" \
-H "Authorization: Bearer $MZ3B_KEY" \
-H "Content-Type: application/json" --data-binary @received-code.json
# Private received-code.json contains {"code":"CODE_ENTERED_BY_USER"}Only status: approved means successful verification. A wrong code may return pending with attempts left, or failed once exhausted. Codes expire after 5 minutes. Protected applications share a total of 5 checks across the same project and recipient over 10 minutes; requesting a new code does not reset that guessing limit.
Send an order alert from the project’s sender
First enroll a genuinely authorized, consenting employee using POST /api/v1/message-recipients, with their phone, role: staff and actual consent evidence. Enrollment does not send or debit. Save their recipient ID for revocation. Maximum: 25 active staff per project; this is not a bulk-marketing endpoint.
curl --silent --show-error --max-time 20 'https://mz3b.com/api/v1/messages' \
-H "Authorization: Bearer $MZ3B_KEY" \
-H "Idempotency-Key: $PERSISTED_ORDER_OPERATION_ID" \
-H "Content-Type: application/json" --data-binary @order-alert.json
# Persist this exact body with the operation:
{
"to": "STAFF_APPROVED_E164",
"text": "Order 42 is ready for review.",
"reference": "order:42:ready:v1",
"url": "https://shop.example.com/orders/42"
}An alert reserves one message from an eligible active trial, then paid credit. The response includes id, status and billing. Read GET /api/v1/messages/{id} later. The same key and body return the original operation without another send or debit. Recipient and event reference also prevent duplication if the idempotency key changes.
Links must be public HTTPS URLs, without credentials, IP literals or local/private hosts. MZ3B does not fetch them or generate previews. Your application must enforce employee login and order permissions, and safely restore the intended order after login. See the complete consent, revoke and alert examples.
accepted is not confirmed receipt. On uncertain, read the original status and retain its reservation. Only definitive non-sending permits a single refund. A missing receipt is not proof of failure.
Explicit simulation: no send, no debit
Use POST /api/v1/verifications/validate or POST /api/v1/messages/validate with the key, operation header and body. They return willSend: false and creditsCharged: 0. This checks inputs, not delivery or live readiness. Message validation does not enroll staff or establish their consent. Normal API rate limits still apply.
A server-only Node.js client
Download the reference client. Importing it sends nothing. It includes bounded timeouts and does not log keys or codes. The full guide shows persistence and error handling.
import { createMZ3B } from './mz3b-node.mjs';
const mz3b = createMZ3B(); // MZ3B_KEY injected by your secret manager
const readiness = await mz3b.summary(); // no send / no debit
// Only after real consent, persist the exact operation in your database.
// const result = await mz3b.sendVerification(persistedOperation);
// const check = await mz3b.checkVerification(result.id, receivedCode);
// const verified = check.status === 'approved';Errors, timeouts and safe retries
Retain the original ID, key, request body and event reference. If the response is lost before you receive an ID, repeat the exact request to retrieve its result. Do not generate a new operation on timeout. After the OTP consent window expires, use the saved attempt ID or request review if uncertainty remains. Provider history loss may leave a result unknown.
400Invalid input, recipient, code or consent401Invalid or revoked key402Insufficient credit403Missing scope, unauthorized recipient or opt-out409Idempotency conflict or operation still in progress410Code expired429Respect Retry-After; never bypass limits202 / 502Inspect status and error: an uncertain send is not a confirmed failure503Service, sender or protected pool is not readyOptional primary and backup numbers
In the dashboard, owners can explicitly enable priority or balanced sender selection and per-number daily caps, shared across that physical number’s projects. Existing projects do not change automatically. The summary adds routing with masked senders, real counts and reset time.
Selection happens only before the first send. An ambiguous attempt is never retried from another number. Protection, suspected bans and uncertain health require review; pools must not bypass WhatsApp limits. Read all pool limits and failure rules.
Security & actual capabilities
- Keep project keys in a server secret manager, never client code, browser storage, chat or logs.
- Never log OTPs. Add application-level user and device limits.
- Ask before sending a real test to a specifically approved recipient.
- Only a signed payment webhook can grant purchased credit. The payment return page is not proof of payment.
Configured WAHA webhooks can reconcile session and message events; customer-facing delivery webhooks and guaranteed delivery receipts are not available. Gemini key storage and isolated provider tests exist; automatic WhatsApp AI replies, email OTP and automatic number-health alerts are not available yet. This is an independent beta service using WAHA, not Meta’s official WhatsApp API.