{"openapi":"3.0.3","info":{"title":"SendTo — Official WhatsApp API","version":"1.0.0","contact":{"name":"Nawa Tech","url":"https://nawa-tech.com"},"x-provider":"SendTo — a Nawa Tech service","description":"Every response carries environment: live | sandbox. Errors: { success: false, error: { code, message, … } }."},"servers":[{"url":"https://whatsapp.nawa-tech.com"}],"components":{"securitySchemes":{"apiKey":{"type":"apiKey","in":"header","name":"x-api-key"}}},"security":[{"apiKey":[]}],"tags":[{"name":"start","description":"Getting started & status"},{"name":"send","description":"Sending"},{"name":"track","description":"Tracking & cancelling"},{"name":"events","description":"Events & replies"},{"name":"recipients","description":"Recipients & opt-outs"},{"name":"webhooks","description":"Webhooks we send you"},{"name":"billing","description":"Balance & invoices"},{"name":"partner","description":"Partner API"}],"paths":{"/api/v1/health":{"get":{"operationId":"health","tags":["start"],"summary":"Health","description":"No key — is the service up.\n\nبلا مفتاح — هل الخدمة تعمل.","security":[],"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"success":true,"service":"sendto-cloudapi-mini","now":"2026-09-26T10:00:00.000Z"}}}}}}},"/api/v1/readiness":{"get":{"operationId":"readiness","tags":["start"],"summary":"Readiness","description":"Whether the invitation template is approved and your numbers are ready.\n\nهل قالبُ الدعوة معتمَدٌ عند Meta وأرقامُكم جاهزة.","responses":{"200":{"description":"OK"}}}},"/api/v1/limits":{"get":{"operationId":"limits","tags":["start"],"summary":"Today's capacity","description":"Your numbers' capacity today and the reset time.\n\nطاقةُ أرقامكم اليوم وموعدُ التصفير.","responses":{"200":{"description":"OK"}}}},"/api/v1/sender/health":{"get":{"operationId":"senderHealth","tags":["start"],"summary":"Sender health","description":"overall, readyForSending, aggregateCapacity, qualityRating and your account caps (fromApplication).\n\noverall · readyForSending · aggregateCapacity · qualityRating · وسقوفُ حسابكم (fromApplication).","responses":{"200":{"description":"OK"}}}},"/api/v1/templates":{"get":{"operationId":"templates","tags":["start"],"summary":"Templates","description":"Your templates: status, category, variable count, header, buttons, usable.\n\nقوالبُكم: الحالة · الفئة · عددُ المتغيّرات · الرأس · الأزرار · usable.","responses":{"200":{"description":"OK"}}}},"/api/v1/messages/text":{"post":{"operationId":"text","tags":["send"],"summary":"Send one message","description":"200 with the result · 202 when scheduled or deferred by a limit (reason sent as message.deferred) · a repeated clientMessageId returns the original result (duplicate: true).\n\n200 بالنتيجة مباشرةً · 202 للمجدولة أو المؤجَّلة بحاجز (والسببُ في message.deferred) · وتكرارُ clientMessageId يعيد النتيجةَ الأصليّة (duplicate: true).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["clientMessageId","to"],"properties":{"clientMessageId":{"type":"string","description":"string — Your message ID (8–128 chars) — used for matching and 48h de-duplication"},"to":{"type":"string","description":"string — Recipient number in international digits (8–20)"},"templateName":{"type":"string","description":"string — Approved template — required outside the 24h conversation window"},"templateLang":{"type":"string","description":"string — Template language (ar, en …)"},"templateParams":{"type":"string","description":"string[] — Template variables in order"},"message":{"type":"string","description":"string — Free text — only inside the conversation window"},"kind":{"type":"string","description":"\"notification\" | \"invitation\" — Default notification · invitations need eventId or expiresAt"},"eventId":{"type":"string","description":"string — A registered event (POST /events)"},"expiresAt":{"type":"string","description":"ISO-8601 — Invitation expiry"},"scheduledFor":{"type":"string","description":"ISO-8601 — Not sent before this time (up to 1 year)"},"deadline":{"type":"string","description":"ISO-8601 — After this it expires unsent and is refunded"},"mediaBase64 / mediaId":{"type":"string","description":"string — jpeg/png image or PDF up to 5MB (or a previous upload ID) with type: image|document"},"sentFrom · fromPolicy":{"type":"string","description":"string · \"prefer\"|\"strict\"|\"auto\" — Preferred sending number and its policy"},"meta":{"type":"string","description":"object ≤ 4KB — Echoed back verbatim in every event"},"buttonUrl":{"type":"string","description":"https URL — Your link for the template button (wallet card, booking page) — we store it, the guest opens our link which redirects to it, and we count opens · only for templates whose button is on our domain"},"buttonPayloads":{"type":"string","description":"string[] (1–10 · كلٌّ 1–128) — Quick-reply button payloads in template order — echoed back verbatim in message.received (raw accounts; default: clientMessageId)"}}},"example":{"clientMessageId":"order-10045","to":"9665XXXXXXXX","templateName":"order_ready","templateLang":"ar","templateParams":["Ahmed","#10045"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"success":true,"environment":"live","data":{"clientMessageId":"order-10045","messageId":"wamid.HBgM…","status":"sent","sentFrom":"9665XXXXXXXX","fallbackUsed":false,"sentAt":"2026-09-26T10:00:01.000Z","duplicate":false}}}}},"400":{"description":"VALIDATION_ERROR — Missing or invalid field — the message names it · INVALID_PHONE — Number is not 8–20 digits · BUTTON_URL_UNSUPPORTED — Template button is not on our domain — buttonUrl not accepted · INVALID_URL — Invalid URL — https and a public host · TEMPLATE_REQUIRED — templateName is required for invitations on your account — no default template · INVALID_BUTTON_PAYLOAD — buttonPayloads: 1–10 strings, each 1–128 chars — never silently truncated"},"402":{"description":"NO_ACTIVE_SUBSCRIPTION — No active subscription · SUBSCRIPTION_EXPIRED — Subscription expired · QUOTA_EXHAUSTED — Unique messages used up · FOLLOWUP_QUOTA_EXHAUSTED — A second message to the same number needs follow-up credit · INVOICE_OVERDUE — An invoice is overdue — sending is paused until paid (includes its number and amount) · USAGE_CREDIT_LIMIT — Pay as you go: balance reached its limit · PARTNER_CAPACITY_EXHAUSTED — Partner capacity used up — request a top-up"},"403":{"description":"RECIPIENT_NOT_ALLOWLISTED — Recipient not on the allowed list"}}}},"/api/v1/messages/otp":{"post":{"operationId":"otp","tags":["send"],"summary":"One-time passcode","description":"Sent with a Meta authentication template (fixed Meta text) and a copy-code button — top priority · not blocked by marketing opt-out · if not sent within its TTL (10 minutes by default) it expires instead of arriving late · the code is erased from our records afterwards.\n\nبقالب «تحقّق» من Meta (نصُّه ثابتٌ منها) وزرِّ نسخ الرمز — أولويّةٌ أولى · لا يحجبه إيقافُ التسويق · وما لم يخرج خلال مهلته (10 دقائق افتراضاً) ينتهي ولا يُرسَل متأخّراً · والرمزُ يُمحى من سجلّاتنا بعد النهاية.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["to","code"],"properties":{"to":{"type":"string","description":"string — International digits"},"code":{"type":"string","description":"string (1–15) — The code — no spaces or links (Meta limit)"},"clientMessageId · ttlSeconds · meta":{"type":"string","description":"string · integer (60–3600) · object — Optional — ttlSeconds defaults to 600"}}},"example":{"to":"9665XXXXXXXX","code":"482913"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"success":true,"environment":"live","data":{"clientMessageId":"otp_…","messageId":"wamid.HBgM…","status":"sent","sentFrom":"9665XXXXXXXX","duplicate":false}}}}},"400":{"description":"INVALID_OTP — Code must be 1–15 characters, no spaces or links · INVALID_PHONE — Number is not 8–20 digits · VALIDATION_ERROR — Missing or invalid field — the message names it"},"403":{"description":"RECIPIENT_NOT_ALLOWLISTED — Recipient not on the allowed list"}}}},"/api/v1/messages/bulk":{"post":{"operationId":"bulk","tags":["send"],"summary":"Bulk batch","description":"Up to 1000 text and 50 media messages · 202 with five lists: accepted, rejected (per-message code), duplicates (48h), held, atRisk — plus a conservative estimatedCompletionAt. Message fields on the envelope return FIELD_NOT_ALLOWED_IN_BULK.\n\nحتى 1000 نصّيّة و50 بوسائط · 202 بخمس قوائم: accepted · rejected (بكود كلّ رسالة) · duplicates (خلال 48 ساعة) · held · atRisk — و estimatedCompletionAt تقديرٌ محافظ. حقولُ الرسالة على الغلاف تُردّ FIELD_NOT_ALLOWED_IN_BULK.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["messages"],"properties":{"messages":{"type":"string","description":"Message[] — Each item has the fields of \"Send one message\""},"batchName":{"type":"string","description":"string — Batch name"},"eventId":{"type":"string","description":"string — Batch event"},"fromPolicy":{"type":"string","description":"string — Policy for the whole batch"}}},"example":{"batchName":"reminders-sep","messages":[{"clientMessageId":"rem-0001","to":"9665XXXXXXXX","templateName":"order_ready","templateLang":"ar","templateParams":["Sara","#1"]}]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"success":true,"environment":"live","data":{"batchId":"b1f…","batchName":"reminders-sep","eventId":null,"total":1,"accepted":[{"clientMessageId":"rem-0001"}],"held":[],"atRisk":[],"rejected":[],"duplicates":[],"estimatedCompletionAt":"2026-09-26T10:05:00.000Z"}}}}},"400":{"description":"VALIDATION_ERROR — Missing or invalid field — the message names it · BATCH_TOO_LARGE — Batch above the limit · FIELD_NOT_ALLOWED_IN_BULK — Message field on the batch envelope · INVALID_FROM_POLICY — fromPolicy: prefer, strict, auto"}}}},"/api/v1/messages/rich":{"post":{"operationId":"rich","tags":["send"],"summary":"Rich message","description":"Header image + text + up to 3 reply buttons (or one URL button). Inside the conversation window, or with an approved template outside it. Call buttons and copyCode are not available (400 unsupported).\n\nصورةُ رأسٍ + نصّ + حتى 3 أزرار ردّ (أو زرُّ رابطٍ واحد). داخل نافذة المحادثة، أو بقالبٍ معتمَد خارجها. زرُّ الاتّصال وcopyCode غيرُ متاحَين في الرسميّ (400 unsupported).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["clientMessageId","to","message","buttons"],"properties":{"clientMessageId":{"type":"string","description":"string — Your message ID (8–128 chars) — used for matching and 48h de-duplication"},"to":{"type":"string","description":"string — Recipient number in international digits (8–20)"},"message":{"type":"string","description":"string ≤ 1024 — Body text"},"buttons":{"type":"string","description":"{id,text}[] | [{text,url}] — Button text ≤ 20 chars"},"headerImage":{"type":"string","description":"base64 | data: — Header image"},"title · footer":{"type":"string","description":"string ≤ 60 — Title and footer"}}},"example":{"clientMessageId":"card-2201","to":"9665XXXXXXXX","message":"تذكرتك جاهزة","buttons":[{"id":"confirm","text":"تأكيد"},{"id":"decline","text":"اعتذار"}]}}}},"responses":{"200":{"description":"OK"},"400":{"description":"VALIDATION_ERROR — Missing or invalid field — the message names it · SERVICE_WINDOW_CLOSED — Free text outside the conversation window — use a template"},"402":{"description":"NO_ACTIVE_SUBSCRIPTION — No active subscription · SUBSCRIPTION_EXPIRED — Subscription expired · QUOTA_EXHAUSTED — Unique messages used up · FOLLOWUP_QUOTA_EXHAUSTED — A second message to the same number needs follow-up credit · INVOICE_OVERDUE — An invoice is overdue — sending is paused until paid (includes its number and amount) · USAGE_CREDIT_LIMIT — Pay as you go: balance reached its limit · PARTNER_CAPACITY_EXHAUSTED — Partner capacity used up — request a top-up"}}}},"/api/v1/messages/upload":{"post":{"operationId":"upload","tags":["send"],"summary":"Upload media","description":"Upload once and reuse the mediaId across many messages (content is de-duplicated).\n\nارفع مرّةً واستعمل mediaId في رسائلَ كثيرة (بلا تكرارٍ للمحتوى).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["mediaBase64"],"properties":{"mediaBase64":{"type":"string","description":"string — jpeg/png/pdf up to 5MB"},"mimetype · filename":{"type":"string","description":"string — Optional"}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"success":true,"environment":"live","data":{"mediaId":"ast_…","mimetype":"image/png","bytes":48211}}}}},"400":{"description":"MEDIA_TOO_LARGE — Media above 5MB · INVALID_MEDIA — Not jpeg/png/pdf"}}}},"/api/v1/messages/{cid}":{"get":{"operationId":"message","tags":["track"],"summary":"Message status","description":"timeline, invitation, attempts, error, meta — by your clientMessageId.\n\ntimeline · invitation · attempts · error · meta — بمعرّفكم clientMessageId.","parameters":[{"name":"cid","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"},"404":{"description":"MESSAGE_NOT_FOUND — No message with this ID"}}}},"/api/v1/links":{"post":{"operationId":"linkCreate","tags":["track"],"summary":"Create a redirect link","description":"A link on our domain that redirects to yours and counts opens — https and a public host.\n\nرابطٌ عندنا يحوّل إلى رابطكم ويعدّ فتحاته — https ومضيفٌ عامّ.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url"],"properties":{"url":{"type":"string","description":"https URL — Destination"},"eventId · clientMessageId · expiresAt":{"type":"string","description":"string · ISO-8601 — Optional"}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"success":true,"environment":"live","data":{"code":"Ab3kP9xQr2","shortUrl":"https://whatsapp.nawa-tech.com/l/Ab3kP9xQr2","url":"https://…","clicks":0,"active":true}}}}},"400":{"description":"INVALID_URL — Invalid URL — https and a public host · VALIDATION_ERROR — Missing or invalid field — the message names it"}}}},"/api/v1/links/{code}":{"get":{"operationId":"linkGet","tags":["track"],"summary":"Link opens","description":"clicks, firstClickAt, lastClickAt, active.\n\nclicks · firstClickAt · lastClickAt · active.","parameters":[{"name":"code","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"},"404":{"description":"LINK_NOT_FOUND — No link with this code"}}}},"/api/v1/batches/{batchId}/cancel":{"post":{"operationId":"cancelBatch","tags":["track"],"summary":"Cancel a batch","description":"Cancels whatever has not been sent (refunded) — messages mid-send are counted in inFlight.\n\nيُلغى ما لم يخرج (ويعود لرصيدكم) — وما كان في الإرسال لحظتَها يُعَدّ في inFlight.","parameters":[{"name":"batchId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}}}},"/api/v1/events":{"post":{"operationId":"events","tags":["events"],"summary":"Register an event","description":"Invitation deadlines, reply texts and the callback for its events. 201 new · 200 duplicate · 409 EVENT_LOCKED when changing the date after an invitation went out (unless confirmLockedChange: true).\n\nمواعيدُ الدعوات ونصوصُ الردّ ووجهةُ أحداثها. 201 جديدة · 200 تكرار · 409 EVENT_LOCKED لتغيير الموعد بعد خروج دعوة (إلّا confirmLockedChange: true).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["eventId","eventDate","callbackUrl","texts.invalidReply · texts.nudge · texts.expired"],"properties":{"eventId":{"type":"string","description":"string (4–64) — Your event ID"},"eventDate":{"type":"string","description":"ISO-8601 — Event date"},"callbackUrl":{"type":"string","description":"https URL — Webhook URL for this event"},"texts.invalidReply · texts.nudge · texts.expired":{"type":"string","description":"string — Texts sent to guests"},"defaults":{"type":"string","description":"object — Invitation deadlines and undeliveredTimeoutMinutes"},"defaults.sentFrom":{"type":"string","description":"string — Default sending number for the event (one of yours) — each new guest starts on it, guests already messaged keep their number · null ⇒ automatic · editable after lock"}}}}}},"responses":{"200":{"description":"OK"},"400":{"description":"VALIDATION_ERROR — Missing or invalid field — the message names it · INVALID_SENDER — Not one of your numbers"},"409":{"description":"EVENT_LOCKED — Date change after invitations went out — confirm with confirmLockedChange"}}}},"/api/v1/events/{eventId}":{"get":{"operationId":"event","tags":["events"],"summary":"Get an event","description":"Its settings + sentInvitations.\n\nإعداداتُها + sentInvitations.","parameters":[{"name":"eventId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"},"404":{"description":"EVENT_NOT_FOUND — No event with this ID"}}}},"/api/v1/numbers/check":{"post":{"operationId":"numbersCheck","tags":["recipients"],"summary":"Check numbers","description":"Up to 500 — true if we delivered to it before · null otherwise (Meta offers no pre-check — never false).\n\nحتى 500 — true لمن سُلِّمت إليه رسالةٌ منّا · null لغيره (Meta لا تتيح الفحصَ المسبق — لا false).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["numbers"],"properties":{"numbers":{"type":"string","description":"string[] — Numbers"}}}}}},"responses":{"200":{"description":"OK"},"400":{"description":"BATCH_TOO_LARGE — Batch above the limit"}}}},"/api/v1/optouts/check":{"post":{"operationId":"optoutsCheck","tags":["recipients"],"summary":"Check opt-outs","description":"Who asked to stop your messages.\n\nمن طلب إيقافَ رسائلكم.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["numbers"],"properties":{"numbers":{"type":"string","description":"string[] — Numbers"}}}}}},"responses":{"200":{"description":"OK"}}}},"/api/v1/optouts/{phoneNumber}":{"delete":{"operationId":"optoutDelete","tags":["recipients"],"summary":"Remove an opt-out","description":"Only at the number owner's request.\n\nبطلب صاحب الرقم وحدَه.","parameters":[{"name":"phoneNumber","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}}}},"/api/v1/webhooks/failed":{"get":{"operationId":"failedHooks","tags":["webhooks"],"summary":"Failed webhooks","description":"Events whose six attempts were exhausted — with their payload.\n\nما استُنفدت محاولاتُه الستّ — بحمولته.","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"string"}},{"name":"since","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}}}},"/api/v1/webhooks/failed/{webhookId}/retry":{"post":{"operationId":"retryHook","tags":["webhooks"],"summary":"Retry a webhook","description":"Re-queued with six new attempts and the same ID.\n\nيعود للتسليم بستّ محاولاتٍ جديدة وبالمعرّف نفسِه.","parameters":[{"name":"webhookId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"}}}},"/api/v1/media/inbound/{wamid}":{"get":{"operationId":"inboundMedia","tags":["webhooks"],"summary":"Guest media file","description":"Delivered ready-made as mediaUrl in guest.* events — signed, so it works without a key (even in <img>) · valid 7 days from receipt (Meta retention) then 410 · we do not store the file: download it within that window.\n\nالرابطُ يصلكم جاهزاً في mediaUrl بأحداث guest.* — موقَّعٌ فيعمل بلا مفتاح (ولو في <img>) · صالحٌ 7 أيّامٍ من الاستلام (صلاحيةُ Meta) ثمّ 410 · ولا نخزّن الملفّ: نزّلوه خلالها.","security":[],"parameters":[{"name":"wamid","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"},"403":{"description":"INVALID_SIGNATURE — Invalid link signature"},"404":{"description":"MEDIA_NOT_FOUND — No media for this message"},"410":{"description":"MEDIA_EXPIRED — Media expired at Meta (7 days)"},"502":{"description":"META_ERROR — Download from Meta failed — its text is in the message"}}}},"/api/v1/feedback/send":{"post":{"operationId":"feedbackSend","tags":["send"],"summary":"Send a feedback form","description":"A feedback form you built in the portal, approved by us and published at Meta — the guest gets a button that opens it inside WhatsApp. **Once per number** (ALREADY_SENT) · **only the first answer counts** and reaches you as feedback.submitted with the question texts · sandbox accounts send a ready form in simulation.\n\nنموذجُ تقييمٍ أنشأتموه في البوّابة ووافقنا عليه ونُشر عند Meta — يصل الضيفَ بزرٍّ يفتحه داخل واتساب. **مرّةً واحدةً لكلّ رقمٍ** (ALREADY_SENT) · **والردُّ الأوّلُ وحدَه يُحسب** ويصلكم feedback.submitted بنصوص الأسئلة · والتجريبيُّ يُرسل النموذجَ الجاهزَ محاكاةً.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["formId","to"],"properties":{"formId":{"type":"string","description":"string — Form id (any version — the latest published one is sent)"},"to":{"type":"string","description":"string — Guest number in international digits"},"clientMessageId":{"type":"string","description":"string — Your id — repeating it returns the original result (duplicate:true)"}}},"example":{"formId":"6f1c…","to":"9665XXXXXXXX","clientMessageId":"visit-10045-rating"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"success":true,"environment":"live","data":{"sendId":"a3b…","jobId":"9e2…","formId":"6f1c…","version":1,"to":"9665XXXXXXXX","status":"queued","duplicate":false}}}}},"400":{"description":"VALIDATION_ERROR — Missing or invalid field — the message names it"},"403":{"description":"RECIPIENT_NOT_ALLOWLISTED — Recipient not on the allowed list"},"404":{"description":"FORM_NOT_FOUND — No feedback form with this id in your account"},"409":{"description":"FORM_NOT_SENDABLE — The form is not published yet (sandbox: not approved yet) · ALREADY_SENT — This form was already sent to this number — rated once (previousSendAt) · DUPLICATE_CLIENT_MESSAGE_ID — clientMessageId already used for another message in this account"}}}},"/api/v1/balance":{"get":{"operationId":"balance","tags":["billing"],"summary":"Balance","description":"Package, billing type (PACKAGE, MONTHLY, USAGE), quota (unique/follow-up), pay-as-you-go money, suspended and open invoices — or partner capacity.\n\nالباقة · النوع (PACKAGE · MONTHLY · USAGE) · الحصّة (فريدة/تابعة) · مالُ «حسب الاستخدام» · suspended والفواتيرُ المفتوحة — وللشريك سعتُه.","responses":{"200":{"description":"OK","content":{"application/json":{"example":{"success":true,"environment":"live","data":{"billingMode":"ENFORCED","suspended":false,"invoices":[],"subscription":{"package":"الأعمال","billingType":"PACKAGE","state":"ACTIVE","unique":{"quota":5000,"used":1200,"remaining":3800},"followup":{"quota":1000,"used":40,"remaining":960}}}}}}}}}},"/api/v1/invoices":{"get":{"operationId":"invoices","tags":["billing"],"summary":"Invoices","description":"Each invoice is valid until dueAt; after that sending pauses (INVOICE_OVERDUE) and resumes by itself once paid in the portal.\n\nكلُّ فاتورةٍ صالحةٌ حتى dueAt؛ بعده يتوقّف الإرسال (INVOICE_OVERDUE) ويعود وحدَه بالسداد من البوّابة.","parameters":[{"name":"status","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"example":{"success":true,"environment":"live","data":[{"number":"INV-2026-000001","kind":"USAGE","status":"OPEN","subtotal":"500.00","vatPct":0,"vatAmount":"0.00","total":"500.00","currency":"SAR","issuedAt":"2026-09-26T10:00:00.000Z","dueAt":"2026-09-27T10:00:00.000Z"}]}}}}}}},"/api/v1/invoices/{number}":{"get":{"operationId":"invoice","tags":["billing"],"summary":"Get an invoice","description":"Its lines, amounts and status.\n\nبنودُها ومبالغُها وحالُها.","parameters":[{"name":"number","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK"},"404":{"description":"NOT_FOUND — Not found"}}}},"/api/v1/partner/account":{"get":{"operationId":"pAccount","tags":["partner"],"summary":"Partner account","description":"Capacity (total, used, remaining), balance owed, credit limit, available credit, maximum top-up now, expiry, pending transfers.\n\nالسعة (الكلّيّة · المستعمَلة · المتبقّية) · المديونيّة · السقف · المتاح · أقصى زيادةٍ الآن · الانتهاء · التحويلاتُ المنتظرة.","responses":{"200":{"description":"OK","content":{"application/json":{"example":{"success":true,"environment":"live","data":{"currency":"SAR","ratePerMessage":"0.3700","capacity":{"total":20000,"used":5120,"remaining":14880,"pct":26},"debt":"4000.00","creditLimit":"10000.00","creditAvailable":"6000.00","maxTopupMessages":16216,"expiresAt":null,"expired":false,"pendingTransfers":[]}}}}},"403":{"description":"NOT_A_PARTNER — Partner accounts only"}}}},"/api/v1/partner/topups":{"post":{"operationId":"pTopup","tags":["partner"],"summary":"Top up capacity","description":"Activated instantly while the balance after it stays within the credit limit — otherwise 402 CREDIT_LIMIT_EXCEEDED with the numbers and the maximum you can request now.\n\nتُفعَّل فوراً ما دامت المديونيّةُ بعدها ≤ السقف — وإلّا 402 CREDIT_LIMIT_EXCEEDED بالأرقام وبأقصى ما يُطلب الآن.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["messages"],"properties":{"messages":{"type":"string","description":"integer — Number of messages"}}},"example":{"messages":20000}}}},"responses":{"200":{"description":"OK"},"400":{"description":"INVALID_QUANTITY — Messages from 1 to 10,000,000"},"402":{"description":"CREDIT_LIMIT_EXCEEDED — Top-up exceeds the credit limit · PARTNER_CAPACITY_EXPIRED — Partner agreement ended"},"403":{"description":"NOT_A_PARTNER — Partner accounts only"}}}},"/api/v1/partner/transfers":{"post":{"operationId":"pTransfer","tags":["partner"],"summary":"Report a transfer","description":"Does not change your balance until we confirm receipt — the amount actually received is applied.\n\nلا يمسّ المديونيّة حتى نؤكّد الاستلام — ويُخصم المبلغُ المستلَم فعلاً.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["amount","reference"],"properties":{"amount":{"type":"string","description":"number — Amount"},"reference":{"type":"string","description":"string — Transfer reference — unique"},"transferDate · note":{"type":"string","description":"date · string — Optional"}}},"example":{"amount":5000,"reference":"TRX-88213","transferDate":"2026-09-26"}}}},"responses":{"200":{"description":"OK"},"400":{"description":"REFERENCE_REQUIRED — Transfer reference required · INVALID_AMOUNT — Invalid amount"},"403":{"description":"NOT_A_PARTNER — Partner accounts only"},"409":{"description":"DUPLICATE_REFERENCE — Reference already reported"}}}},"/api/v1/partner/ledger":{"get":{"operationId":"pLedger","tags":["partner"],"summary":"Ledger","description":"Every entry with the balance after it: TOPUP, TRANSFER_REPORTED, TRANSFER_CONFIRMED, TRANSFER_REJECTED, ADJUSTMENT.\n\nكلُّ حركةٍ بمديونيّتها بعدها: TOPUP · TRANSFER_REPORTED · TRANSFER_CONFIRMED · TRANSFER_REJECTED · ADJUSTMENT.","responses":{"200":{"description":"OK"}}}}}}