{"info":{"version":"1.0.0","description":"Enterprise SMS & Messaging Platform REST API","title":"Murasil SMS Platform API","contact":{"name":"Murasil Support","email":"api@murasil.io"}},"components":{"schemas":{"Error":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}}},"BatchLookupRequest":{"type":"object","required":["msisdns"],"properties":{"msisdns":{"description":"Destination MSISDNs: a list, or a comma/space/semicolon-separated string (max 10000).","oneOf":[{"type":"array","items":{"type":"string"},"example":["+971501234567","+966512345678"]},{"type":"string","example":"+971501234567,+966512345678"}]}}},"BulkMessageResponse":{"type":"object","description":"Returned when `to` resolves to more than one recipient.","properties":{"messages":{"type":"array","items":{"type":"object","properties":{"to":{"type":"string"},"message_id":{"type":"string"},"segments":{"type":"integer"}}}},"status":{"type":"string","example":"submitted"},"accepted":{"type":"integer","example":2}}},"DlrWebhook":{"type":"object","description":"DLR callback POSTed to your `callback_url`. The raw body is signed with HMAC-SHA256; verify the `X-Signature: sha256=<hex>` header against your shared secret.","properties":{"status":{"type":"string","enum":["delivered","undeliverable","expired","deleted","rejected","accepted","enroute","unknown"]},"timestamp":{"type":"string","format":"date-time"},"reference":{"type":"string","description":"Echo of the `reference` you supplied on send"},"ts":{"type":"integer","description":"Unix epoch seconds"},"error_code":{"type":"string"},"message_id":{"type":"string"}}},"LoginRequest":{"type":"object","required":["tenant_id","email","password"],"properties":{"otp":{"type":"string","description":"TOTP or recovery code, required once 2FA is on"},"password":{"type":"string","format":"password"},"email":{"type":"string","format":"email","example":"admin@acme.com"},"tenant_id":{"type":"string","example":"acme"}}},"LoginResponse":{"type":"object","properties":{"user":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string"},"role":{"type":"string"},"tenant_id":{"type":"string"}}},"token_type":{"type":"string","example":"Bearer"},"access_token":{"type":"string"},"expires_in":{"type":"integer","example":28800},"refresh_token":{"type":"string"}}},"LookupJob":{"type":"object","properties":{"status":{"type":"string","enum":["pending","running","completed","failed"]},"total":{"type":"integer"},"not_found":{"type":"integer"},"errors":{"type":"integer"},"found":{"type":"integer"},"currency":{"type":"string","example":"USD"},"processed":{"type":"integer"},"cost":{"type":"string","description":"Rolled-up billed cost"},"job_id":{"type":"string"}}},"LookupResponse":{"type":"object","description":"Number-intelligence result. HLR signals (`reachable`/`valid`/`roaming`) are tri-state: true, false, or null when the resolving provider does not report them.","properties":{"reachable":{"type":"boolean","description":"HLR: subscriber currently reachable","nullable":true},"source":{"type":"string","description":"Resolution source: enum | http | hlr | stub | number_range | dial_code | cache | ledger"},"valid":{"type":"boolean","description":"HLR: number is a valid/allocated MSISDN","nullable":true},"cached":{"type":"boolean","description":"Served from cache / durable ledger (not a fresh provider hit)"},"country":{"type":"string","description":"ISO 3166-1 alpha-2","example":"AE"},"msisdn":{"type":"string","example":"+971501234567"},"mccmnc":{"type":"string","description":"Resolved MCC+MNC","example":"42402"},"network":{"type":"string","description":"Canonical operator name","example":"Etisalat"},"line_type":{"type":"string","enum":["mobile","landline","voip","unknown"],"nullable":true},"roaming":{"type":"boolean","description":"HLR: subscriber roaming","nullable":true},"ported":{"type":"boolean","example":false}}},"MessageStatus":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["submitted","delivered","failed","expired","unknown"]},"from":{"type":"string"},"currency":{"type":"string"},"to":{"type":"string"},"direction":{"type":"string","enum":["mt","mo"]},"submitted_at":{"type":"string","format":"date-time"},"segments":{"type":"integer"},"cost":{"type":"string"},"delivered_at":{"type":"string","format":"date-time","nullable":true}}},"OmniMessageStatus":{"type":"object","properties":{"status":{"type":"string","enum":["submitted","delivered","read","failed","expired","received","unknown"]},"from":{"type":"string"},"currency":{"type":"string"},"to":{"type":"string"},"direction":{"type":"string","enum":["mt","mo"]},"sent_at":{"type":"string","format":"date-time"},"message_id":{"type":"string"},"charge":{"type":"string"},"channel":{"type":"string"},"conversation_category":{"type":"string","enum":["marketing","utility","authentication","service",""]},"delivered_via":{"type":"string"}}},"OmniSendRequest":{"type":"object","required":["channel","from","to"],"properties":{"interactive":{"type":"object","description":"Channel-specific interactive payload (buttons / list / card / carousel)"},"text":{"type":"string","description":"Free-form text body"},"template":{"type":"object","properties":{"name":{"type":"string"},"variables":{"type":"object","description":"Template parameters (positional or header/body/button structured)"},"language":{"type":"string","example":"en"}}},"from":{"type":"string","description":"Sender (WA phone-number-id / Viber sender / RCS agent / SMS sender id)"},"to":{"description":"One destination MSISDN, a list of MSISDNs, or a comma/space-separated string.","oneOf":[{"type":"string","example":"+971501234567"},{"type":"array","items":{"type":"string"}}]},"fallback":{"type":"array","description":"Ordered fallback channels tried if the primary fails","items":{"type":"string","enum":["whatsapp","viber","rcs","sms"]}},"language":{"type":"string","description":"Per-message language code (template language / content locale)","example":"ar"},"channel":{"type":"string","enum":["whatsapp","viber","rcs","sms"],"description":"Primary channel"},"media":{"type":"object","properties":{"type":{"type":"string","enum":["image","document","video","audio"]},"url":{"type":"string","format":"uri"},"caption":{"type":"string"}}}}},"OmniSendResponse":{"type":"object","properties":{"status":{"type":"string","example":"submitted"},"to":{"type":"string"},"message_id":{"type":"string"},"channel":{"type":"string","description":"Channel actually used (after fallback)"},"provider_msg_id":{"type":"string"},"delivered_via":{"type":"string","description":"Same as channel — the winning channel"}}},"SendMessageRequest":{"type":"object","required":["from","to","text"],"properties":{"text":{"type":"string","description":"Message body (aliases: message, short_message)","example":"Hello from Murasil!"},"reference":{"type":"string","description":"Your internal reference ID — echoed back in the DLR callback","maxLength":255},"from":{"type":"string","description":"Sender ID (alphanumeric or MSISDN)","example":"ACME"},"to":{"description":"One destination MSISDN, a list of MSISDNs, or a comma/space-separated string.","oneOf":[{"type":"string","example":"+971501234567"},{"type":"array","items":{"type":"string"},"example":["+971501234567","+966512345678"]}]},"data_coding":{"default":0,"type":"integer","enum":[0,8],"description":"0 = GSM-7, 8 = UCS2 (unicode)"},"registered_delivery":{"default":1,"type":"integer","description":"1 = request DLR"},"validity_period":{"type":"string","description":"SMPP validity period string"},"callback_url":{"type":"string","format":"uri","description":"DLR callback URL (alias: dlr_url), https on a public host. Signed with HMAC-SHA256. Redirects are not followed. Delivery is best effort: one attempt, and callbacks wait or drop if your endpoint falls far behind, so reconcile against GET /api/v1/messages/{id}.","maxLength":2048}}},"SendMessageResponse":{"type":"object","description":"Single-recipient acknowledgement. For multiple recipients a BulkMessageResponse is returned instead.","properties":{"status":{"type":"string","example":"submitted"},"reference":{"type":"string"},"to":{"type":"string"},"message_id":{"type":"string","description":"Murasil message ID"},"segments":{"type":"integer","example":1}}}},"securitySchemes":{"ApiKeyAuth":{"in":"header","name":"X-API-Key","type":"apiKey"},"BearerAuth":{"scheme":"bearer","type":"http","bearerFormat":"JWT"}}},"paths":{"/auth/login":{"post":{"responses":{"200":{"description":"JWT tokens and user info","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginResponse"}}}},"401":{"description":"Invalid credentials, otp_required or invalid_otp","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"two_factor_setup_required: staff must enrol in the web app first","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many failed attempts","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["Authentication"],"summary":"Login and get JWT tokens","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequest"}}}}}},"/auth/logout":{"delete":{"responses":{"200":{"description":"Token revoked","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}}}}}}},"tags":["Authentication"],"summary":"Revoke current token"}},"/auth/refresh":{"post":{"responses":{"200":{"description":"New access token","content":{"application/json":{"schema":{"type":"object","properties":{"access_token":{"type":"string"}}}}}},"401":{"description":"Invalid or expired token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["Authentication"],"summary":"Refresh access token","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["refresh_token"],"properties":{"refresh_token":{"type":"string"}}}}}}}},"/lookup":{"get":{"parameters":[{"in":"query","name":"msisdn","required":true,"schema":{"type":"string"},"example":"+971501234567"}],"responses":{"200":{"description":"Number information","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LookupResponse"}}}},"402":{"description":"No credit for this lookup","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Key lacks the send scope a billed lookup needs, or the caller has no customer to bill (no_billable_customer)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Missing msisdn, or not an optional + and 6 to 15 digits","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["Number Intelligence"],"summary":"Synchronous MNP/HLR number lookup (billed per query)","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}]}},"/lookup/batch":{"post":{"responses":{"202":{"description":"Job accepted (pending)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LookupJob"}}}},"402":{"description":"Credit does not cover the batch","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Key lacks the send scope a billed lookup needs, or the caller has no customer to bill (no_billable_customer)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Empty or oversized batch, or an entry that is not an optional + and 6 to 15 digits","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["Number Intelligence"],"summary":"Submit an async batch lookup job","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchLookupRequest"}}}}}},"/lookup/jobs/{id}":{"get":{"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Job status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LookupJob"}}}},"404":{"description":"Job not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["Number Intelligence"],"summary":"Get batch lookup job status + counters","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}]}},"/lookup/jobs/{id}/results":{"get":{"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}},{"in":"query","name":"limit","required":false,"schema":{"default":1000,"type":"integer"}}],"responses":{"200":{"description":"Per-number outcomes","content":{"application/json":{"schema":{"type":"object","properties":{"count":{"type":"integer"},"results":{"type":"array","items":{"$ref":"#/components/schemas/LookupResponse"}},"job_id":{"type":"string"}}}}}},"404":{"description":"Job not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["Number Intelligence"],"summary":"Get per-number results for a batch lookup job","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}]}},"/messages":{"post":{"responses":{"202":{"description":"Message(s) accepted for delivery","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/SendMessageResponse"},{"$ref":"#/components/schemas/BulkMessageResponse"}]}}}},"402":{"description":"Insufficient balance","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Invalid request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["Messaging"],"summary":"Send an SMS to one or many recipients","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendMessageRequest"}}}}}},"/messages/{id}":{"get":{"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Message details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageStatus"}}}},"404":{"description":"Message not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["Messaging"],"summary":"Get message status and CDR details"}},"/omni/messages":{"post":{"responses":{"202":{"description":"Message accepted (records the channel actually used)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OmniSendResponse"}}}},"403":{"description":"Recipient opted out","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Invalid request / outside session window / template not approved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["Omnichannel"],"summary":"Send a message over WhatsApp / Viber / RCS / SMS with cost/capability-aware fallback","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OmniSendRequest"}}}}}},"/omni/messages/{id}":{"get":{"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Latest omnichannel message status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OmniMessageStatus"}}}},"404":{"description":"Message not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["Omnichannel"],"summary":"Get the latest status for an omnichannel message"}},"/portal/invoices":{"get":{"responses":{"200":{"description":"Own invoices","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["Portal"],"summary":"List the caller's invoices","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}]}},"/portal/invoices/{id}/cdr.csv":{"get":{"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"CSV file"},"404":{"description":"Invoice not found / not owned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["Portal"],"summary":"Download an invoice's traffic CDR as CSV (own invoice only)","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}]}},"/portal/lookup":{"get":{"parameters":[{"in":"query","name":"msisdn","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Number info + rate","content":{"application/json":{"schema":{"type":"object"}}}},"402":{"description":"No credit for this lookup","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Key lacks the send scope a billed lookup needs, or the caller has no customer to bill (no_billable_customer)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Missing msisdn, or not an optional + and 6 to 15 digits","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["Portal"],"summary":"MNP/HLR lookup for a destination + the caller's rate for it (billed per query)","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}]}},"/portal/rates":{"get":{"responses":{"200":{"description":"Active rates","content":{"application/json":{"schema":{"type":"object"}}}}},"tags":["Portal"],"summary":"List the caller's active rate sheet","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}]}},"/portal/stats":{"get":{"parameters":[{"in":"query","name":"from","required":false,"schema":{"type":"string","format":"date"}},{"in":"query","name":"to","required":false,"schema":{"type":"string","format":"date"}},{"in":"query","name":"range","required":false,"schema":{"type":"string","enum":["7d","30d","90d"]}},{"in":"query","name":"currency","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Totals + daily series for the caller's customer","content":{"application/json":{"schema":{"type":"object"}}}},"403":{"description":"Key not scoped to a customer","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"tags":["Portal"],"summary":"Customer SMS statistics (scoped to the key's customer)","security":[{"ApiKeyAuth":[]},{"BearerAuth":[]}]}},"/webhooks/dlr":{"post":{"responses":{"200":{"description":"Accepted"}},"tags":["Webhooks"],"summary":"Receive DLR (delivery receipt) from supplier","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DlrWebhook"}}}}}}},"security":[{"BearerAuth":[]}],"openapi":"3.1.0","servers":[{"description":"Current version","url":"/api/v1"}]}