{
    "openapi": "3.1.0",
    "info": {
        "title": "MobiReach SMS API",
        "version": "1.0.0",
        "summary": "Send SMS and one-time codes to Ugandan mobile numbers, manage contacts and receive signed delivery webhooks.",
        "description": "All requests use HTTPS and JSON. Authenticate with a Bearer API key from **Developers \u2192 API keys** in the app: `Authorization: Bearer mr_live_\u2026` (live) or `mr_test_\u2026` (test: nothing is sent to a network, nothing is billed, OTP codes are always `000000`).\n\n**Credits and segments.** 1 credit = 1 SMS segment. A GSM-7 message is 1 segment up to 160 characters, then 153 per segment; a message with any other character (emoji, curly quotes, most accents) is Unicode: 70 characters, then 67 per segment. Every message of a batch is billed by its own segments; credits are reserved when the batch is accepted and settled once every message was attempted (messages a network never accepted are not billed).\n\n**Rate limits.** Each key may make 120 requests per minute (configurable per key), the OTP endpoints 10 per minute per key on top. Requests with a missing or invalid key are limited per IP. Every response carries `X-RateLimit-Limit` and `X-RateLimit-Remaining`; a 429 also carries `Retry-After` (seconds).\n\n**Idempotency.** POST /messages, /sms/send, /otp/send, /contacts and /groups accept an `Idempotency-Key` header (1-255 printable characters, e.g. a UUID). A retry with the same key and the same body returns the stored response with `Idempotent-Replayed: true` and never sends twice; the same key with a different body is `422 idempotency_mismatch`; while the first request is still running a retry gets `409 idempotency_in_progress`. Keys are remembered for 24 hours per API key.\n\n**Errors** always look like `{\"error\": {\"type\", \"code\", \"message\", \"request_id\"}}`; `code` is the value to branch on (see the error table in the docs). Every response has an `X-Request-Id` header; quote it to support.",
        "contact": {
            "name": "MobiReach support",
            "url": "https://mobireach.theanathoth.com/contact"
        }
    },
    "servers": [
        {
            "url": "https://mobireach.theanathoth.com/api/v1",
            "description": "Live and test (the key decides)"
        }
    ],
    "security": [
        {
            "bearerAuth": []
        }
    ],
    "tags": [
        {
            "name": "Messages",
            "description": "Send SMS and follow each message."
        },
        {
            "name": "OTP",
            "description": "One-time verification codes."
        },
        {
            "name": "Contacts",
            "description": "Contacts and groups of the workspace."
        },
        {
            "name": "Account",
            "description": "Balance and sender IDs."
        },
        {
            "name": "Webhooks",
            "description": "Endpoints that receive signed events."
        }
    ],
    "paths": {
        "/messages": {
            "post": {
                "tags": [
                    "Messages"
                ],
                "summary": "Send SMS",
                "description": "Creates one batch with one message per recipient. Each `to` entry comes back in `accepted` or in `rejected` with a reason (`invalid_number`, `duplicate`, `opted_out`). **201** when queued (or scheduled) with every message id; **202** when the batch is held for a quick review (large sends, links, new accounts; `status` `in_review`), waits for a team approver (the workspace requires send approval and the member who created this live key cannot approve sends; `status` `pending_approval`) or has more than 1000 recipients (it is built in the background; follow it with GET /batches/{batch_id}). Test keys never reach a network and are not billed.",
                "x-scope": "sms:send",
                "x-idempotent": true,
                "parameters": [
                    {
                        "$ref": "#/components/parameters/IdempotencyKey"
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "to": {
                                        "description": "One number, a comma-separated list, or an array (at most 1000). Local (0772\u2026) or international (+256772\u2026) format.",
                                        "oneOf": [
                                            {
                                                "type": "string"
                                            },
                                            {
                                                "type": "array",
                                                "items": {
                                                    "type": "string"
                                                }
                                            }
                                        ]
                                    },
                                    "group_ids": {
                                        "type": "array",
                                        "items": {
                                            "type": "string"
                                        },
                                        "description": "Send to every member of these groups (grp_12 or 12). Combine with `to` freely; duplicates are sent once."
                                    },
                                    "message": {
                                        "type": "string",
                                        "maxLength": 1600,
                                        "description": "The text. Placeholders like {{first_name}} or {{first_name|friend}} are filled from saved contacts. Required unless `template_id` is given."
                                    },
                                    "template_id": {
                                        "type": "string",
                                        "description": "Use a saved template's text (tpl_7 or 7) when `message` is absent."
                                    },
                                    "from": {
                                        "type": "string",
                                        "description": "An approved sender ID of the workspace (alias: `sender_id`). Absent: the workspace default, else the shared platform sender."
                                    },
                                    "sender_id": {
                                        "type": "string",
                                        "description": "Alias of `from`."
                                    },
                                    "schedule_at": {
                                        "type": "string",
                                        "format": "date-time",
                                        "description": "ISO 8601 time to send at, up to 90 days ahead. Without an offset it is read in the workspace timezone (Africa/Kampala)."
                                    },
                                    "client_ref": {
                                        "type": "string",
                                        "maxLength": 100,
                                        "description": "Your own reference, stored on the batch and returned everywhere it appears (filter with GET /batches?client_ref=)."
                                    },
                                    "metadata": {
                                        "type": "object",
                                        "maxProperties": 20,
                                        "additionalProperties": {
                                            "type": [
                                                "string",
                                                "number",
                                                "boolean",
                                                "null"
                                            ]
                                        },
                                        "description": "Up to 20 keys of your own data, echoed on the batch."
                                    },
                                    "opt_out": {
                                        "type": "boolean",
                                        "description": "true: always add the opt-out line; false: never; absent: the workspace policy."
                                    }
                                },
                                "example": {
                                    "to": [
                                        "+256772123456",
                                        "0700123456"
                                    ],
                                    "message": "Hello {{first_name|parent}}, school reopens on Monday 13 Oct.",
                                    "client_ref": "term3-reopening",
                                    "metadata": {
                                        "term": "3"
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "Queued or scheduled",
                        "headers": {
                            "X-RateLimit-Limit": {
                                "$ref": "#/components/headers/X-RateLimit-Limit"
                            },
                            "X-RateLimit-Remaining": {
                                "$ref": "#/components/headers/X-RateLimit-Remaining"
                            },
                            "X-Request-Id": {
                                "$ref": "#/components/headers/X-Request-Id"
                            }
                        },
                        "content": {
                            "application/json": {
                                "example": {
                                    "batch_id": "batch_311",
                                    "status": "queued",
                                    "kind": "api",
                                    "mode": "live",
                                    "livemode": true,
                                    "recipients": 2,
                                    "estimated_segments": 1,
                                    "estimated_credits": 2,
                                    "client_ref": "term3-reopening",
                                    "metadata": {
                                        "term": "3"
                                    },
                                    "scheduled_at": null,
                                    "created_at": "2026-10-06T09:00:00+00:00",
                                    "accepted": [
                                        "+256772123456",
                                        "+256700123456"
                                    ],
                                    "rejected": [],
                                    "message_id": null,
                                    "messages": [
                                        {
                                            "message_id": "msg_90211",
                                            "to": "+256772123456",
                                            "status": "queued"
                                        },
                                        {
                                            "message_id": "msg_90212",
                                            "to": "+256700123456",
                                            "status": "queued"
                                        }
                                    ]
                                }
                            }
                        }
                    },
                    "202": {
                        "description": "Held for review, or being built in the background",
                        "headers": {
                            "X-RateLimit-Limit": {
                                "$ref": "#/components/headers/X-RateLimit-Limit"
                            },
                            "X-RateLimit-Remaining": {
                                "$ref": "#/components/headers/X-RateLimit-Remaining"
                            },
                            "X-Request-Id": {
                                "$ref": "#/components/headers/X-Request-Id"
                            }
                        },
                        "content": {
                            "application/json": {
                                "example": {
                                    "batch_id": "batch_311",
                                    "status": "preparing",
                                    "kind": "api",
                                    "mode": "live",
                                    "livemode": true,
                                    "recipients": 4800,
                                    "estimated_segments": 1,
                                    "estimated_credits": 2,
                                    "client_ref": "term3-reopening",
                                    "metadata": {
                                        "term": "3"
                                    },
                                    "scheduled_at": null,
                                    "created_at": "2026-10-06T09:00:00+00:00",
                                    "accepted": [
                                        "+256772123456",
                                        "\u2026"
                                    ],
                                    "rejected": [],
                                    "message_id": null,
                                    "messages": [],
                                    "message": "Large batch: the messages are being prepared in the background. Follow it with GET /v1/batches/batch_311 and list its messages with GET /v1/messages?batch_id=batch_311."
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "402": {
                        "$ref": "#/components/responses/InsufficientCredits"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "409": {
                        "description": "idempotency_in_progress (a request with this Idempotency-Key is still running) or conflict (the audience changed while the send was processed; nothing was sent): retry shortly",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                },
                                "example": {
                                    "error": {
                                        "type": "conflict",
                                        "code": "idempotency_in_progress",
                                        "message": "A request with this Idempotency-Key is still being processed. Retry shortly.",
                                        "request_id": "req_7h2kq9x0m1n3b5v7c9z1"
                                    }
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Invalid request: validation_failed, invalid_sender_id, too_many_recipients, no_valid_recipients, content_blocked, invalid_schedule, unknown_group, unknown_template, idempotency_mismatch, invalid_idempotency_key",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                },
                                "example": {
                                    "error": {
                                        "type": "invalid_request",
                                        "code": "invalid_sender_id",
                                        "message": "Sender ID \"ACME\" is not approved for this workspace.",
                                        "request_id": "req_7h2kq9x0m1n3b5v7c9z1"
                                    }
                                }
                            }
                        }
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/ServerError"
                    }
                },
                "operationId": "sendMessages"
            },
            "get": {
                "tags": [
                    "Messages"
                ],
                "operationId": "listMessages",
                "summary": "List messages",
                "description": "Newest first, cursor-paginated. Filter by batch, status (comma-separated) or number.",
                "x-scope": "reports:read",
                "parameters": [
                    {
                        "name": "batch_id",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Only this batch (batch_311)."
                    },
                    {
                        "name": "status",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "type": "string"
                        },
                        "description": "One or more of: queued, sending, sent, delivered, failed, expired, unknown, skipped, cancelled, opted_out."
                    },
                    {
                        "name": "to",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Only this phone number."
                    },
                    {
                        "$ref": "#/components/parameters/Limit"
                    },
                    {
                        "$ref": "#/components/parameters/Cursor"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "A page of messages",
                        "headers": {
                            "X-RateLimit-Limit": {
                                "$ref": "#/components/headers/X-RateLimit-Limit"
                            },
                            "X-RateLimit-Remaining": {
                                "$ref": "#/components/headers/X-RateLimit-Remaining"
                            },
                            "X-Request-Id": {
                                "$ref": "#/components/headers/X-Request-Id"
                            }
                        },
                        "content": {
                            "application/json": {
                                "example": {
                                    "data": [
                                        {
                                            "message_id": "msg_90211",
                                            "batch_id": "batch_311",
                                            "to": "+256772123456",
                                            "status": "delivered",
                                            "failure_reason": null,
                                            "skip_reason": null,
                                            "network": "mtn",
                                            "segments": 1,
                                            "credits": 1,
                                            "refunded": false,
                                            "client_ref": "term3-reopening",
                                            "livemode": true,
                                            "created_at": "2026-10-06T09:00:00+00:00",
                                            "sent_at": "2026-10-06T09:00:02+00:00",
                                            "delivered_at": "2026-10-06T09:00:03+00:00"
                                        }
                                    ],
                                    "has_more": true,
                                    "next_cursor": "aWQ6OTAyMTE"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/ServerError"
                    }
                }
            }
        },
        "/sms/send": {
            "post": {
                "tags": [
                    "Messages"
                ],
                "summary": "Send SMS (alias of POST /messages)",
                "description": "Identical to POST /messages.",
                "x-scope": "sms:send",
                "x-idempotent": true,
                "parameters": [
                    {
                        "$ref": "#/components/parameters/IdempotencyKey"
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "to": {
                                        "description": "One number, a comma-separated list, or an array (at most 1000). Local (0772\u2026) or international (+256772\u2026) format.",
                                        "oneOf": [
                                            {
                                                "type": "string"
                                            },
                                            {
                                                "type": "array",
                                                "items": {
                                                    "type": "string"
                                                }
                                            }
                                        ]
                                    },
                                    "group_ids": {
                                        "type": "array",
                                        "items": {
                                            "type": "string"
                                        },
                                        "description": "Send to every member of these groups (grp_12 or 12). Combine with `to` freely; duplicates are sent once."
                                    },
                                    "message": {
                                        "type": "string",
                                        "maxLength": 1600,
                                        "description": "The text. Placeholders like {{first_name}} or {{first_name|friend}} are filled from saved contacts. Required unless `template_id` is given."
                                    },
                                    "template_id": {
                                        "type": "string",
                                        "description": "Use a saved template's text (tpl_7 or 7) when `message` is absent."
                                    },
                                    "from": {
                                        "type": "string",
                                        "description": "An approved sender ID of the workspace (alias: `sender_id`). Absent: the workspace default, else the shared platform sender."
                                    },
                                    "sender_id": {
                                        "type": "string",
                                        "description": "Alias of `from`."
                                    },
                                    "schedule_at": {
                                        "type": "string",
                                        "format": "date-time",
                                        "description": "ISO 8601 time to send at, up to 90 days ahead. Without an offset it is read in the workspace timezone (Africa/Kampala)."
                                    },
                                    "client_ref": {
                                        "type": "string",
                                        "maxLength": 100,
                                        "description": "Your own reference, stored on the batch and returned everywhere it appears (filter with GET /batches?client_ref=)."
                                    },
                                    "metadata": {
                                        "type": "object",
                                        "maxProperties": 20,
                                        "additionalProperties": {
                                            "type": [
                                                "string",
                                                "number",
                                                "boolean",
                                                "null"
                                            ]
                                        },
                                        "description": "Up to 20 keys of your own data, echoed on the batch."
                                    },
                                    "opt_out": {
                                        "type": "boolean",
                                        "description": "true: always add the opt-out line; false: never; absent: the workspace policy."
                                    }
                                },
                                "example": {
                                    "to": [
                                        "+256772123456",
                                        "0700123456"
                                    ],
                                    "message": "Hello {{first_name|parent}}, school reopens on Monday 13 Oct.",
                                    "client_ref": "term3-reopening",
                                    "metadata": {
                                        "term": "3"
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "Queued or scheduled",
                        "headers": {
                            "X-RateLimit-Limit": {
                                "$ref": "#/components/headers/X-RateLimit-Limit"
                            },
                            "X-RateLimit-Remaining": {
                                "$ref": "#/components/headers/X-RateLimit-Remaining"
                            },
                            "X-Request-Id": {
                                "$ref": "#/components/headers/X-Request-Id"
                            }
                        },
                        "content": {
                            "application/json": {
                                "example": {
                                    "batch_id": "batch_311",
                                    "status": "queued",
                                    "kind": "api",
                                    "mode": "live",
                                    "livemode": true,
                                    "recipients": 2,
                                    "estimated_segments": 1,
                                    "estimated_credits": 2,
                                    "client_ref": "term3-reopening",
                                    "metadata": {
                                        "term": "3"
                                    },
                                    "scheduled_at": null,
                                    "created_at": "2026-10-06T09:00:00+00:00",
                                    "accepted": [
                                        "+256772123456",
                                        "+256700123456"
                                    ],
                                    "rejected": [],
                                    "message_id": null,
                                    "messages": [
                                        {
                                            "message_id": "msg_90211",
                                            "to": "+256772123456",
                                            "status": "queued"
                                        },
                                        {
                                            "message_id": "msg_90212",
                                            "to": "+256700123456",
                                            "status": "queued"
                                        }
                                    ]
                                }
                            }
                        }
                    },
                    "202": {
                        "description": "Held for review, or being built in the background",
                        "headers": {
                            "X-RateLimit-Limit": {
                                "$ref": "#/components/headers/X-RateLimit-Limit"
                            },
                            "X-RateLimit-Remaining": {
                                "$ref": "#/components/headers/X-RateLimit-Remaining"
                            },
                            "X-Request-Id": {
                                "$ref": "#/components/headers/X-Request-Id"
                            }
                        },
                        "content": {
                            "application/json": {
                                "example": {
                                    "batch_id": "batch_311",
                                    "status": "preparing",
                                    "kind": "api",
                                    "mode": "live",
                                    "livemode": true,
                                    "recipients": 4800,
                                    "estimated_segments": 1,
                                    "estimated_credits": 2,
                                    "client_ref": "term3-reopening",
                                    "metadata": {
                                        "term": "3"
                                    },
                                    "scheduled_at": null,
                                    "created_at": "2026-10-06T09:00:00+00:00",
                                    "accepted": [
                                        "+256772123456",
                                        "\u2026"
                                    ],
                                    "rejected": [],
                                    "message_id": null,
                                    "messages": [],
                                    "message": "Large batch: the messages are being prepared in the background. Follow it with GET /v1/batches/batch_311 and list its messages with GET /v1/messages?batch_id=batch_311."
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "402": {
                        "$ref": "#/components/responses/InsufficientCredits"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "409": {
                        "description": "idempotency_in_progress (a request with this Idempotency-Key is still running) or conflict (the audience changed while the send was processed; nothing was sent): retry shortly",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                },
                                "example": {
                                    "error": {
                                        "type": "conflict",
                                        "code": "idempotency_in_progress",
                                        "message": "A request with this Idempotency-Key is still being processed. Retry shortly.",
                                        "request_id": "req_7h2kq9x0m1n3b5v7c9z1"
                                    }
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Invalid request: validation_failed, invalid_sender_id, too_many_recipients, no_valid_recipients, content_blocked, invalid_schedule, unknown_group, unknown_template, idempotency_mismatch, invalid_idempotency_key",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                },
                                "example": {
                                    "error": {
                                        "type": "invalid_request",
                                        "code": "invalid_sender_id",
                                        "message": "Sender ID \"ACME\" is not approved for this workspace.",
                                        "request_id": "req_7h2kq9x0m1n3b5v7c9z1"
                                    }
                                }
                            }
                        }
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/ServerError"
                    }
                },
                "operationId": "smsSend"
            }
        },
        "/messages/{message_id}": {
            "get": {
                "tags": [
                    "Messages"
                ],
                "operationId": "getMessage",
                "summary": "Get one message",
                "description": "The status of one recipient. `message_id` is the id returned by the send call and carried by every message.* webhook.",
                "x-scope": "reports:read",
                "parameters": [
                    {
                        "name": "message_id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "msg_90211"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "The message",
                        "headers": {
                            "X-RateLimit-Limit": {
                                "$ref": "#/components/headers/X-RateLimit-Limit"
                            },
                            "X-RateLimit-Remaining": {
                                "$ref": "#/components/headers/X-RateLimit-Remaining"
                            },
                            "X-Request-Id": {
                                "$ref": "#/components/headers/X-Request-Id"
                            }
                        },
                        "content": {
                            "application/json": {
                                "example": {
                                    "message_id": "msg_90211",
                                    "batch_id": "batch_311",
                                    "to": "+256772123456",
                                    "status": "delivered",
                                    "failure_reason": null,
                                    "skip_reason": null,
                                    "network": "mtn",
                                    "segments": 1,
                                    "credits": 1,
                                    "refunded": false,
                                    "client_ref": "term3-reopening",
                                    "livemode": true,
                                    "created_at": "2026-10-06T09:00:00+00:00",
                                    "sent_at": "2026-10-06T09:00:02+00:00",
                                    "delivered_at": "2026-10-06T09:00:03+00:00"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "404": {
                        "$ref": "#/components/responses/NotFound"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/ServerError"
                    }
                }
            }
        },
        "/batches": {
            "get": {
                "tags": [
                    "Messages"
                ],
                "operationId": "listBatches",
                "summary": "List batches",
                "x-scope": "reports:read",
                "parameters": [
                    {
                        "name": "client_ref",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Only batches with this client_ref."
                    },
                    {
                        "$ref": "#/components/parameters/Limit"
                    },
                    {
                        "$ref": "#/components/parameters/Cursor"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "A page of batches",
                        "headers": {
                            "X-RateLimit-Limit": {
                                "$ref": "#/components/headers/X-RateLimit-Limit"
                            },
                            "X-RateLimit-Remaining": {
                                "$ref": "#/components/headers/X-RateLimit-Remaining"
                            },
                            "X-Request-Id": {
                                "$ref": "#/components/headers/X-Request-Id"
                            }
                        },
                        "content": {
                            "application/json": {
                                "example": {
                                    "data": [
                                        {
                                            "batch_id": "batch_311",
                                            "status": "sent",
                                            "kind": "api",
                                            "mode": "live",
                                            "livemode": true,
                                            "recipients": 2,
                                            "estimated_segments": 1,
                                            "estimated_credits": 2,
                                            "client_ref": "term3-reopening",
                                            "metadata": {
                                                "term": "3"
                                            },
                                            "scheduled_at": null,
                                            "created_at": "2026-10-06T09:00:00+00:00",
                                            "counts": {
                                                "queued": 0,
                                                "sending": 0,
                                                "sent": 0,
                                                "delivered": 2,
                                                "failed": 0,
                                                "expired": 0,
                                                "unknown": 0,
                                                "skipped": 0,
                                                "cancelled": 0,
                                                "opted_out": 0
                                            },
                                            "credits_used": 2,
                                            "sent_at": "2026-10-06T09:00:02+00:00",
                                            "completed_at": "2026-10-06T09:00:05+00:00"
                                        }
                                    ],
                                    "has_more": true,
                                    "next_cursor": "aWQ6OTAyMTE"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/ServerError"
                    }
                }
            }
        },
        "/batches/{batch_id}": {
            "get": {
                "tags": [
                    "Messages"
                ],
                "operationId": "getBatch",
                "summary": "Get one batch",
                "description": "Status and per-status counts of one send request.",
                "x-scope": "reports:read",
                "parameters": [
                    {
                        "name": "batch_id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "batch_311"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "The batch",
                        "headers": {
                            "X-RateLimit-Limit": {
                                "$ref": "#/components/headers/X-RateLimit-Limit"
                            },
                            "X-RateLimit-Remaining": {
                                "$ref": "#/components/headers/X-RateLimit-Remaining"
                            },
                            "X-Request-Id": {
                                "$ref": "#/components/headers/X-Request-Id"
                            }
                        },
                        "content": {
                            "application/json": {
                                "example": {
                                    "batch_id": "batch_311",
                                    "status": "sent",
                                    "kind": "api",
                                    "mode": "live",
                                    "livemode": true,
                                    "recipients": 2,
                                    "estimated_segments": 1,
                                    "estimated_credits": 2,
                                    "client_ref": "term3-reopening",
                                    "metadata": {
                                        "term": "3"
                                    },
                                    "scheduled_at": null,
                                    "created_at": "2026-10-06T09:00:00+00:00",
                                    "counts": {
                                        "queued": 0,
                                        "sending": 0,
                                        "sent": 0,
                                        "delivered": 2,
                                        "failed": 0,
                                        "expired": 0,
                                        "unknown": 0,
                                        "skipped": 0,
                                        "cancelled": 0,
                                        "opted_out": 0
                                    },
                                    "credits_used": 2,
                                    "sent_at": "2026-10-06T09:00:02+00:00",
                                    "completed_at": "2026-10-06T09:00:05+00:00"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "404": {
                        "$ref": "#/components/responses/NotFound"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/ServerError"
                    }
                }
            }
        },
        "/otp/send": {
            "post": {
                "tags": [
                    "OTP"
                ],
                "operationId": "sendOtp",
                "summary": "Send a one-time code",
                "description": "Sends a 6-digit code as \"{code} is your {app_name} verification code. It expires in N minutes. Do not share it.\" from the workspace's sender ID. A new code replaces any earlier one for the same number and purpose. Live keys are billed per segment (usually 1 credit). Limits: one code per number every 30 s, 5 per number per hour; unusual traffic (volume spikes, one number range, codes never verified) is throttled automatically. Test keys send nothing and the code is always 000000.",
                "x-scope": "otp:send",
                "x-idempotent": true,
                "parameters": [
                    {
                        "$ref": "#/components/parameters/IdempotencyKey"
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "to"
                                ],
                                "properties": {
                                    "to": {
                                        "type": "string",
                                        "description": "The phone number."
                                    },
                                    "purpose": {
                                        "type": "string",
                                        "maxLength": 40,
                                        "description": "Your label (login, signup, payment \u2026). Codes of different purposes are independent. Default `verify`."
                                    },
                                    "app_name": {
                                        "type": "string",
                                        "maxLength": 30,
                                        "description": "Name in the text. Default: the workspace name."
                                    },
                                    "expiry_seconds": {
                                        "type": "integer",
                                        "minimum": 300,
                                        "maximum": 600,
                                        "default": 300
                                    },
                                    "from": {
                                        "type": "string",
                                        "description": "An approved sender ID (alias `sender_id`)."
                                    }
                                },
                                "example": {
                                    "to": "+256772123456",
                                    "purpose": "login",
                                    "app_name": "Acme"
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "Code sent",
                        "headers": {
                            "X-RateLimit-Limit": {
                                "$ref": "#/components/headers/X-RateLimit-Limit"
                            },
                            "X-RateLimit-Remaining": {
                                "$ref": "#/components/headers/X-RateLimit-Remaining"
                            },
                            "X-Request-Id": {
                                "$ref": "#/components/headers/X-Request-Id"
                            }
                        },
                        "content": {
                            "application/json": {
                                "example": {
                                    "request_id": "otp_8f2kq0x1m3n5b7v9c4z6a2s1",
                                    "to": "+256772123456",
                                    "status": "sent",
                                    "purpose": "login",
                                    "expires_in": 300,
                                    "expires_at": "2026-10-06T09:05:00+00:00",
                                    "segments": 1,
                                    "credits_charged": 1,
                                    "mode": "live",
                                    "livemode": true
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "402": {
                        "$ref": "#/components/responses/InsufficientCredits"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "409": {
                        "$ref": "#/components/responses/IdempotencyInProgress"
                    },
                    "422": {
                        "description": "invalid_phone, invalid_sender_id, validation_failed, idempotency_mismatch, invalid_idempotency_key",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                },
                                "example": {
                                    "error": {
                                        "type": "invalid_request",
                                        "code": "invalid_phone",
                                        "message": "The \"to\" number is not a valid Ugandan mobile number.",
                                        "request_id": "req_7h2kq9x0m1n3b5v7c9z1"
                                    }
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "otp_cooldown, otp_hourly_cap, otp_locked, otp_throttled or rate_limit_exceeded (Retry-After says when to retry)",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                },
                                "example": {
                                    "error": {
                                        "type": "rate_limited",
                                        "code": "otp_cooldown",
                                        "message": "Please wait 30 seconds before requesting another code.",
                                        "request_id": "req_7h2kq9x0m1n3b5v7c9z1"
                                    }
                                }
                            }
                        },
                        "headers": {
                            "Retry-After": {
                                "$ref": "#/components/headers/Retry-After"
                            }
                        }
                    },
                    "500": {
                        "$ref": "#/components/responses/ServerError"
                    },
                    "503": {
                        "description": "otp_delivery_failed: no network accepted the code (nothing billed)",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                },
                                "example": {
                                    "error": {
                                        "type": "api_error",
                                        "code": "otp_delivery_failed",
                                        "message": "The code could not be delivered right now. Nothing was billed; please try again.",
                                        "request_id": "req_7h2kq9x0m1n3b5v7c9z1"
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/otp/verify": {
            "post": {
                "tags": [
                    "OTP"
                ],
                "operationId": "verifyOtp",
                "summary": "Verify a code",
                "description": "Checks what the user typed against the code of `request_id`. Codes are single-use; 5 wrong attempts lock the number for 30 minutes. The older form `{to, code, purpose}` (newest code for that number) also works.",
                "x-scope": "otp:verify",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "code"
                                ],
                                "properties": {
                                    "request_id": {
                                        "type": "string"
                                    },
                                    "code": {
                                        "type": "string"
                                    },
                                    "to": {
                                        "type": "string"
                                    },
                                    "purpose": {
                                        "type": "string"
                                    }
                                },
                                "example": {
                                    "request_id": "otp_8f2kq0x1m3n5b7v9c4z6a2s1",
                                    "code": "482910"
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Approved",
                        "headers": {
                            "X-RateLimit-Limit": {
                                "$ref": "#/components/headers/X-RateLimit-Limit"
                            },
                            "X-RateLimit-Remaining": {
                                "$ref": "#/components/headers/X-RateLimit-Remaining"
                            },
                            "X-Request-Id": {
                                "$ref": "#/components/headers/X-Request-Id"
                            }
                        },
                        "content": {
                            "application/json": {
                                "example": {
                                    "request_id": "otp_8f2kq0x1m3n5b7v9c4z6a2s1",
                                    "to": "+256772123456",
                                    "verified": true,
                                    "status": "approved"
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Unknown request_id for this workspace and key mode",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                },
                                "example": {
                                    "error": {
                                        "type": "not_found",
                                        "code": "resource_not_found",
                                        "message": "No code with this request_id exists for this API key's workspace and mode.",
                                        "request_id": "req_7h2kq9x0m1n3b5v7c9z1"
                                    }
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "otp_incorrect, otp_expired, otp_already_verified, otp_max_attempts (body also has verified: false and status)",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                },
                                "example": {
                                    "error": {
                                        "type": "invalid_request",
                                        "code": "otp_incorrect",
                                        "message": "The code is incorrect.",
                                        "request_id": "req_7h2kq9x0m1n3b5v7c9z1"
                                    }
                                }
                            }
                        }
                    },
                    "429": {
                        "description": "otp_locked",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                },
                                "example": {
                                    "error": {
                                        "type": "rate_limited",
                                        "code": "otp_locked",
                                        "message": "Too many wrong codes for this number. Wait before trying again.",
                                        "request_id": "req_7h2kq9x0m1n3b5v7c9z1"
                                    }
                                }
                            }
                        },
                        "headers": {
                            "Retry-After": {
                                "$ref": "#/components/headers/Retry-After"
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "500": {
                        "$ref": "#/components/responses/ServerError"
                    }
                }
            }
        },
        "/contacts": {
            "get": {
                "tags": [
                    "Contacts"
                ],
                "operationId": "listContacts",
                "summary": "List contacts",
                "x-scope": "contacts:read",
                "parameters": [
                    {
                        "name": "q",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Name, or a number (whole or part) in any local form: 0772\u2026, 772\u2026, 256\u2026, +256\u2026."
                    },
                    {
                        "name": "phone",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Exact number."
                    },
                    {
                        "name": "group_id",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Members of this group (grp_12)."
                    },
                    {
                        "name": "consent_status",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "type": "string"
                        },
                        "description": "subscribed, opted_out or unknown."
                    },
                    {
                        "name": "updated_since",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "type": "string"
                        },
                        "description": "ISO 8601: changed at or after."
                    },
                    {
                        "$ref": "#/components/parameters/Limit"
                    },
                    {
                        "$ref": "#/components/parameters/Cursor"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "A page of contacts",
                        "headers": {
                            "X-RateLimit-Limit": {
                                "$ref": "#/components/headers/X-RateLimit-Limit"
                            },
                            "X-RateLimit-Remaining": {
                                "$ref": "#/components/headers/X-RateLimit-Remaining"
                            },
                            "X-Request-Id": {
                                "$ref": "#/components/headers/X-Request-Id"
                            }
                        },
                        "content": {
                            "application/json": {
                                "example": {
                                    "data": [
                                        {
                                            "id": "ct_5512",
                                            "phone": "+256772123456",
                                            "first_name": "Grace",
                                            "last_name": "Achieng",
                                            "date_of_birth": null,
                                            "anniversary_date": null,
                                            "custom_fields": {
                                                "class": "P5"
                                            },
                                            "consent_status": "subscribed",
                                            "suppressed": false,
                                            "can_receive": true,
                                            "groups": [
                                                "grp_12"
                                            ],
                                            "source": "api",
                                            "created_at": "2026-10-01T08:00:00+00:00",
                                            "updated_at": "2026-10-05T12:30:00+00:00"
                                        }
                                    ],
                                    "has_more": true,
                                    "next_cursor": "aWQ6OTAyMTE"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/ServerError"
                    }
                }
            },
            "post": {
                "tags": [
                    "Contacts"
                ],
                "operationId": "upsertContact",
                "summary": "Create (or update) a contact",
                "description": "Keyed by phone. A new number is saved as subscribed (consent source `api`, recorded with your key), unless it is on the workspace suppression list (then opted out). If the number exists, only the fields you send change and consent never does. `custom_fields` are merged; a null value removes a field.",
                "x-scope": "contacts:write",
                "x-idempotent": true,
                "parameters": [
                    {
                        "$ref": "#/components/parameters/IdempotencyKey"
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "phone"
                                ],
                                "properties": {
                                    "phone": {
                                        "type": "string"
                                    },
                                    "first_name": {
                                        "type": [
                                            "string",
                                            "null"
                                        ],
                                        "maxLength": 80
                                    },
                                    "last_name": {
                                        "type": [
                                            "string",
                                            "null"
                                        ],
                                        "maxLength": 80
                                    },
                                    "date_of_birth": {
                                        "type": [
                                            "string",
                                            "null"
                                        ],
                                        "format": "date"
                                    },
                                    "anniversary_date": {
                                        "type": [
                                            "string",
                                            "null"
                                        ],
                                        "format": "date"
                                    },
                                    "custom_fields": {
                                        "type": "object",
                                        "additionalProperties": {
                                            "type": [
                                                "string",
                                                "number",
                                                "boolean",
                                                "null"
                                            ]
                                        }
                                    },
                                    "group_ids": {
                                        "type": "array",
                                        "items": {
                                            "type": "string"
                                        },
                                        "description": "Add to these static groups (never removes)."
                                    }
                                },
                                "example": {
                                    "phone": "0772123456",
                                    "first_name": "Grace",
                                    "custom_fields": {
                                        "class": "P5"
                                    },
                                    "group_ids": [
                                        "grp_12"
                                    ]
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Existing contact updated",
                        "headers": {
                            "X-RateLimit-Limit": {
                                "$ref": "#/components/headers/X-RateLimit-Limit"
                            },
                            "X-RateLimit-Remaining": {
                                "$ref": "#/components/headers/X-RateLimit-Remaining"
                            },
                            "X-Request-Id": {
                                "$ref": "#/components/headers/X-Request-Id"
                            }
                        },
                        "content": {
                            "application/json": {
                                "example": {
                                    "id": "ct_5512",
                                    "phone": "+256772123456",
                                    "first_name": "Grace",
                                    "last_name": "Achieng",
                                    "date_of_birth": null,
                                    "anniversary_date": null,
                                    "custom_fields": {
                                        "class": "P5"
                                    },
                                    "consent_status": "subscribed",
                                    "suppressed": false,
                                    "can_receive": true,
                                    "groups": [
                                        "grp_12"
                                    ],
                                    "source": "api",
                                    "created_at": "2026-10-01T08:00:00+00:00",
                                    "updated_at": "2026-10-05T12:30:00+00:00",
                                    "created": false
                                }
                            }
                        }
                    },
                    "201": {
                        "description": "Created",
                        "headers": {
                            "X-RateLimit-Limit": {
                                "$ref": "#/components/headers/X-RateLimit-Limit"
                            },
                            "X-RateLimit-Remaining": {
                                "$ref": "#/components/headers/X-RateLimit-Remaining"
                            },
                            "X-Request-Id": {
                                "$ref": "#/components/headers/X-Request-Id"
                            }
                        },
                        "content": {
                            "application/json": {
                                "example": {
                                    "id": "ct_5512",
                                    "phone": "+256772123456",
                                    "first_name": "Grace",
                                    "last_name": "Achieng",
                                    "date_of_birth": null,
                                    "anniversary_date": null,
                                    "custom_fields": {
                                        "class": "P5"
                                    },
                                    "consent_status": "subscribed",
                                    "suppressed": false,
                                    "can_receive": true,
                                    "groups": [
                                        "grp_12"
                                    ],
                                    "source": "api",
                                    "created_at": "2026-10-01T08:00:00+00:00",
                                    "updated_at": "2026-10-05T12:30:00+00:00",
                                    "created": true
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "409": {
                        "$ref": "#/components/responses/IdempotencyInProgress"
                    },
                    "422": {
                        "description": "validation_failed, invalid_phone, unknown_group, idempotency_mismatch, invalid_idempotency_key",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                },
                                "example": {
                                    "error": {
                                        "type": "invalid_request",
                                        "code": "invalid_phone",
                                        "message": "The phone number is not a valid Ugandan mobile number.",
                                        "request_id": "req_7h2kq9x0m1n3b5v7c9z1"
                                    }
                                }
                            }
                        }
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/ServerError"
                    }
                }
            }
        },
        "/contacts/{contact_id}": {
            "get": {
                "tags": [
                    "Contacts"
                ],
                "operationId": "getContact",
                "summary": "Get a contact",
                "x-scope": "contacts:read",
                "parameters": [
                    {
                        "name": "contact_id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "ct_5512"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "The contact",
                        "headers": {
                            "X-RateLimit-Limit": {
                                "$ref": "#/components/headers/X-RateLimit-Limit"
                            },
                            "X-RateLimit-Remaining": {
                                "$ref": "#/components/headers/X-RateLimit-Remaining"
                            },
                            "X-Request-Id": {
                                "$ref": "#/components/headers/X-Request-Id"
                            }
                        },
                        "content": {
                            "application/json": {
                                "example": {
                                    "id": "ct_5512",
                                    "phone": "+256772123456",
                                    "first_name": "Grace",
                                    "last_name": "Achieng",
                                    "date_of_birth": null,
                                    "anniversary_date": null,
                                    "custom_fields": {
                                        "class": "P5"
                                    },
                                    "consent_status": "subscribed",
                                    "suppressed": false,
                                    "can_receive": true,
                                    "groups": [
                                        "grp_12"
                                    ],
                                    "source": "api",
                                    "created_at": "2026-10-01T08:00:00+00:00",
                                    "updated_at": "2026-10-05T12:30:00+00:00"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "404": {
                        "$ref": "#/components/responses/NotFound"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/ServerError"
                    }
                }
            },
            "patch": {
                "tags": [
                    "Contacts"
                ],
                "operationId": "updateContact",
                "summary": "Update a contact",
                "description": "Only the fields present change. Phone and consent can't be changed here.",
                "x-scope": "contacts:write",
                "parameters": [
                    {
                        "name": "contact_id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "ct_5512"
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [],
                                "properties": {
                                    "first_name": {
                                        "type": [
                                            "string",
                                            "null"
                                        ],
                                        "maxLength": 80
                                    },
                                    "last_name": {
                                        "type": [
                                            "string",
                                            "null"
                                        ],
                                        "maxLength": 80
                                    },
                                    "date_of_birth": {
                                        "type": [
                                            "string",
                                            "null"
                                        ],
                                        "format": "date"
                                    },
                                    "anniversary_date": {
                                        "type": [
                                            "string",
                                            "null"
                                        ],
                                        "format": "date"
                                    },
                                    "custom_fields": {
                                        "type": "object",
                                        "additionalProperties": {
                                            "type": [
                                                "string",
                                                "number",
                                                "boolean",
                                                "null"
                                            ]
                                        }
                                    },
                                    "group_ids": {
                                        "type": "array",
                                        "items": {
                                            "type": "string"
                                        },
                                        "description": "Add to these static groups (never removes)."
                                    }
                                },
                                "example": {
                                    "last_name": "Achieng",
                                    "custom_fields": {
                                        "class": "P6"
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "The updated contact",
                        "headers": {
                            "X-RateLimit-Limit": {
                                "$ref": "#/components/headers/X-RateLimit-Limit"
                            },
                            "X-RateLimit-Remaining": {
                                "$ref": "#/components/headers/X-RateLimit-Remaining"
                            },
                            "X-Request-Id": {
                                "$ref": "#/components/headers/X-Request-Id"
                            }
                        },
                        "content": {
                            "application/json": {
                                "example": {
                                    "id": "ct_5512",
                                    "phone": "+256772123456",
                                    "first_name": "Grace",
                                    "last_name": "Achieng",
                                    "date_of_birth": null,
                                    "anniversary_date": null,
                                    "custom_fields": {
                                        "class": "P5"
                                    },
                                    "consent_status": "subscribed",
                                    "suppressed": false,
                                    "can_receive": true,
                                    "groups": [
                                        "grp_12"
                                    ],
                                    "source": "api",
                                    "created_at": "2026-10-01T08:00:00+00:00",
                                    "updated_at": "2026-10-05T12:30:00+00:00"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "404": {
                        "$ref": "#/components/responses/NotFound"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/ServerError"
                    }
                }
            },
            "delete": {
                "tags": [
                    "Contacts"
                ],
                "operationId": "deleteContact",
                "summary": "Delete a contact",
                "description": "A suppression of the number (opt-out) stays in force.",
                "x-scope": "contacts:write",
                "parameters": [
                    {
                        "name": "contact_id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "ct_5512"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Deleted",
                        "headers": {
                            "X-RateLimit-Limit": {
                                "$ref": "#/components/headers/X-RateLimit-Limit"
                            },
                            "X-RateLimit-Remaining": {
                                "$ref": "#/components/headers/X-RateLimit-Remaining"
                            },
                            "X-Request-Id": {
                                "$ref": "#/components/headers/X-Request-Id"
                            }
                        },
                        "content": {
                            "application/json": {
                                "example": {
                                    "id": "ct_5512",
                                    "deleted": true
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "404": {
                        "$ref": "#/components/responses/NotFound"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/ServerError"
                    }
                }
            }
        },
        "/groups": {
            "get": {
                "tags": [
                    "Contacts"
                ],
                "operationId": "listGroups",
                "summary": "List groups",
                "x-scope": "contacts:read",
                "responses": {
                    "200": {
                        "description": "Groups",
                        "headers": {
                            "X-RateLimit-Limit": {
                                "$ref": "#/components/headers/X-RateLimit-Limit"
                            },
                            "X-RateLimit-Remaining": {
                                "$ref": "#/components/headers/X-RateLimit-Remaining"
                            },
                            "X-Request-Id": {
                                "$ref": "#/components/headers/X-Request-Id"
                            }
                        },
                        "content": {
                            "application/json": {
                                "example": {
                                    "data": [
                                        {
                                            "id": "grp_12",
                                            "name": "Parents P5",
                                            "type": "static",
                                            "contacts": 248,
                                            "created_at": "2026-10-01T08:00:00+00:00",
                                            "updated_at": "2026-10-05T12:30:00+00:00"
                                        }
                                    ]
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/ServerError"
                    }
                }
            },
            "post": {
                "tags": [
                    "Contacts"
                ],
                "operationId": "createGroup",
                "summary": "Create a group",
                "description": "A group with the same name (any case) is returned instead of a duplicate (200, created false).",
                "x-scope": "contacts:write",
                "x-idempotent": true,
                "parameters": [
                    {
                        "$ref": "#/components/parameters/IdempotencyKey"
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "name"
                                ],
                                "properties": {
                                    "name": {
                                        "type": "string",
                                        "maxLength": 80
                                    }
                                },
                                "example": {
                                    "name": "Parents P5"
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "Created",
                        "headers": {
                            "X-RateLimit-Limit": {
                                "$ref": "#/components/headers/X-RateLimit-Limit"
                            },
                            "X-RateLimit-Remaining": {
                                "$ref": "#/components/headers/X-RateLimit-Remaining"
                            },
                            "X-Request-Id": {
                                "$ref": "#/components/headers/X-Request-Id"
                            }
                        },
                        "content": {
                            "application/json": {
                                "example": {
                                    "id": "grp_12",
                                    "name": "Parents P5",
                                    "type": "static",
                                    "contacts": 248,
                                    "created_at": "2026-10-01T08:00:00+00:00",
                                    "updated_at": "2026-10-05T12:30:00+00:00",
                                    "created": true
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "409": {
                        "$ref": "#/components/responses/IdempotencyInProgress"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/ServerError"
                    }
                }
            }
        },
        "/groups/{group_id}": {
            "get": {
                "tags": [
                    "Contacts"
                ],
                "operationId": "getGroup",
                "summary": "Get a group",
                "x-scope": "contacts:read",
                "parameters": [
                    {
                        "name": "group_id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "grp_12"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "The group",
                        "headers": {
                            "X-RateLimit-Limit": {
                                "$ref": "#/components/headers/X-RateLimit-Limit"
                            },
                            "X-RateLimit-Remaining": {
                                "$ref": "#/components/headers/X-RateLimit-Remaining"
                            },
                            "X-Request-Id": {
                                "$ref": "#/components/headers/X-Request-Id"
                            }
                        },
                        "content": {
                            "application/json": {
                                "example": {
                                    "id": "grp_12",
                                    "name": "Parents P5",
                                    "type": "static",
                                    "contacts": 248,
                                    "created_at": "2026-10-01T08:00:00+00:00",
                                    "updated_at": "2026-10-05T12:30:00+00:00"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "404": {
                        "$ref": "#/components/responses/NotFound"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/ServerError"
                    }
                }
            },
            "patch": {
                "tags": [
                    "Contacts"
                ],
                "operationId": "renameGroup",
                "summary": "Rename a group",
                "description": "Group names are unique in a workspace (any case): a name another group already has is refused with 422.",
                "x-scope": "contacts:write",
                "parameters": [
                    {
                        "name": "group_id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "grp_12"
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "name"
                                ],
                                "properties": {
                                    "name": {
                                        "type": "string",
                                        "maxLength": 80
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "The group",
                        "headers": {
                            "X-RateLimit-Limit": {
                                "$ref": "#/components/headers/X-RateLimit-Limit"
                            },
                            "X-RateLimit-Remaining": {
                                "$ref": "#/components/headers/X-RateLimit-Remaining"
                            },
                            "X-Request-Id": {
                                "$ref": "#/components/headers/X-Request-Id"
                            }
                        },
                        "content": {
                            "application/json": {
                                "example": {
                                    "id": "grp_12",
                                    "name": "Parents P5",
                                    "type": "static",
                                    "contacts": 248,
                                    "created_at": "2026-10-01T08:00:00+00:00",
                                    "updated_at": "2026-10-05T12:30:00+00:00"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "validation_failed (missing name, or another group already has it)",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                },
                                "example": {
                                    "error": {
                                        "type": "invalid_request",
                                        "code": "validation_failed",
                                        "message": "Another group in this workspace is already called \"Parents P5\".",
                                        "request_id": "req_7h2kq9x0m1n3b5v7c9z1"
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "404": {
                        "$ref": "#/components/responses/NotFound"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/ServerError"
                    }
                }
            },
            "delete": {
                "tags": [
                    "Contacts"
                ],
                "operationId": "deleteGroup",
                "summary": "Delete a group",
                "description": "Its contacts are kept.",
                "x-scope": "contacts:write",
                "parameters": [
                    {
                        "name": "group_id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "grp_12"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Deleted",
                        "headers": {
                            "X-RateLimit-Limit": {
                                "$ref": "#/components/headers/X-RateLimit-Limit"
                            },
                            "X-RateLimit-Remaining": {
                                "$ref": "#/components/headers/X-RateLimit-Remaining"
                            },
                            "X-Request-Id": {
                                "$ref": "#/components/headers/X-Request-Id"
                            }
                        },
                        "content": {
                            "application/json": {
                                "example": {
                                    "id": "grp_12",
                                    "deleted": true
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "404": {
                        "$ref": "#/components/responses/NotFound"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/ServerError"
                    }
                }
            }
        },
        "/groups/{group_id}/contacts": {
            "get": {
                "tags": [
                    "Contacts"
                ],
                "operationId": "listGroupContacts",
                "summary": "List a group's contacts",
                "x-scope": "contacts:read",
                "parameters": [
                    {
                        "name": "group_id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "grp_12"
                    },
                    {
                        "$ref": "#/components/parameters/Limit"
                    },
                    {
                        "$ref": "#/components/parameters/Cursor"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "A page of contacts",
                        "headers": {
                            "X-RateLimit-Limit": {
                                "$ref": "#/components/headers/X-RateLimit-Limit"
                            },
                            "X-RateLimit-Remaining": {
                                "$ref": "#/components/headers/X-RateLimit-Remaining"
                            },
                            "X-Request-Id": {
                                "$ref": "#/components/headers/X-Request-Id"
                            }
                        },
                        "content": {
                            "application/json": {
                                "example": {
                                    "data": [
                                        {
                                            "id": "ct_5512",
                                            "phone": "+256772123456",
                                            "first_name": "Grace",
                                            "last_name": "Achieng",
                                            "date_of_birth": null,
                                            "anniversary_date": null,
                                            "custom_fields": {
                                                "class": "P5"
                                            },
                                            "consent_status": "subscribed",
                                            "suppressed": false,
                                            "can_receive": true,
                                            "groups": [
                                                "grp_12"
                                            ],
                                            "source": "api",
                                            "created_at": "2026-10-01T08:00:00+00:00",
                                            "updated_at": "2026-10-05T12:30:00+00:00"
                                        }
                                    ],
                                    "has_more": true,
                                    "next_cursor": "aWQ6OTAyMTE"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "404": {
                        "$ref": "#/components/responses/NotFound"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/ServerError"
                    }
                }
            },
            "post": {
                "tags": [
                    "Contacts"
                ],
                "operationId": "addGroupContacts",
                "summary": "Add contacts to a group",
                "description": "Existing contacts only, by id or phone (up to 1,000 per call). Segments (type `segment`) compute their members and can't be changed.",
                "x-scope": "contacts:write",
                "parameters": [
                    {
                        "name": "group_id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "grp_12"
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "contact_ids": {
                                        "type": "array",
                                        "items": {
                                            "type": "string"
                                        },
                                        "maxItems": 1000
                                    },
                                    "phones": {
                                        "type": "array",
                                        "items": {
                                            "type": "string"
                                        },
                                        "maxItems": 1000
                                    }
                                },
                                "example": {
                                    "contact_ids": [
                                        "ct_5512"
                                    ],
                                    "phones": [
                                        "0700123456"
                                    ]
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Membership changed",
                        "headers": {
                            "X-RateLimit-Limit": {
                                "$ref": "#/components/headers/X-RateLimit-Limit"
                            },
                            "X-RateLimit-Remaining": {
                                "$ref": "#/components/headers/X-RateLimit-Remaining"
                            },
                            "X-Request-Id": {
                                "$ref": "#/components/headers/X-Request-Id"
                            }
                        },
                        "content": {
                            "application/json": {
                                "example": {
                                    "group": {
                                        "id": "grp_12",
                                        "name": "Parents P5",
                                        "type": "static",
                                        "contacts": 248,
                                        "created_at": "2026-10-01T08:00:00+00:00",
                                        "updated_at": "2026-10-05T12:30:00+00:00"
                                    },
                                    "added": 2,
                                    "not_found": 0
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "404": {
                        "$ref": "#/components/responses/NotFound"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/ServerError"
                    }
                }
            },
            "delete": {
                "tags": [
                    "Contacts"
                ],
                "operationId": "removeGroupContacts",
                "summary": "Remove contacts from a group",
                "description": "The contacts themselves are kept.",
                "x-scope": "contacts:write",
                "parameters": [
                    {
                        "name": "group_id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "grp_12"
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "contact_ids": {
                                        "type": "array",
                                        "items": {
                                            "type": "string"
                                        },
                                        "maxItems": 1000
                                    },
                                    "phones": {
                                        "type": "array",
                                        "items": {
                                            "type": "string"
                                        },
                                        "maxItems": 1000
                                    }
                                },
                                "example": {
                                    "contact_ids": [
                                        "ct_5512"
                                    ],
                                    "phones": [
                                        "0700123456"
                                    ]
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Membership changed",
                        "headers": {
                            "X-RateLimit-Limit": {
                                "$ref": "#/components/headers/X-RateLimit-Limit"
                            },
                            "X-RateLimit-Remaining": {
                                "$ref": "#/components/headers/X-RateLimit-Remaining"
                            },
                            "X-Request-Id": {
                                "$ref": "#/components/headers/X-Request-Id"
                            }
                        },
                        "content": {
                            "application/json": {
                                "example": {
                                    "group": {
                                        "id": "grp_12",
                                        "name": "Parents P5",
                                        "type": "static",
                                        "contacts": 248,
                                        "created_at": "2026-10-01T08:00:00+00:00",
                                        "updated_at": "2026-10-05T12:30:00+00:00"
                                    },
                                    "removed": 2,
                                    "not_found": 0
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "404": {
                        "$ref": "#/components/responses/NotFound"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/ServerError"
                    }
                }
            }
        },
        "/balance": {
            "get": {
                "tags": [
                    "Account"
                ],
                "operationId": "getBalance",
                "summary": "Credit balance",
                "description": "1 credit = 1 SMS segment. `available_for_api` excludes free trial credits, which can't pay for live API traffic.",
                "x-scope": "balance:read",
                "responses": {
                    "200": {
                        "description": "The balance",
                        "headers": {
                            "X-RateLimit-Limit": {
                                "$ref": "#/components/headers/X-RateLimit-Limit"
                            },
                            "X-RateLimit-Remaining": {
                                "$ref": "#/components/headers/X-RateLimit-Remaining"
                            },
                            "X-Request-Id": {
                                "$ref": "#/components/headers/X-Request-Id"
                            }
                        },
                        "content": {
                            "application/json": {
                                "example": {
                                    "currency": "UGX",
                                    "credits": 12500,
                                    "reserved": 300,
                                    "available": 12200,
                                    "available_for_api": 12180,
                                    "promo_credits": 20
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/ServerError"
                    }
                }
            }
        },
        "/sender-ids": {
            "get": {
                "tags": [
                    "Account"
                ],
                "operationId": "listSenderIds",
                "summary": "Approved sender IDs",
                "description": "Scope `sender_ids:read` (keys with `balance:read` or `sms:send` may also call it).",
                "x-scope": "sender_ids:read",
                "responses": {
                    "200": {
                        "description": "Approved sender IDs",
                        "headers": {
                            "X-RateLimit-Limit": {
                                "$ref": "#/components/headers/X-RateLimit-Limit"
                            },
                            "X-RateLimit-Remaining": {
                                "$ref": "#/components/headers/X-RateLimit-Remaining"
                            },
                            "X-Request-Id": {
                                "$ref": "#/components/headers/X-Request-Id"
                            }
                        },
                        "content": {
                            "application/json": {
                                "example": {
                                    "data": [
                                        {
                                            "sender_id": "KPSCHOOL",
                                            "status": "approved",
                                            "default": true
                                        }
                                    ]
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/ServerError"
                    }
                }
            }
        },
        "/webhooks": {
            "get": {
                "tags": [
                    "Webhooks"
                ],
                "operationId": "listWebhooks",
                "summary": "List webhook endpoints",
                "x-scope": "webhooks:write",
                "responses": {
                    "200": {
                        "description": "Endpoints",
                        "headers": {
                            "X-RateLimit-Limit": {
                                "$ref": "#/components/headers/X-RateLimit-Limit"
                            },
                            "X-RateLimit-Remaining": {
                                "$ref": "#/components/headers/X-RateLimit-Remaining"
                            },
                            "X-Request-Id": {
                                "$ref": "#/components/headers/X-Request-Id"
                            }
                        },
                        "content": {
                            "application/json": {
                                "example": {
                                    "data": [
                                        {
                                            "id": "wh_3",
                                            "url": "https://api.example.com/hooks/mobireach",
                                            "description": "Production",
                                            "events": [
                                                "message.delivered",
                                                "message.failed"
                                            ],
                                            "status": "active",
                                            "disabled_reason": null,
                                            "secret_rotated_at": null,
                                            "created_at": "2026-10-01T08:00:00+00:00",
                                            "updated_at": "2026-10-01T08:00:00+00:00",
                                            "health": "healthy"
                                        }
                                    ],
                                    "events": [
                                        "message.sent",
                                        "message.delivered",
                                        "message.failed",
                                        "message.inbound",
                                        "contact.opted_out",
                                        "contact.opted_in",
                                        "payment.confirmed",
                                        "payment.failed",
                                        "payment.refunded",
                                        "campaign.completed",
                                        "wallet.low_balance"
                                    ]
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/ServerError"
                    }
                }
            },
            "post": {
                "tags": [
                    "Webhooks"
                ],
                "operationId": "createWebhook",
                "summary": "Create a webhook endpoint",
                "description": "https only; the host must resolve to public addresses (private, loopback and link-local ranges are refused, at save and before every delivery). The signing `secret` is returned only here and on rotation. At most 5 endpoints per workspace.",
                "x-scope": "webhooks:write",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [
                                    "url"
                                ],
                                "properties": {
                                    "url": {
                                        "type": "string",
                                        "format": "uri",
                                        "maxLength": 300
                                    },
                                    "description": {
                                        "type": [
                                            "string",
                                            "null"
                                        ],
                                        "maxLength": 120
                                    },
                                    "events": {
                                        "type": "array",
                                        "items": {
                                            "type": "string",
                                            "enum": [
                                                "message.sent",
                                                "message.delivered",
                                                "message.failed",
                                                "message.inbound",
                                                "contact.opted_out",
                                                "contact.opted_in",
                                                "payment.confirmed",
                                                "payment.failed",
                                                "payment.refunded",
                                                "campaign.completed",
                                                "wallet.low_balance"
                                            ]
                                        },
                                        "description": "Default: message.delivered, message.failed, payment.confirmed."
                                    }
                                },
                                "example": {
                                    "url": "https://api.example.com/hooks/mobireach",
                                    "events": [
                                        "message.delivered",
                                        "message.failed"
                                    ]
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "Created",
                        "headers": {
                            "X-RateLimit-Limit": {
                                "$ref": "#/components/headers/X-RateLimit-Limit"
                            },
                            "X-RateLimit-Remaining": {
                                "$ref": "#/components/headers/X-RateLimit-Remaining"
                            },
                            "X-Request-Id": {
                                "$ref": "#/components/headers/X-Request-Id"
                            }
                        },
                        "content": {
                            "application/json": {
                                "example": {
                                    "id": "wh_3",
                                    "url": "https://api.example.com/hooks/mobireach",
                                    "description": "Production",
                                    "events": [
                                        "message.delivered",
                                        "message.failed"
                                    ],
                                    "status": "active",
                                    "disabled_reason": null,
                                    "secret_rotated_at": null,
                                    "created_at": "2026-10-01T08:00:00+00:00",
                                    "updated_at": "2026-10-01T08:00:00+00:00",
                                    "secret": "whsec_3f1c\u2026",
                                    "created": true
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "invalid_webhook_url, webhook_limit_reached, validation_failed",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                },
                                "example": {
                                    "error": {
                                        "type": "invalid_request",
                                        "code": "invalid_webhook_url",
                                        "message": "The webhook URL must point at a public address, not a private, loopback or link-local one.",
                                        "request_id": "req_7h2kq9x0m1n3b5v7c9z1"
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/ServerError"
                    }
                }
            }
        },
        "/webhooks/{webhook_id}": {
            "get": {
                "tags": [
                    "Webhooks"
                ],
                "operationId": "getWebhook",
                "summary": "Get an endpoint",
                "x-scope": "webhooks:write",
                "parameters": [
                    {
                        "name": "webhook_id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "wh_3"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "The endpoint",
                        "headers": {
                            "X-RateLimit-Limit": {
                                "$ref": "#/components/headers/X-RateLimit-Limit"
                            },
                            "X-RateLimit-Remaining": {
                                "$ref": "#/components/headers/X-RateLimit-Remaining"
                            },
                            "X-Request-Id": {
                                "$ref": "#/components/headers/X-Request-Id"
                            }
                        },
                        "content": {
                            "application/json": {
                                "example": {
                                    "id": "wh_3",
                                    "url": "https://api.example.com/hooks/mobireach",
                                    "description": "Production",
                                    "events": [
                                        "message.delivered",
                                        "message.failed"
                                    ],
                                    "status": "active",
                                    "disabled_reason": null,
                                    "secret_rotated_at": null,
                                    "created_at": "2026-10-01T08:00:00+00:00",
                                    "updated_at": "2026-10-01T08:00:00+00:00",
                                    "health": "healthy"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "404": {
                        "$ref": "#/components/responses/NotFound"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/ServerError"
                    }
                }
            },
            "patch": {
                "tags": [
                    "Webhooks"
                ],
                "operationId": "updateWebhook",
                "summary": "Update an endpoint",
                "description": "Saving an endpoint also turns it back on after an automatic disable.",
                "x-scope": "webhooks:write",
                "parameters": [
                    {
                        "name": "webhook_id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "wh_3"
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "required": [],
                                "properties": {
                                    "url": {
                                        "type": "string",
                                        "format": "uri",
                                        "maxLength": 300
                                    },
                                    "description": {
                                        "type": [
                                            "string",
                                            "null"
                                        ],
                                        "maxLength": 120
                                    },
                                    "events": {
                                        "type": "array",
                                        "items": {
                                            "type": "string",
                                            "enum": [
                                                "message.sent",
                                                "message.delivered",
                                                "message.failed",
                                                "message.inbound",
                                                "contact.opted_out",
                                                "contact.opted_in",
                                                "payment.confirmed",
                                                "payment.failed",
                                                "payment.refunded",
                                                "campaign.completed",
                                                "wallet.low_balance"
                                            ]
                                        },
                                        "description": "Default: message.delivered, message.failed, payment.confirmed."
                                    }
                                },
                                "example": {
                                    "url": "https://api.example.com/hooks/mobireach",
                                    "events": [
                                        "message.delivered",
                                        "message.failed"
                                    ]
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "The endpoint",
                        "headers": {
                            "X-RateLimit-Limit": {
                                "$ref": "#/components/headers/X-RateLimit-Limit"
                            },
                            "X-RateLimit-Remaining": {
                                "$ref": "#/components/headers/X-RateLimit-Remaining"
                            },
                            "X-Request-Id": {
                                "$ref": "#/components/headers/X-Request-Id"
                            }
                        },
                        "content": {
                            "application/json": {
                                "example": {
                                    "id": "wh_3",
                                    "url": "https://api.example.com/hooks/mobireach",
                                    "description": "Production",
                                    "events": [
                                        "message.delivered",
                                        "message.failed"
                                    ],
                                    "status": "active",
                                    "disabled_reason": null,
                                    "secret_rotated_at": null,
                                    "created_at": "2026-10-01T08:00:00+00:00",
                                    "updated_at": "2026-10-01T08:00:00+00:00"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "404": {
                        "$ref": "#/components/responses/NotFound"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/ServerError"
                    }
                }
            },
            "delete": {
                "tags": [
                    "Webhooks"
                ],
                "operationId": "deleteWebhook",
                "summary": "Delete an endpoint",
                "x-scope": "webhooks:write",
                "parameters": [
                    {
                        "name": "webhook_id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "wh_3"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Deleted",
                        "headers": {
                            "X-RateLimit-Limit": {
                                "$ref": "#/components/headers/X-RateLimit-Limit"
                            },
                            "X-RateLimit-Remaining": {
                                "$ref": "#/components/headers/X-RateLimit-Remaining"
                            },
                            "X-Request-Id": {
                                "$ref": "#/components/headers/X-Request-Id"
                            }
                        },
                        "content": {
                            "application/json": {
                                "example": {
                                    "id": "wh_3",
                                    "deleted": true
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "404": {
                        "$ref": "#/components/responses/NotFound"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/ServerError"
                    }
                }
            }
        },
        "/webhooks/{webhook_id}/rotate-secret": {
            "post": {
                "tags": [
                    "Webhooks"
                ],
                "operationId": "rotateWebhookSecret",
                "summary": "Rotate the signing secret",
                "description": "For `grace_hours` (0-168, default 24) deliveries carry two `v1` signatures, one per secret, so you can switch without dropping events.",
                "x-scope": "webhooks:write",
                "parameters": [
                    {
                        "name": "webhook_id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "wh_3"
                    }
                ],
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "grace_hours": {
                                        "type": "integer",
                                        "minimum": 0,
                                        "maximum": 168,
                                        "default": 24
                                    }
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "New secret",
                        "headers": {
                            "X-RateLimit-Limit": {
                                "$ref": "#/components/headers/X-RateLimit-Limit"
                            },
                            "X-RateLimit-Remaining": {
                                "$ref": "#/components/headers/X-RateLimit-Remaining"
                            },
                            "X-Request-Id": {
                                "$ref": "#/components/headers/X-Request-Id"
                            }
                        },
                        "content": {
                            "application/json": {
                                "example": {
                                    "id": "wh_3",
                                    "url": "https://api.example.com/hooks/mobireach",
                                    "description": "Production",
                                    "events": [
                                        "message.delivered",
                                        "message.failed"
                                    ],
                                    "status": "active",
                                    "disabled_reason": null,
                                    "secret_rotated_at": null,
                                    "created_at": "2026-10-01T08:00:00+00:00",
                                    "updated_at": "2026-10-01T08:00:00+00:00",
                                    "secret": "whsec_9a7b\u2026",
                                    "previous_secret_valid_until": "2026-10-07T09:00:00+00:00"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "404": {
                        "$ref": "#/components/responses/NotFound"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/ServerError"
                    }
                }
            }
        },
        "/webhooks/{webhook_id}/test": {
            "post": {
                "tags": [
                    "Webhooks"
                ],
                "operationId": "testWebhook",
                "summary": "Send a test event",
                "description": "Sends one `webhook.test` event right away (no retries) and returns the delivery with your endpoint's response code.",
                "x-scope": "webhooks:write",
                "parameters": [
                    {
                        "name": "webhook_id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "wh_3"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "The test delivery",
                        "headers": {
                            "X-RateLimit-Limit": {
                                "$ref": "#/components/headers/X-RateLimit-Limit"
                            },
                            "X-RateLimit-Remaining": {
                                "$ref": "#/components/headers/X-RateLimit-Remaining"
                            },
                            "X-Request-Id": {
                                "$ref": "#/components/headers/X-Request-Id"
                            }
                        },
                        "content": {
                            "application/json": {
                                "example": {
                                    "id": "whd_901",
                                    "event_id": "evt_k2m9x8c7v6b5n4q3w2e1r0ty",
                                    "event": "webhook.test",
                                    "status": "delivered",
                                    "attempts": 1,
                                    "response_status": 200,
                                    "error": null,
                                    "duration_ms": 182,
                                    "next_attempt_at": null,
                                    "delivered_at": "2026-10-06T09:00:04+00:00",
                                    "created_at": "2026-10-06T09:00:03+00:00"
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "404": {
                        "$ref": "#/components/responses/NotFound"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/ServerError"
                    }
                }
            }
        },
        "/webhooks/{webhook_id}/deliveries": {
            "get": {
                "tags": [
                    "Webhooks"
                ],
                "operationId": "listWebhookDeliveries",
                "summary": "Recent deliveries",
                "x-scope": "webhooks:write",
                "parameters": [
                    {
                        "name": "webhook_id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "wh_3"
                    },
                    {
                        "$ref": "#/components/parameters/Limit"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Deliveries, newest first",
                        "headers": {
                            "X-RateLimit-Limit": {
                                "$ref": "#/components/headers/X-RateLimit-Limit"
                            },
                            "X-RateLimit-Remaining": {
                                "$ref": "#/components/headers/X-RateLimit-Remaining"
                            },
                            "X-Request-Id": {
                                "$ref": "#/components/headers/X-Request-Id"
                            }
                        },
                        "content": {
                            "application/json": {
                                "example": {
                                    "data": [
                                        {
                                            "id": "whd_901",
                                            "event_id": "evt_k2m9x8c7v6b5n4q3w2e1r0ty",
                                            "event": "message.delivered",
                                            "status": "delivered",
                                            "attempts": 1,
                                            "response_status": 200,
                                            "error": null,
                                            "duration_ms": 182,
                                            "next_attempt_at": null,
                                            "delivered_at": "2026-10-06T09:00:04+00:00",
                                            "created_at": "2026-10-06T09:00:03+00:00"
                                        }
                                    ]
                                }
                            }
                        }
                    },
                    "401": {
                        "$ref": "#/components/responses/Unauthorized"
                    },
                    "403": {
                        "$ref": "#/components/responses/Forbidden"
                    },
                    "404": {
                        "$ref": "#/components/responses/NotFound"
                    },
                    "429": {
                        "$ref": "#/components/responses/RateLimited"
                    },
                    "500": {
                        "$ref": "#/components/responses/ServerError"
                    }
                }
            }
        }
    },
    "webhooks": {
        "message.sent": {
            "post": {
                "summary": "message.sent",
                "description": "A message was accepted for delivery. Verify `X-MobiReach-Signature` before trusting the body (see the docs).",
                "parameters": [
                    {
                        "name": "X-MobiReach-Signature",
                        "in": "header",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "t=1759741203,v1=5f2b\u2026"
                    },
                    {
                        "name": "X-MobiReach-Event",
                        "in": "header",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "message.sent"
                    },
                    {
                        "name": "X-MobiReach-Event-Id",
                        "in": "header",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "evt_k2m9x8c7v6b5n4q3w2e1r0ty"
                    },
                    {
                        "name": "X-MobiReach-Delivery",
                        "in": "header",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "whd_901"
                    }
                ],
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/WebhookEvent"
                            },
                            "example": {
                                "id": "evt_k2m9x8c7v6b5n4q3w2e1r0ty",
                                "event": "message.sent",
                                "created_at": "2026-10-06T09:00:03+00:00",
                                "data": {
                                    "message_id": "msg_90211",
                                    "batch_id": "batch_311",
                                    "campaign_id": 311,
                                    "to": "+256772123456",
                                    "status": "sent",
                                    "network": "mtn",
                                    "segments": 1,
                                    "sent_at": "2026-10-06T09:00:02+00:00",
                                    "livemode": true
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "2XX": {
                        "description": "Received. Answer within 10 seconds; anything else is retried."
                    }
                }
            }
        },
        "message.delivered": {
            "post": {
                "summary": "message.delivered",
                "description": "The handset confirmed delivery. Verify `X-MobiReach-Signature` before trusting the body (see the docs).",
                "parameters": [
                    {
                        "name": "X-MobiReach-Signature",
                        "in": "header",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "t=1759741203,v1=5f2b\u2026"
                    },
                    {
                        "name": "X-MobiReach-Event",
                        "in": "header",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "message.delivered"
                    },
                    {
                        "name": "X-MobiReach-Event-Id",
                        "in": "header",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "evt_k2m9x8c7v6b5n4q3w2e1r0ty"
                    },
                    {
                        "name": "X-MobiReach-Delivery",
                        "in": "header",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "whd_901"
                    }
                ],
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/WebhookEvent"
                            },
                            "example": {
                                "id": "evt_k2m9x8c7v6b5n4q3w2e1r0ty",
                                "event": "message.delivered",
                                "created_at": "2026-10-06T09:00:03+00:00",
                                "data": {
                                    "message_id": "msg_90211",
                                    "batch_id": "batch_311",
                                    "campaign_id": 311,
                                    "to": "+256772123456",
                                    "status": "delivered",
                                    "failure_reason": null,
                                    "network": "mtn",
                                    "segments": 1,
                                    "delivered_at": "2026-10-06T09:00:03+00:00",
                                    "refunded": false,
                                    "livemode": true
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "2XX": {
                        "description": "Received. Answer within 10 seconds; anything else is retried."
                    }
                }
            }
        },
        "message.failed": {
            "post": {
                "summary": "message.failed",
                "description": "A message could not be delivered. Verify `X-MobiReach-Signature` before trusting the body (see the docs).",
                "parameters": [
                    {
                        "name": "X-MobiReach-Signature",
                        "in": "header",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "t=1759741203,v1=5f2b\u2026"
                    },
                    {
                        "name": "X-MobiReach-Event",
                        "in": "header",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "message.failed"
                    },
                    {
                        "name": "X-MobiReach-Event-Id",
                        "in": "header",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "evt_k2m9x8c7v6b5n4q3w2e1r0ty"
                    },
                    {
                        "name": "X-MobiReach-Delivery",
                        "in": "header",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "whd_901"
                    }
                ],
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/WebhookEvent"
                            },
                            "example": {
                                "id": "evt_k2m9x8c7v6b5n4q3w2e1r0ty",
                                "event": "message.failed",
                                "created_at": "2026-10-06T09:00:03+00:00",
                                "data": {
                                    "message_id": "msg_90211",
                                    "batch_id": "batch_311",
                                    "campaign_id": 311,
                                    "to": "+256772123456",
                                    "status": "failed",
                                    "failure_reason": "invalid_number",
                                    "network": "airtel",
                                    "segments": 1,
                                    "delivered_at": null,
                                    "refunded": false,
                                    "livemode": true
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "2XX": {
                        "description": "Received. Answer within 10 seconds; anything else is retried."
                    }
                }
            }
        },
        "message.inbound": {
            "post": {
                "summary": "message.inbound",
                "description": "A reply came in. Verify `X-MobiReach-Signature` before trusting the body (see the docs).",
                "parameters": [
                    {
                        "name": "X-MobiReach-Signature",
                        "in": "header",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "t=1759741203,v1=5f2b\u2026"
                    },
                    {
                        "name": "X-MobiReach-Event",
                        "in": "header",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "message.inbound"
                    },
                    {
                        "name": "X-MobiReach-Event-Id",
                        "in": "header",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "evt_k2m9x8c7v6b5n4q3w2e1r0ty"
                    },
                    {
                        "name": "X-MobiReach-Delivery",
                        "in": "header",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "whd_901"
                    }
                ],
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/WebhookEvent"
                            },
                            "example": {
                                "id": "evt_k2m9x8c7v6b5n4q3w2e1r0ty",
                                "event": "message.inbound",
                                "created_at": "2026-10-06T09:00:03+00:00",
                                "data": {
                                    "id": "in_771",
                                    "from": "+256772123456",
                                    "to": "6000",
                                    "text": "YES",
                                    "keyword": "kps",
                                    "action": "reply",
                                    "in_reply_to": "msg_90211",
                                    "batch_id": "batch_311",
                                    "received_at": "2026-10-06T10:12:00+00:00"
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "2XX": {
                        "description": "Received. Answer within 10 seconds; anything else is retried."
                    }
                }
            }
        },
        "contact.opted_out": {
            "post": {
                "summary": "contact.opted_out",
                "description": "A number opted out (STOP). Verify `X-MobiReach-Signature` before trusting the body (see the docs).",
                "parameters": [
                    {
                        "name": "X-MobiReach-Signature",
                        "in": "header",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "t=1759741203,v1=5f2b\u2026"
                    },
                    {
                        "name": "X-MobiReach-Event",
                        "in": "header",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "contact.opted_out"
                    },
                    {
                        "name": "X-MobiReach-Event-Id",
                        "in": "header",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "evt_k2m9x8c7v6b5n4q3w2e1r0ty"
                    },
                    {
                        "name": "X-MobiReach-Delivery",
                        "in": "header",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "whd_901"
                    }
                ],
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/WebhookEvent"
                            },
                            "example": {
                                "id": "evt_k2m9x8c7v6b5n4q3w2e1r0ty",
                                "event": "contact.opted_out",
                                "created_at": "2026-10-06T09:00:03+00:00",
                                "data": {
                                    "phone": "+256772123456",
                                    "source": "sms_stop",
                                    "contact_id": 5512,
                                    "opted_out_at": "2026-10-06T10:12:00+00:00"
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "2XX": {
                        "description": "Received. Answer within 10 seconds; anything else is retried."
                    }
                }
            }
        },
        "contact.opted_in": {
            "post": {
                "summary": "contact.opted_in",
                "description": "A number opted back in (START). Verify `X-MobiReach-Signature` before trusting the body (see the docs).",
                "parameters": [
                    {
                        "name": "X-MobiReach-Signature",
                        "in": "header",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "t=1759741203,v1=5f2b\u2026"
                    },
                    {
                        "name": "X-MobiReach-Event",
                        "in": "header",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "contact.opted_in"
                    },
                    {
                        "name": "X-MobiReach-Event-Id",
                        "in": "header",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "evt_k2m9x8c7v6b5n4q3w2e1r0ty"
                    },
                    {
                        "name": "X-MobiReach-Delivery",
                        "in": "header",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "whd_901"
                    }
                ],
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/WebhookEvent"
                            },
                            "example": {
                                "id": "evt_k2m9x8c7v6b5n4q3w2e1r0ty",
                                "event": "contact.opted_in",
                                "created_at": "2026-10-06T09:00:03+00:00",
                                "data": {
                                    "phone": "+256772123456",
                                    "source": "sms_start",
                                    "contact_id": 5512,
                                    "opted_in_at": "2026-10-07T08:30:00+00:00"
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "2XX": {
                        "description": "Received. Answer within 10 seconds; anything else is retried."
                    }
                }
            }
        },
        "payment.confirmed": {
            "post": {
                "summary": "payment.confirmed",
                "description": "A top-up was paid and credited. Verify `X-MobiReach-Signature` before trusting the body (see the docs).",
                "parameters": [
                    {
                        "name": "X-MobiReach-Signature",
                        "in": "header",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "t=1759741203,v1=5f2b\u2026"
                    },
                    {
                        "name": "X-MobiReach-Event",
                        "in": "header",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "payment.confirmed"
                    },
                    {
                        "name": "X-MobiReach-Event-Id",
                        "in": "header",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "evt_k2m9x8c7v6b5n4q3w2e1r0ty"
                    },
                    {
                        "name": "X-MobiReach-Delivery",
                        "in": "header",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "whd_901"
                    }
                ],
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/WebhookEvent"
                            },
                            "example": {
                                "id": "evt_k2m9x8c7v6b5n4q3w2e1r0ty",
                                "event": "payment.confirmed",
                                "created_at": "2026-10-06T09:00:03+00:00",
                                "data": {
                                    "reference": "MR-8KQ2F",
                                    "amount_ugx": 350000,
                                    "credits": 11666,
                                    "rate_ugx": 30,
                                    "method": "mtn_momo",
                                    "provider": "mtn_momo",
                                    "status": "paid",
                                    "livemode": true
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "2XX": {
                        "description": "Received. Answer within 10 seconds; anything else is retried."
                    }
                }
            }
        },
        "payment.failed": {
            "post": {
                "summary": "payment.failed",
                "description": "A top-up failed. Verify `X-MobiReach-Signature` before trusting the body (see the docs).",
                "parameters": [
                    {
                        "name": "X-MobiReach-Signature",
                        "in": "header",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "t=1759741203,v1=5f2b\u2026"
                    },
                    {
                        "name": "X-MobiReach-Event",
                        "in": "header",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "payment.failed"
                    },
                    {
                        "name": "X-MobiReach-Event-Id",
                        "in": "header",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "evt_k2m9x8c7v6b5n4q3w2e1r0ty"
                    },
                    {
                        "name": "X-MobiReach-Delivery",
                        "in": "header",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "whd_901"
                    }
                ],
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/WebhookEvent"
                            },
                            "example": {
                                "id": "evt_k2m9x8c7v6b5n4q3w2e1r0ty",
                                "event": "payment.failed",
                                "created_at": "2026-10-06T09:00:03+00:00",
                                "data": {
                                    "reference": "MR-9LA1C",
                                    "amount_ugx": 50000,
                                    "credits": 1428,
                                    "rate_ugx": 35,
                                    "method": "airtel_money",
                                    "provider": "airtel_money",
                                    "status": "failed",
                                    "livemode": true,
                                    "reason": "declined"
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "2XX": {
                        "description": "Received. Answer within 10 seconds; anything else is retried."
                    }
                }
            }
        },
        "payment.refunded": {
            "post": {
                "summary": "payment.refunded",
                "description": "A top-up was refunded. Verify `X-MobiReach-Signature` before trusting the body (see the docs).",
                "parameters": [
                    {
                        "name": "X-MobiReach-Signature",
                        "in": "header",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "t=1759741203,v1=5f2b\u2026"
                    },
                    {
                        "name": "X-MobiReach-Event",
                        "in": "header",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "payment.refunded"
                    },
                    {
                        "name": "X-MobiReach-Event-Id",
                        "in": "header",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "evt_k2m9x8c7v6b5n4q3w2e1r0ty"
                    },
                    {
                        "name": "X-MobiReach-Delivery",
                        "in": "header",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "whd_901"
                    }
                ],
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/WebhookEvent"
                            },
                            "example": {
                                "id": "evt_k2m9x8c7v6b5n4q3w2e1r0ty",
                                "event": "payment.refunded",
                                "created_at": "2026-10-06T09:00:03+00:00",
                                "data": {
                                    "reference": "MR-8KQ2F",
                                    "amount_ugx": 350000,
                                    "credits": 11666,
                                    "rate_ugx": 30,
                                    "method": "mtn_momo",
                                    "provider": "mtn_momo",
                                    "status": "refunded",
                                    "livemode": true,
                                    "refunded_amount_ugx": 350000,
                                    "refunded_credits": 11666
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "2XX": {
                        "description": "Received. Answer within 10 seconds; anything else is retried."
                    }
                }
            }
        },
        "campaign.completed": {
            "post": {
                "summary": "campaign.completed",
                "description": "Every message of a campaign has a final result. Verify `X-MobiReach-Signature` before trusting the body (see the docs).",
                "parameters": [
                    {
                        "name": "X-MobiReach-Signature",
                        "in": "header",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "t=1759741203,v1=5f2b\u2026"
                    },
                    {
                        "name": "X-MobiReach-Event",
                        "in": "header",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "campaign.completed"
                    },
                    {
                        "name": "X-MobiReach-Event-Id",
                        "in": "header",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "evt_k2m9x8c7v6b5n4q3w2e1r0ty"
                    },
                    {
                        "name": "X-MobiReach-Delivery",
                        "in": "header",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "whd_901"
                    }
                ],
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/WebhookEvent"
                            },
                            "example": {
                                "id": "evt_k2m9x8c7v6b5n4q3w2e1r0ty",
                                "event": "campaign.completed",
                                "created_at": "2026-10-06T09:00:03+00:00",
                                "data": {
                                    "batch_id": "batch_311",
                                    "campaign_id": 311,
                                    "name": "API term3-reopening",
                                    "kind": "api",
                                    "status": "sent",
                                    "recipients": 2,
                                    "delivered": 2,
                                    "failed": 0,
                                    "skipped": 0,
                                    "cost_credits": 2,
                                    "completed_at": "2026-10-06T09:00:05+00:00",
                                    "livemode": true
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "2XX": {
                        "description": "Received. Answer within 10 seconds; anything else is retried."
                    }
                }
            }
        },
        "wallet.low_balance": {
            "post": {
                "summary": "wallet.low_balance",
                "description": "Credits fell below the alert level. Verify `X-MobiReach-Signature` before trusting the body (see the docs).",
                "parameters": [
                    {
                        "name": "X-MobiReach-Signature",
                        "in": "header",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "t=1759741203,v1=5f2b\u2026"
                    },
                    {
                        "name": "X-MobiReach-Event",
                        "in": "header",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "wallet.low_balance"
                    },
                    {
                        "name": "X-MobiReach-Event-Id",
                        "in": "header",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "evt_k2m9x8c7v6b5n4q3w2e1r0ty"
                    },
                    {
                        "name": "X-MobiReach-Delivery",
                        "in": "header",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "example": "whd_901"
                    }
                ],
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/WebhookEvent"
                            },
                            "example": {
                                "id": "evt_k2m9x8c7v6b5n4q3w2e1r0ty",
                                "event": "wallet.low_balance",
                                "created_at": "2026-10-06T09:00:03+00:00",
                                "data": {
                                    "available_credits": 300,
                                    "balance_credits": 420,
                                    "reserved_credits": 120,
                                    "promo_credits": 0,
                                    "threshold_credits": 500,
                                    "scheduled_next_7_days": {
                                        "campaigns": 1,
                                        "credits": 120
                                    },
                                    "next_scheduled_campaign": {
                                        "batch_id": "batch_318",
                                        "name": "Term 3 fees reminder",
                                        "scheduled_at": "2026-10-09T06:00:00+00:00",
                                        "credits": 120
                                    },
                                    "alerted_at": "2026-10-06T07:00:00+00:00",
                                    "livemode": true
                                }
                            }
                        }
                    }
                },
                "responses": {
                    "2XX": {
                        "description": "Received. Answer within 10 seconds; anything else is retried."
                    }
                }
            }
        }
    },
    "components": {
        "securitySchemes": {
            "bearerAuth": {
                "type": "http",
                "scheme": "bearer",
                "bearerFormat": "mr_live_\u2026 / mr_test_\u2026",
                "description": "API key from Developers \u2192 API keys. Each operation needs one scope (x-scope): \n- `sms:send`: Send messages (POST /v1/messages, /v1/sms/send)\n- `reports:read`: Read messages and batches (GET /v1/messages, /v1/batches)\n- `otp:send`: Send one-time codes (POST /v1/otp/send)\n- `otp:verify`: Verify one-time codes (POST /v1/otp/verify)\n- `contacts:read`: Read contacts and groups\n- `contacts:write`: Create, change and delete contacts and groups\n- `balance:read`: Read the credit balance (and sender IDs)\n- `sender_ids:read`: List approved sender IDs\n- `webhooks:write`: Manage webhook endpoints (GET/POST/PATCH/DELETE /v1/webhooks)"
            }
        },
        "parameters": {
            "IdempotencyKey": {
                "name": "Idempotency-Key",
                "in": "header",
                "required": false,
                "schema": {
                    "type": "string",
                    "maxLength": 255
                },
                "description": "Makes retries safe: the same key and body replay the first response.",
                "example": "5b0f2c1e-8d6a-4c4e-9f6e-2a7b1d9c3e11"
            },
            "Limit": {
                "name": "limit",
                "in": "query",
                "schema": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 100,
                    "default": 50
                },
                "description": "Items per page (1-100)."
            },
            "Cursor": {
                "name": "cursor",
                "in": "query",
                "schema": {
                    "type": "string"
                },
                "description": "`next_cursor` of the previous page."
            }
        },
        "headers": {
            "X-RateLimit-Limit": {
                "schema": {
                    "type": "integer"
                },
                "description": "Requests allowed per minute."
            },
            "X-RateLimit-Remaining": {
                "schema": {
                    "type": "integer"
                },
                "description": "Requests left in the current minute."
            },
            "Retry-After": {
                "schema": {
                    "type": "integer"
                },
                "description": "Seconds to wait (429)."
            },
            "X-Request-Id": {
                "schema": {
                    "type": "string"
                },
                "description": "Id of this request; quote it to support."
            }
        },
        "schemas": {
            "Error": {
                "type": "object",
                "required": [
                    "error"
                ],
                "properties": {
                    "error": {
                        "type": "object",
                        "required": [
                            "type",
                            "code",
                            "message",
                            "request_id"
                        ],
                        "properties": {
                            "type": {
                                "type": "string",
                                "enum": [
                                    "invalid_request",
                                    "authentication_error",
                                    "insufficient_credits",
                                    "permission_denied",
                                    "not_found",
                                    "method_not_allowed",
                                    "conflict",
                                    "rate_limited",
                                    "api_error"
                                ]
                            },
                            "code": {
                                "type": "string",
                                "enum": [
                                    "validation_failed",
                                    "invalid_phone",
                                    "invalid_sender_id",
                                    "too_many_recipients",
                                    "no_valid_recipients",
                                    "content_blocked",
                                    "invalid_schedule",
                                    "unknown_group",
                                    "unknown_template",
                                    "otp_incorrect",
                                    "otp_expired",
                                    "otp_already_verified",
                                    "otp_max_attempts",
                                    "invalid_idempotency_key",
                                    "idempotency_mismatch",
                                    "invalid_webhook_url",
                                    "webhook_limit_reached",
                                    "missing_api_key",
                                    "invalid_api_key",
                                    "api_key_expired",
                                    "key_creator_removed",
                                    "missing_scope",
                                    "ip_not_allowed",
                                    "workspace_suspended",
                                    "insufficient_credits",
                                    "promo_credits_restricted",
                                    "resource_not_found",
                                    "route_not_found",
                                    "method_not_allowed",
                                    "idempotency_in_progress",
                                    "conflict",
                                    "group_in_use",
                                    "rate_limit_exceeded",
                                    "otp_cooldown",
                                    "otp_hourly_cap",
                                    "otp_locked",
                                    "otp_throttled",
                                    "otp_delivery_failed",
                                    "internal_error"
                                ]
                            },
                            "message": {
                                "type": "string"
                            },
                            "request_id": {
                                "type": "string"
                            },
                            "errors": {
                                "type": "object",
                                "additionalProperties": {
                                    "type": "array",
                                    "items": {
                                        "type": "string"
                                    }
                                },
                                "description": "validation_failed: messages per field"
                            },
                            "reason": {
                                "type": "string",
                                "description": "OTP throttles: cooldown, hourly_cap, locked, throttled"
                            }
                        }
                    }
                }
            },
            "WebhookEvent": {
                "type": "object",
                "required": [
                    "id",
                    "event",
                    "created_at",
                    "data"
                ],
                "properties": {
                    "id": {
                        "type": "string",
                        "description": "evt_\u2026: the same on every retry and endpoint; de-duplicate on it."
                    },
                    "event": {
                        "type": "string",
                        "enum": [
                            "message.sent",
                            "message.delivered",
                            "message.failed",
                            "message.inbound",
                            "contact.opted_out",
                            "contact.opted_in",
                            "payment.confirmed",
                            "payment.failed",
                            "payment.refunded",
                            "campaign.completed",
                            "wallet.low_balance",
                            "webhook.test"
                        ]
                    },
                    "created_at": {
                        "type": "string",
                        "format": "date-time"
                    },
                    "data": {
                        "type": "object"
                    }
                }
            }
        },
        "responses": {
            "Unauthorized": {
                "description": "missing_api_key, invalid_api_key, api_key_expired, key_creator_removed",
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/Error"
                        },
                        "example": {
                            "error": {
                                "type": "authentication_error",
                                "code": "invalid_api_key",
                                "message": "Invalid API key.",
                                "request_id": "req_7h2kq9x0m1n3b5v7c9z1"
                            }
                        }
                    }
                }
            },
            "Forbidden": {
                "description": "missing_scope, ip_not_allowed, workspace_suspended",
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/Error"
                        },
                        "example": {
                            "error": {
                                "type": "permission_denied",
                                "code": "missing_scope",
                                "message": "This API key is missing the required scope: \"sms:send\".",
                                "request_id": "req_7h2kq9x0m1n3b5v7c9z1"
                            }
                        }
                    }
                }
            },
            "NotFound": {
                "description": "resource_not_found",
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/Error"
                        },
                        "example": {
                            "error": {
                                "type": "not_found",
                                "code": "resource_not_found",
                                "message": "Message not found.",
                                "request_id": "req_7h2kq9x0m1n3b5v7c9z1"
                            }
                        }
                    }
                }
            },
            "RateLimited": {
                "description": "rate_limit_exceeded (see Retry-After)",
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/Error"
                        },
                        "example": {
                            "error": {
                                "type": "rate_limited",
                                "code": "rate_limit_exceeded",
                                "message": "Too many requests. Slow down and retry after the number of seconds in Retry-After.",
                                "request_id": "req_7h2kq9x0m1n3b5v7c9z1"
                            }
                        }
                    }
                },
                "headers": {
                    "Retry-After": {
                        "$ref": "#/components/headers/Retry-After"
                    }
                }
            },
            "IdempotencyInProgress": {
                "description": "idempotency_in_progress: a request with this Idempotency-Key is still being processed; retry shortly with the same key",
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/Error"
                        },
                        "example": {
                            "error": {
                                "type": "conflict",
                                "code": "idempotency_in_progress",
                                "message": "A request with this Idempotency-Key is still being processed. Retry shortly.",
                                "request_id": "req_7h2kq9x0m1n3b5v7c9z1"
                            }
                        }
                    }
                }
            },
            "InsufficientCredits": {
                "description": "insufficient_credits or promo_credits_restricted",
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/Error"
                        },
                        "example": {
                            "error": {
                                "type": "insufficient_credits",
                                "code": "insufficient_credits",
                                "message": "Not enough SMS credits for this request. Top up your wallet.",
                                "request_id": "req_7h2kq9x0m1n3b5v7c9z1"
                            }
                        }
                    }
                }
            },
            "ServerError": {
                "description": "internal_error",
                "content": {
                    "application/json": {
                        "schema": {
                            "$ref": "#/components/schemas/Error"
                        },
                        "example": {
                            "error": {
                                "type": "api_error",
                                "code": "internal_error",
                                "message": "Something went wrong on our side. Retry the request; quote the request_id if it keeps failing.",
                                "request_id": "req_7h2kq9x0m1n3b5v7c9z1"
                            }
                        }
                    }
                }
            }
        }
    }
}