{"openapi":"3.1.0","info":{"title":"MZ3B Verify API","version":"1.3.0","description":"One project key: WhatsApp OTP, consenting staff transactional messages and actual balance/status. No session identifiers needed. Optional dashboard-configured sender pools use priority or balanced routing before the first send only; original operations retain their sender. Accepted is not delivered; only approved verifies an OTP. Legacy unrestricted keys retain existing OTP and gain documented capabilities; explicitly restricted keys require the listed scopes. Server-side use only. See /docs, /integration.md and /for-agents."},"servers":[{"url":"https://mz3b.com"}],"security":[{"bearerAuth":[]}],"paths":{"/api/v1/account/summary":{"get":{"operationId":"getAccountSummary","summary":"Read project, live sender readiness and actual account-shared credits. No send, no debit.","x-required-scopes":["account:read"],"description":"12 reads/minute per account. Does not start/relink sessions. Authentication/rate-limit metadata may update; no billing mutation. ready is a point-in-time observation, not a guarantee. Billing scope is account_shared, not a separate project wallet.","responses":{"200":{"description":"Masked sender, live state, trial expiration and actual paid credits","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountSummary"}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"INVALID_API_KEY","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"INSUFFICIENT_SCOPE or recipient consent/opt-out policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"PROJECT_NOT_FOUND","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited or SENDER_DAILY_LIMIT_REACHED (read routing.resetsAt); do not bypass limits","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"schema":{"type":"integer"}}}},"503":{"description":"Service/configuration unavailable; SENDER_POOL_PROTECTED, SENDER_POOL_REVIEW_REQUIRED, SENDER_POOL_STATUS_UNAVAILABLE, SENDER_POOL_LOCAL_STATE_PENDING or SENDER_POOL_NO_CONNECTED_NUMBER. Never route around protection or uncertainty.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/balance":{"get":{"operationId":"getLegacyBalance","summary":"Existing paid-balance endpoint; preserved","x-required-scopes":["account:read"],"responses":{"200":{"description":"Legacy response retained","content":{"application/json":{"schema":{"type":"object","properties":{"balance":{"type":"integer"},"unit":{"const":"verification_credit"},"unlimited":{"type":"boolean"},"entitlement":{"type":["string","null"]}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"INVALID_API_KEY","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"INSUFFICIENT_SCOPE or recipient consent/opt-out policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited or SENDER_DAILY_LIMIT_REACHED (read routing.resetsAt); do not bypass limits","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"schema":{"type":"integer"}}}},"503":{"description":"Service/configuration unavailable; SENDER_POOL_PROTECTED, SENDER_POOL_REVIEW_REQUIRED, SENDER_POOL_STATUS_UNAVAILABLE, SENDER_POOL_LOCAL_STATE_PENDING or SENDER_POOL_NO_CONNECTED_NUMBER. Never route around protection or uncertainty.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/message-recipients":{"get":{"operationId":"listStaffRecipients","x-required-scopes":["recipients:manage"],"summary":"List this project’s staff roster; masked numbers only","responses":{"200":{"description":"Up to 100 retained records, at most 25 active","content":{"application/json":{"schema":{"type":"object","properties":{"recipients":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"phone":{"type":"string"},"status":{"enum":["active","revoked"]},"consentAt":{"type":"integer","description":"Unix milliseconds"}}}},"limit":{"const":25}}}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"INVALID_API_KEY","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"INSUFFICIENT_SCOPE or recipient consent/opt-out policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited or SENDER_DAILY_LIMIT_REACHED (read routing.resetsAt); do not bypass limits","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"schema":{"type":"integer"}}}},"503":{"description":"Service/configuration unavailable; SENDER_POOL_PROTECTED, SENDER_POOL_REVIEW_REQUIRED, SENDER_POOL_STATUS_UNAVAILABLE, SENDER_POOL_LOCAL_STATE_PENDING or SENDER_POOL_NO_CONNECTED_NUMBER. Never route around protection or uncertainty.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"authorizeStaffRecipient","x-required-scopes":["recipients:manage"],"summary":"Record actual staff authorization and fresh consent; sends nothing and charges nothing","description":"Caller must have authority over the staff roster and retain evidence of real opt-in. Declaration is not independent proof. At most 25 active/project, 100 total/project and 10 enrollments/reactivations per account/day. Same active number replays without sending. Consent time within 10 minutes. Do not manufacture consent.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StaffConsent"}}}},"responses":{"200":{"description":"Already authorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StaffResult"}}}},"201":{"description":"Staff recipient enrolled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StaffResult"}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"INVALID_API_KEY","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"INSUFFICIENT_SCOPE or recipient consent/opt-out policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"RECIPIENT_LIMIT_REACHED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited or SENDER_DAILY_LIMIT_REACHED (read routing.resetsAt); do not bypass limits","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"schema":{"type":"integer"}}}},"503":{"description":"Service/configuration unavailable; SENDER_POOL_PROTECTED, SENDER_POOL_REVIEW_REQUIRED, SENDER_POOL_STATUS_UNAVAILABLE, SENDER_POOL_LOCAL_STATE_PENDING or SENDER_POOL_NO_CONNECTED_NUMBER. Never route around protection or uncertainty.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/message-recipients/{id}":{"delete":{"operationId":"revokeStaffRecipient","x-required-scopes":["recipients:manage"],"summary":"Revoke future alerts; cannot cancel already reserved/in-flight messages","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Recipient revoked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StaffResult"}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"INVALID_API_KEY","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"INSUFFICIENT_SCOPE or recipient consent/opt-out policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"RECIPIENT_NOT_FOUND in this project","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited or SENDER_DAILY_LIMIT_REACHED (read routing.resetsAt); do not bypass limits","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"schema":{"type":"integer"}}}},"503":{"description":"Service/configuration unavailable; SENDER_POOL_PROTECTED, SENDER_POOL_REVIEW_REQUIRED, SENDER_POOL_STATUS_UNAVAILABLE, SENDER_POOL_LOCAL_STATE_PENDING or SENDER_POOL_NO_CONNECTED_NUMBER. Never route around protection or uncertainty.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/messages":{"post":{"operationId":"sendTransactionalMessage","x-required-scopes":["messages:send"],"summary":"Send one real staff transaction alert from the project’s OTP sender","description":"Same project key and sender selection policy as OTP; no session ids. Optional pools may choose different senders for distinct events; each operation pins its original sender. Must enroll consenting staff first. Reserve one trial/paid message credit atomically. Same exact key/body replays without send/debit; changing key alone for the same recipient/reference cannot duplicate the event. Different body conflicts. Text is not stored in MZ3B; retain original body/key securely on your server. No automatic resend, even after gateway history expires. accepted does NOT mean delivered. HTTPS URL is not fetched; previews disabled. 20/minute per physical sender shared with OTP; account minute policy shared with OTP; 10/minute and 200/day per recipient globally; 1000/day per account.","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9._:-]{16,128}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TransactionalRequest"}}}},"responses":{"200":{"description":"Existing accepted operation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Message"}}}},"201":{"description":"Accepted by provider, not confirmed delivered","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Message"}}}},"202":{"description":"Reserved or uncertain; keep id/key/body and read status. Do not send a new operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Message"}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"INVALID_API_KEY","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"INSUFFICIENT_CREDITS","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"INSUFFICIENT_SCOPE or recipient consent/opt-out policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"IDEMPOTENCY_KEY_REUSED, ACCOUNT_STATE_CHANGED or TRANSACTIONAL_WHATSAPP_REQUIRED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited or SENDER_DAILY_LIMIT_REACHED (read routing.resetsAt); do not bypass limits","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"schema":{"type":"integer"}}}},"502":{"description":"Confirmed not sent; reserved credit refunded exactly once. Same operation replays failure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Message"}}}},"503":{"description":"Service/configuration unavailable; SENDER_POOL_PROTECTED, SENDER_POOL_REVIEW_REQUIRED, SENDER_POOL_STATUS_UNAVAILABLE, SENDER_POOL_LOCAL_STATE_PENDING or SENDER_POOL_NO_CONNECTED_NUMBER. Never route around protection or uncertainty.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/messages/{id}":{"get":{"operationId":"getTransactionalMessage","x-required-scopes":["messages:read"],"summary":"Read or reconcile original message without sending again","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"description":"Only this project. If still reserved/uncertain after 15 seconds, reads the durable gateway outcome, at most 6 reconciliations/minute/message. Confirmed not-sent refunds once; uncertainty retains reservation. Missing history alone is not proof of non-delivery. Acceptance is not a delivery receipt.","responses":{"200":{"description":"Current message result","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Message"}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"INVALID_API_KEY","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"INSUFFICIENT_SCOPE or recipient consent/opt-out policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"MESSAGE_NOT_FOUND","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited or SENDER_DAILY_LIMIT_REACHED (read routing.resetsAt); do not bypass limits","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"schema":{"type":"integer"}}}},"503":{"description":"Service/configuration unavailable; SENDER_POOL_PROTECTED, SENDER_POOL_REVIEW_REQUIRED, SENDER_POOL_STATUS_UNAVAILABLE, SENDER_POOL_LOCAL_STATE_PENDING or SENDER_POOL_NO_CONNECTED_NUMBER. Never route around protection or uncertainty.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/messages/validate":{"post":{"operationId":"validateTransactionalMessage","x-required-scopes":["messages:send"],"summary":"Syntax-only simulation: no send, credit charge or reservation","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9._:-]{16,128}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TransactionalRequest"}}}},"description":"Does not check consent roster, credits or readiness. Never interprets the key prefix as sandbox. Ordinary API request rate limits apply.","responses":{"200":{"description":"Valid syntax only","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationResult"}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"INVALID_API_KEY","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"INSUFFICIENT_SCOPE or recipient consent/opt-out policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited or SENDER_DAILY_LIMIT_REACHED (read routing.resetsAt); do not bypass limits","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"schema":{"type":"integer"}}}},"503":{"description":"Service/configuration unavailable; SENDER_POOL_PROTECTED, SENDER_POOL_REVIEW_REQUIRED, SENDER_POOL_STATUS_UNAVAILABLE, SENDER_POOL_LOCAL_STATE_PENDING or SENDER_POOL_NO_CONNECTED_NUMBER. Never route around protection or uncertainty.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/verifications/validate":{"post":{"operationId":"validateVerification","x-required-scopes":["verifications:send"],"summary":"OTP syntax/consent validation only; no code generation, send or debit","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9._:-]{16,128}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendRequest"}}}},"description":"Does not check provider readiness or reserve credits. Use account summary separately. Ordinary API request rate limits apply.","responses":{"200":{"description":"Valid input only","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationResult"}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"INVALID_API_KEY","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"INSUFFICIENT_SCOPE or recipient consent/opt-out policy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited or SENDER_DAILY_LIMIT_REACHED (read routing.resetsAt); do not bypass limits","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"headers":{"Retry-After":{"schema":{"type":"integer"}}}},"503":{"description":"Service/configuration unavailable; SENDER_POOL_PROTECTED, SENDER_POOL_REVIEW_REQUIRED, SENDER_POOL_STATUS_UNAVAILABLE, SENDER_POOL_LOCAL_STATE_PENDING or SENDER_POOL_NO_CONNECTED_NUMBER. Never route around protection or uncertainty.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/verifications":{"post":{"operationId":"sendVerification","summary":"Send one real verification code","x-required-scopes":["verifications:send"],"description":"Reserves one active trial message or paid credit. New-operation consent must be fresh (10 minutes). Retry with the SAME Idempotency-Key and exact body. Existing operations retain original consent time and sender snapshot, including after freshness elapses; replay never sends or reserves again. Provider-error responses include the original attempt id. On uncertainty, do not automatically send a new request. Pending is not proof of delivery.","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9._:-]{16,128}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendRequest"}}}},"responses":{"200":{"description":"Existing attempt replay; no new send","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendResult"}}}},"201":{"description":"Provider accepted; delivery unconfirmed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendResult"}}}},"400":{"description":"Invalid body, phone, code, consent/context or idempotency key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Invalid or revoked project key"},"402":{"description":"INSUFFICIENT_CREDITS"},"403":{"description":"Missing scope, consent, opt-out or provisioning policy rejected"},"409":{"description":"IDEMPOTENCY_KEY_REUSED, REQUEST_IN_PROGRESS or ACCOUNT_STATE_CHANGED. Inspect before retrying."},"429":{"description":"Rate limited or check budget exhausted. Never bypass protection.","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds when supplied"}}},"502":{"description":"DELIVERY_STATE_UNAVAILABLE (uncertain) or DELIVERY_PROVIDER_ERROR (rejected). Keep original key/body and inspect returned id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Live sending, signing or WhatsApp session not ready"}}}},"/api/v1/verifications/{id}":{"get":{"operationId":"getVerification","summary":"Read attempt without sending","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"x-required-scopes":["verifications:read"],"responses":{"200":{"description":"Attempt and available delivery evidence; null delivery is unknown","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Verification"}}}},"400":{"description":"Invalid body, phone, code, consent/context or idempotency key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Invalid or revoked project key"},"403":{"description":"INSUFFICIENT_SCOPE or recipient consent/opt-out policy"},"404":{"description":"Attempt not found in this account and project"},"429":{"description":"Rate limited or check budget exhausted. Never bypass protection.","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds when supplied"}}}}}},"/api/v1/verifications/{id}/check":{"post":{"operationId":"checkVerification","summary":"Check the code supplied by its recipient","x-required-scopes":["verifications:check"],"description":"Never guess. WhatsApp codes are six digits, valid five minutes. Five checks per project/recipient in ten minutes; changing userSessionId does not reset the budget.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["code"],"properties":{"code":{"type":"string","pattern":"^[0-9]{4,8}$","description":"Actual six-digit WhatsApp code; legacy providers may accept 4–8 digits"}}}}}},"responses":{"200":{"description":"Only approved is success. pending/failed is NOT successful verification.","content":{"application/json":{"schema":{"type":"object","required":["id","status"],"properties":{"id":{"type":"string","format":"uuid"},"status":{"enum":["approved","pending","failed"]},"remainingAttempts":{"type":"integer","minimum":0,"maximum":5}}}}}},"400":{"description":"Invalid body, phone, code, consent/context or idempotency key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Invalid or revoked project key"},"403":{"description":"INSUFFICIENT_SCOPE or recipient consent/opt-out policy"},"404":{"description":"Attempt not found in this project"},"409":{"description":"VERIFICATION_NOT_PENDING"},"410":{"description":"OTP_EXPIRED"},"429":{"description":"Rate limited or check budget exhausted. Never bypass protection.","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds when supplied"}}},"502":{"description":"VERIFICATION_PROVIDER_ERROR"}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"Project secret shown once. Existing mz_test_ keys are LIVE-capable; prefix does not mean sandbox. Store only on the server."}},"schemas":{"SenderPool":{"type":"object","description":"Optional, explicitly enabled in the dashboard. Never send internal session identifiers from an external application. A read does not reserve a sender for the next request.","properties":{"mode":{"enum":["priority","balanced"]},"ready":{"type":"boolean"},"reasons":{"type":"array","items":{"type":"string"}},"dayTimezone":{"const":"UTC"},"resetsAt":{"type":"string","format":"date-time"},"failoverPolicy":{"type":"string"},"senders":{"type":"array","maxItems":8,"items":{"type":"object","properties":{"name":{"type":"string"},"senderMasked":{"type":["string","null"]},"connection":{"enum":["connected","disconnected","unknown"]},"observedAt":{"type":["string","null"],"format":"date-time"},"priority":{"type":"integer","minimum":0},"dailyLimit":{"type":"integer","minimum":1,"maximum":10000},"usedToday":{"type":"integer","minimum":0,"description":"Reservations across all projects and both OTP/messages for this physical sender today UTC. Uncertain and sent OTPs with failed checks count. Only confirmed not-sent releases quota."},"available":{"type":"boolean"},"reason":{"type":["string","null"]}}}}}},"ValidationResult":{"type":"object","required":["valid","willSend","creditsCharged"],"properties":{"valid":{"const":true},"mode":{"const":"validation"},"willSend":{"const":false},"creditsCharged":{"const":0}}},"TransactionalRequest":{"type":"object","required":["to","text","reference","url"],"properties":{"to":{"type":"string","pattern":"^\\+[1-9][0-9]{7,14}$","description":"One consenting, enrolled staff recipient only"},"text":{"type":"string","minLength":1,"maxLength":1500,"description":"Plain transactional text without embedded URLs; place the link in url"},"reference":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._:-]{0,99}$","description":"Stable unique event, e.g. order:42:ready:v1. No personal data or tokens. Use a new reference for a genuinely new status event."},"url":{"type":"string","format":"uri","maxLength":1000,"description":"Public HTTPS host, no IP/localhost/credentials/custom port. Details must require staff login/authorization in your application. Never include API keys or long-lived tokens."}},"example":{"to":"+15555550100","text":"Order 42 is ready","reference":"order:42:ready:v1","url":"https://shop.example.com/orders/42"}},"StaffConsent":{"type":"object","required":["to","role","consent"],"properties":{"to":{"type":"string","pattern":"^\\+[1-9][0-9]{7,14}$"},"role":{"const":"staff"},"consent":{"type":"object","required":["granted","occurredAt","reference"],"properties":{"granted":{"const":true},"occurredAt":{"type":"string","format":"date-time"},"reference":{"type":"string","pattern":"^[A-Za-z0-9][A-Za-z0-9._:-]{0,99}$"}}}}},"StaffResult":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"status":{"enum":["active","revoked"]},"replayed":{"type":"boolean"}}},"Message":{"type":"object","required":["id","status","delivery","billing"],"properties":{"id":{"type":"string","format":"uuid"},"status":{"enum":["reserved","accepted","uncertain","failed"]},"to":{"type":"string","description":"Masked recipient"},"reference":{"type":"string"},"replayed":{"type":"boolean"},"delivery":{"type":"object","properties":{"status":{"enum":["accepted","not_sent","unknown"]},"confirmed":{"const":false}}},"billing":{"type":"object","properties":{"unit":{"const":"message_credit"},"source":{"enum":["trial_credit","paid_credit","owner_unlimited"]},"cost":{"type":"integer","minimum":0,"maximum":1},"state":{"enum":["reserved","charged","refunded"]}}},"error":{"type":["string","null"]},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"links":{"type":"object","properties":{"status":{"type":"string","format":"uri"}}}}},"AccountSummary":{"type":"object","required":["project","ready","reasons","whatsapp","billing","scopes","links"],"properties":{"routing":{"$ref":"#/components/schemas/SenderPool"},"project":{"type":"object","properties":{"name":{"type":"string"}}},"ready":{"type":"boolean"},"reasons":{"type":"array","items":{"type":"string"}},"whatsapp":{"type":"object","properties":{"connection":{"enum":["connected","disconnected","unknown"]},"senderMasked":{"type":["string","null"]},"observedAt":{"type":["string","null"],"format":"date-time"}}},"billing":{"type":"object","properties":{"scope":{"const":"account_shared"},"unit":{"const":"message_credit"},"paidCredits":{"type":"integer"},"unlimited":{"type":"boolean"},"trial":{"type":"object","properties":{"remaining":{"type":"integer","minimum":0},"startedAt":{"type":["string","null"],"format":"date-time"},"expiresAt":{"type":["string","null"],"format":"date-time"},"eligibleForSender":{"type":"boolean"}}},"sendCost":{"type":"object","properties":{"verification":{"type":"integer","minimum":0,"maximum":1},"transactionalMessage":{"type":"integer","minimum":0,"maximum":1}}},"moneyPerCredit":{"type":"null","description":"No fabricated dollar price; purchase bundles vary"},"chargingPolicy":{"type":"string"}}},"scopes":{"type":"array","items":{"type":"string"}},"links":{"type":"object","properties":{"manage":{"type":"string","format":"uri"},"topUp":{"type":"string","format":"uri"},"docs":{"type":"string","format":"uri"}}},"features":{"type":"object","properties":{"otp":{"type":"boolean"},"transactionalMessages":{"type":"boolean"},"staffConsentRequired":{"const":true},"deliveryReceiptsGuaranteed":{"const":false}}}}},"SendRequest":{"type":"object","required":["to","purpose","consent","context"],"properties":{"to":{"type":"string","pattern":"^\\+[1-9][0-9]{7,14}$","description":"User-approved recipient, E.164. Never infer a phone."},"locale":{"type":"string","enum":["ar","en","fr","tr","es","de"],"default":"ar"},"purpose":{"const":"authentication"},"consent":{"type":"object","required":["granted","occurredAt","reference"],"properties":{"granted":{"const":true},"occurredAt":{"type":"string","format":"date-time","description":"Actual consent time within 10 minutes; preserve on retry"},"reference":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,128}$"}}},"context":{"type":"object","required":["userSessionId"],"properties":{"userSessionId":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,128}$","description":"Opaque end-user session reference, not a raw login credential"},"deviceId":{"type":"string","pattern":"^[A-Za-z0-9._:-]{8,128}$"}}}}},"SendResult":{"type":"object","required":["id","status"],"properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","description":"pending is not delivered"},"channel":{"const":"whatsapp"},"expiresIn":{"type":"integer"},"replayed":{"type":"boolean"}}},"Verification":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"to":{"type":"string","description":"Masked phone"},"status":{"type":"string"},"delivery":{"type":["object","null"],"properties":{"status":{"type":"string"},"ack":{"type":["integer","null"]},"updatedAt":{"type":["string","null"]},"deliveredAt":{"type":["string","null"]},"readAt":{"type":["string","null"]}}},"expiresAt":{"type":"string","format":"date-time"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"Error":{"type":"object","properties":{"error":{"type":"string"},"id":{"type":"string"},"hint":{"type":"string"}}}}}}