{
  "openapi": "3.1.0",
  "info": {
    "title": "Web Rabbit API",
    "version": "2026.10.17",
    "summary": "Payments and Messaging for Ghana.",
    "description": "Machine-readable contract for the Web Rabbit public API. This document is the source of truth:\nif the docs prose and this file disagree, this file is correct.\n\n**Versioning.** `info.version` is a date stamp. Every field or behaviour change is listed, dated,\nin the API changelog at https://webrabbitmedia.com/docs/changelog. Additive changes (new optional\nrequest fields, new response fields, new webhook event types) ship without notice; anything that\nremoves or renames a field is announced in the changelog first.\n\n**One host.** Payments and Messaging both live on `https://api.webrabbitmedia.com`, share the same\nbearer auth, error envelope, `x-request-id` and rate-limit headers. The older Messaging URLs on\n`https://eydjkasswyygiycitnml.supabase.co/functions/v1` still work and are documented as `*Legacy`\noperations. `https://webrabbitmedia.com/functions/v1` is the marketing site and never served the API.\n\n**Nullability.** Response schemas set `required` and nullable types explicitly. A field marked\n`required` is always present; a field typed `[\"string\", \"null\"]` is present but may be null.",
    "contact": {
      "name": "Web Rabbit Media",
      "url": "https://webrabbitmedia.com/docs",
      "email": "support@webrabbitmedia.com"
    },
    "termsOfService": "https://webrabbitmedia.com/terms",
    "license": {
      "name": "Proprietary",
      "url": "https://webrabbitmedia.com/terms"
    }
  },
  "servers": [
    {
      "url": "https://api.webrabbitmedia.com",
      "description": "Payments and Messaging (production)"
    },
    {
      "url": "https://eydjkasswyygiycitnml.supabase.co/functions/v1",
      "description": "Messaging, legacy function URLs"
    }
  ],
  "tags": [
    {
      "name": "Payments",
      "description": "Collections, hosted checkout and transactions."
    },
    {
      "name": "Messaging",
      "description": "SMS campaigns, one-time passcodes and wallet balance."
    },
    {
      "name": "Bills",
      "description": "Airtime top-ups, data bundles and bill payments from your settled balance."
    }
  ],
  "externalDocs": {
    "url": "https://webrabbitmedia.com/docs",
    "description": "Guides and reference"
  },
  "security": [
    {
      "SecretKey": []
    }
  ],
  "paths": {
    "/v1/health": {
      "get": {
        "tags": [
          "Payments"
        ],
        "operationId": "health",
        "summary": "Health check",
        "description": "Unauthenticated liveness probe served at the edge.",
        "security": [],
        "responses": {
          "200": {
            "description": "Service is up.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Health"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              },
              "retry-after": {
                "$ref": "#/components/headers/retry-after"
              },
              "x-ratelimit-limit": {
                "$ref": "#/components/headers/x-ratelimit-limit"
              },
              "x-ratelimit-remaining": {
                "$ref": "#/components/headers/x-ratelimit-remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Rate limit exceeded",
                  "request_id": "6f3c7d1e-9a2b-4c8f-b5e0-2b1a7e9d0c33"
                }
              }
            }
          }
        }
      }
    },
    "/v1/me": {
      "get": {
        "tags": [
          "Payments"
        ],
        "operationId": "getMe",
        "summary": "Key and business status",
        "description": "Pre-flight check. Call it on boot to confirm which mode you are in, whether the business can transact live, and — in test mode — whether the rail behind your key is the simulator or a real provider.",
        "parameters": [
          {
            "$ref": "#/components/parameters/HttpReferer"
          },
          {
            "$ref": "#/components/parameters/AppTitle"
          }
        ],
        "responses": {
          "200": {
            "description": "Key context.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              },
              "x-wr-mode": {
                "$ref": "#/components/headers/x-wr-mode"
              },
              "x-wr-upstream": {
                "$ref": "#/components/headers/x-wr-upstream"
              },
              "x-wr-test-mode": {
                "$ref": "#/components/headers/x-wr-test-mode"
              },
              "x-wr-attribution-warning": {
                "$ref": "#/components/headers/x-wr-attribution-warning"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Me"
                },
                "example": {
                  "mode": "test",
                  "upstream": "simulator",
                  "test_mode": "simulated",
                  "test_moves_real_money": false,
                  "test_max_amount": 10,
                  "business_id": "3f9a2c1e-8d44-4a0b-9c2e-77b1f0a91234",
                  "business_name": "Many Express",
                  "business_status": "approved",
                  "live_ready": true,
                  "api_key_id": "9c4f1b22-1e35-4b7a-8f31-0d2b6a7c8e90",
                  "scopes": [
                    "read",
                    "write"
                  ],
                  "commission_bps": 1000,
                  "app": {
                    "site": "manyexpress.com",
                    "name": "Many Express",
                    "status": "approved"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid API key"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              },
              "retry-after": {
                "$ref": "#/components/headers/retry-after"
              },
              "x-ratelimit-limit": {
                "$ref": "#/components/headers/x-ratelimit-limit"
              },
              "x-ratelimit-remaining": {
                "$ref": "#/components/headers/x-ratelimit-remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Rate limit exceeded",
                  "request_id": "6f3c7d1e-9a2b-4c8f-b5e0-2b1a7e9d0c33"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "internal_error",
                  "request_id": "6f3c7d1e-9a2b-4c8f-b5e0-2b1a7e9d0c33"
                }
              }
            }
          }
        }
      }
    },
    "/v1/collect/momo": {
      "post": {
        "tags": [
          "Payments"
        ],
        "operationId": "collectMomo",
        "summary": "Charge a mobile money wallet",
        "description": "Resolves the wallet holder name first — no debit is attempted and no transaction is recorded if the lookup fails — then pushes an approval prompt to the customer. Most successful charges return `202` with `pending`; poll `/v1/transactions/{transaction_id}` or wait for the `collection.approved` webhook.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          },
          {
            "$ref": "#/components/parameters/HttpReferer"
          },
          {
            "$ref": "#/components/parameters/AppTitle"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CollectMomoRequest"
              },
              "example": {
                "amount": 10,
                "subscriber_number": "0240000000",
                "network": "MTN",
                "desc": "Trip WR-1042"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Charge resolved to a final failure. Nothing moved.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              },
              "x-wr-mode": {
                "$ref": "#/components/headers/x-wr-mode"
              },
              "x-wr-upstream": {
                "$ref": "#/components/headers/x-wr-upstream"
              },
              "x-wr-test-mode": {
                "$ref": "#/components/headers/x-wr-test-mode"
              },
              "x-wr-attribution-warning": {
                "$ref": "#/components/headers/x-wr-attribution-warning"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Charge"
                }
              }
            }
          },
          "201": {
            "description": "Charge approved synchronously. Fees are assessed.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              },
              "x-wr-mode": {
                "$ref": "#/components/headers/x-wr-mode"
              },
              "x-wr-upstream": {
                "$ref": "#/components/headers/x-wr-upstream"
              },
              "x-wr-test-mode": {
                "$ref": "#/components/headers/x-wr-test-mode"
              },
              "x-wr-attribution-warning": {
                "$ref": "#/components/headers/x-wr-attribution-warning"
              },
              "idempotent-replayed": {
                "$ref": "#/components/headers/idempotent-replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Charge"
                }
              }
            }
          },
          "202": {
            "description": "Prompt sent, awaiting the customer. `fee_amount` is 0 and `net_amount` equals `gross_amount` until the charge is approved.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              },
              "x-wr-mode": {
                "$ref": "#/components/headers/x-wr-mode"
              },
              "x-wr-upstream": {
                "$ref": "#/components/headers/x-wr-upstream"
              },
              "x-wr-test-mode": {
                "$ref": "#/components/headers/x-wr-test-mode"
              },
              "x-wr-attribution-warning": {
                "$ref": "#/components/headers/x-wr-attribution-warning"
              },
              "idempotent-replayed": {
                "$ref": "#/components/headers/idempotent-replayed"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Charge"
                },
                "example": {
                  "transaction_id": "521888807466",
                  "provider_transaction_id": null,
                  "status": "pending",
                  "code": null,
                  "reason": null,
                  "account_name": "KWAME MENSAH",
                  "subscriber_number": "0240000000",
                  "gross_amount": 10,
                  "fee_amount": 0,
                  "net_amount": 10,
                  "currency": "GHS"
                }
              }
            }
          },
          "400": {
            "description": "Invalid parameters.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "amount must be > 0",
                  "request_id": "6f3c7d1e-9a2b-4c8f-b5e0-2b1a7e9d0c33"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid API key"
                }
              }
            }
          },
          "403": {
            "description": "Key lacks permission, business not approved for live, or the calling site is blocked.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "domain_blocked",
                  "reason": "This site was blocked after review"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reused with a different request body.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "idempotency_conflict"
                }
              }
            }
          },
          "422": {
            "description": "Wallet could not be resolved. No transaction was created.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "account_not_found",
                  "code": "account_not_found",
                  "reason": "No wallet found for this number on MTN",
                  "subscriber_number": "0240000000"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              },
              "retry-after": {
                "$ref": "#/components/headers/retry-after"
              },
              "x-ratelimit-limit": {
                "$ref": "#/components/headers/x-ratelimit-limit"
              },
              "x-ratelimit-remaining": {
                "$ref": "#/components/headers/x-ratelimit-remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Rate limit exceeded",
                  "request_id": "6f3c7d1e-9a2b-4c8f-b5e0-2b1a7e9d0c33"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "internal_error",
                  "request_id": "6f3c7d1e-9a2b-4c8f-b5e0-2b1a7e9d0c33"
                }
              }
            }
          },
          "502": {
            "description": "Upstream provider unavailable. The transaction is recorded as failed.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "upstream_error",
                  "reason": "Upstream provider unavailable"
                }
              }
            }
          }
        }
      }
    },
    "/v1/collect/momo/otp": {
      "post": {
        "tags": [
          "Payments"
        ],
        "operationId": "submitMomoOtp",
        "summary": "Submit or resend a mobile money OTP",
        "description": "Some providers (OGateway) ask the customer for an SMS code before approving a charge. When a collection returns `pending` with reason code `otp_required`, submit the code here — or ask for a fresh one. The charge then continues; poll `/v1/transactions/{transaction_id}` or wait for the webhook.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "transaction_id"
                ],
                "properties": {
                  "transaction_id": {
                    "type": "string",
                    "description": "The 12-digit transaction id returned by `/v1/collect/momo`."
                  },
                  "otp": {
                    "type": "string",
                    "description": "The 4–8 digit code the customer received. Omit when `resend` is true."
                  },
                  "resend": {
                    "type": "boolean",
                    "description": "Set true to ask the provider to send a fresh code instead of submitting one."
                  }
                }
              },
              "example": {
                "transaction_id": "521888807466",
                "otp": "482913"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Code accepted (or re-sent); the charge is back to awaiting the customer.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              },
              "x-wr-mode": {
                "$ref": "#/components/headers/x-wr-mode"
              },
              "x-wr-upstream": {
                "$ref": "#/components/headers/x-wr-upstream"
              },
              "x-wr-test-mode": {
                "$ref": "#/components/headers/x-wr-test-mode"
              },
              "x-wr-attribution-warning": {
                "$ref": "#/components/headers/x-wr-attribution-warning"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Charge"
                }
              }
            }
          },
          "400": {
            "description": "Invalid parameters.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "amount must be > 0",
                  "request_id": "6f3c7d1e-9a2b-4c8f-b5e0-2b1a7e9d0c33"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid API key"
                }
              }
            }
          },
          "403": {
            "description": "Key lacks permission, business not approved for live, or the calling site is blocked.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "domain_blocked",
                  "reason": "This site was blocked after review"
                }
              }
            }
          },
          "404": {
            "description": "No pending OTP charge with this id for your business.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "transaction_not_found"
                }
              }
            }
          },
          "409": {
            "description": "This charge is not waiting for an OTP.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "otp_not_required"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              },
              "retry-after": {
                "$ref": "#/components/headers/retry-after"
              },
              "x-ratelimit-limit": {
                "$ref": "#/components/headers/x-ratelimit-limit"
              },
              "x-ratelimit-remaining": {
                "$ref": "#/components/headers/x-ratelimit-remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Rate limit exceeded",
                  "request_id": "6f3c7d1e-9a2b-4c8f-b5e0-2b1a7e9d0c33"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "internal_error",
                  "request_id": "6f3c7d1e-9a2b-4c8f-b5e0-2b1a7e9d0c33"
                }
              }
            }
          }
        }
      }
    },
    "/v1/checkout/session": {
      "post": {
        "tags": [
          "Payments"
        ],
        "operationId": "createCheckoutSession",
        "summary": "Create a hosted checkout session",
        "description": "Returns a branded checkout URL. The payment itself is created when the customer submits on that page — mobile money natively, cards through the provider’s PCI-compliant page.",
        "parameters": [
          {
            "$ref": "#/components/parameters/HttpReferer"
          },
          {
            "$ref": "#/components/parameters/AppTitle"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CheckoutSessionRequest"
              },
              "example": {
                "amount": 45,
                "desc": "Order MX-8817",
                "channel": "ANY",
                "customer_email": "ama@example.com",
                "success_url": "https://manyexpress.com/orders/8817",
                "metadata": {
                  "order_id": "8817"
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Session created.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              },
              "x-wr-mode": {
                "$ref": "#/components/headers/x-wr-mode"
              },
              "x-wr-upstream": {
                "$ref": "#/components/headers/x-wr-upstream"
              },
              "x-wr-test-mode": {
                "$ref": "#/components/headers/x-wr-test-mode"
              },
              "x-wr-attribution-warning": {
                "$ref": "#/components/headers/x-wr-attribution-warning"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckoutSession"
                }
              }
            }
          },
          "400": {
            "description": "Invalid parameters.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "amount must be > 0",
                  "request_id": "6f3c7d1e-9a2b-4c8f-b5e0-2b1a7e9d0c33"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid API key"
                }
              }
            }
          },
          "403": {
            "description": "Key lacks permission, business not approved for live, or the calling site is blocked.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "domain_blocked",
                  "reason": "This site was blocked after review"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              },
              "retry-after": {
                "$ref": "#/components/headers/retry-after"
              },
              "x-ratelimit-limit": {
                "$ref": "#/components/headers/x-ratelimit-limit"
              },
              "x-ratelimit-remaining": {
                "$ref": "#/components/headers/x-ratelimit-remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Rate limit exceeded",
                  "request_id": "6f3c7d1e-9a2b-4c8f-b5e0-2b1a7e9d0c33"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "internal_error",
                  "request_id": "6f3c7d1e-9a2b-4c8f-b5e0-2b1a7e9d0c33"
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Payments"
        ],
        "operationId": "getCheckoutSession",
        "summary": "Retrieve a hosted checkout session",
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Session token. Alias of `token`."
          },
          {
            "name": "token",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/HttpReferer"
          },
          {
            "$ref": "#/components/parameters/AppTitle"
          }
        ],
        "responses": {
          "200": {
            "description": "Session state, including the payment once the customer has acted.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              },
              "x-wr-mode": {
                "$ref": "#/components/headers/x-wr-mode"
              },
              "x-wr-upstream": {
                "$ref": "#/components/headers/x-wr-upstream"
              },
              "x-wr-test-mode": {
                "$ref": "#/components/headers/x-wr-test-mode"
              },
              "x-wr-attribution-warning": {
                "$ref": "#/components/headers/x-wr-attribution-warning"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckoutSession"
                }
              }
            }
          },
          "400": {
            "description": "Invalid parameters.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "amount must be > 0",
                  "request_id": "6f3c7d1e-9a2b-4c8f-b5e0-2b1a7e9d0c33"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid API key"
                }
              }
            }
          },
          "404": {
            "description": "No such session for this business.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Checkout session not found"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              },
              "retry-after": {
                "$ref": "#/components/headers/retry-after"
              },
              "x-ratelimit-limit": {
                "$ref": "#/components/headers/x-ratelimit-limit"
              },
              "x-ratelimit-remaining": {
                "$ref": "#/components/headers/x-ratelimit-remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Rate limit exceeded",
                  "request_id": "6f3c7d1e-9a2b-4c8f-b5e0-2b1a7e9d0c33"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "internal_error",
                  "request_id": "6f3c7d1e-9a2b-4c8f-b5e0-2b1a7e9d0c33"
                }
              }
            }
          }
        }
      }
    },
    "/v1/collect/card": {
      "post": {
        "tags": [
          "Payments"
        ],
        "operationId": "collectCard",
        "summary": "Create a hosted checkout session (alias)",
        "description": "Identical to `POST /v1/checkout/session`. Kept for older integrations.",
        "deprecated": true,
        "parameters": [
          {
            "$ref": "#/components/parameters/HttpReferer"
          },
          {
            "$ref": "#/components/parameters/AppTitle"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CheckoutSessionRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Session created.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              },
              "x-wr-mode": {
                "$ref": "#/components/headers/x-wr-mode"
              },
              "x-wr-upstream": {
                "$ref": "#/components/headers/x-wr-upstream"
              },
              "x-wr-test-mode": {
                "$ref": "#/components/headers/x-wr-test-mode"
              },
              "x-wr-attribution-warning": {
                "$ref": "#/components/headers/x-wr-attribution-warning"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckoutSession"
                }
              }
            }
          },
          "400": {
            "description": "Invalid parameters.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "amount must be > 0",
                  "request_id": "6f3c7d1e-9a2b-4c8f-b5e0-2b1a7e9d0c33"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid API key"
                }
              }
            }
          },
          "403": {
            "description": "Key lacks permission, business not approved for live, or the calling site is blocked.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "domain_blocked",
                  "reason": "This site was blocked after review"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              },
              "retry-after": {
                "$ref": "#/components/headers/retry-after"
              },
              "x-ratelimit-limit": {
                "$ref": "#/components/headers/x-ratelimit-limit"
              },
              "x-ratelimit-remaining": {
                "$ref": "#/components/headers/x-ratelimit-remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Rate limit exceeded",
                  "request_id": "6f3c7d1e-9a2b-4c8f-b5e0-2b1a7e9d0c33"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "internal_error",
                  "request_id": "6f3c7d1e-9a2b-4c8f-b5e0-2b1a7e9d0c33"
                }
              }
            }
          }
        }
      }
    },
    "/v1/disbursements": {
      "get": {
        "tags": [
          "Payments"
        ],
        "operationId": "listDisbursements",
        "summary": "List disbursements",
        "description": "Your disbursement batches in the key’s mode, newest first.",
        "parameters": [
          {
            "$ref": "#/components/parameters/HttpReferer"
          },
          {
            "$ref": "#/components/parameters/AppTitle"
          }
        ],
        "responses": {
          "200": {
            "description": "Disbursements.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              },
              "x-wr-mode": {
                "$ref": "#/components/headers/x-wr-mode"
              },
              "x-wr-upstream": {
                "$ref": "#/components/headers/x-wr-upstream"
              },
              "x-wr-test-mode": {
                "$ref": "#/components/headers/x-wr-test-mode"
              },
              "x-wr-attribution-warning": {
                "$ref": "#/components/headers/x-wr-attribution-warning"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DisbursementList"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid API key"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              },
              "retry-after": {
                "$ref": "#/components/headers/retry-after"
              },
              "x-ratelimit-limit": {
                "$ref": "#/components/headers/x-ratelimit-limit"
              },
              "x-ratelimit-remaining": {
                "$ref": "#/components/headers/x-ratelimit-remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Rate limit exceeded",
                  "request_id": "6f3c7d1e-9a2b-4c8f-b5e0-2b1a7e9d0c33"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Payments"
        ],
        "operationId": "createDisbursement",
        "summary": "Create a disbursement",
        "description": "Pays a list of recipients out of your settled balance. Disbursements are switched on per business. A batch is never released at creation: it settles on your tier clock (individual T+7, registered business T+3, Powered by Web Rabbit T+1) and, except on the Powered tier, waits for human review. Requires a write key.",
        "parameters": [
          {
            "$ref": "#/components/parameters/HttpReferer"
          },
          {
            "$ref": "#/components/parameters/AppTitle"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DisbursementCreateRequest"
              },
              "example": {
                "channel": "momo",
                "reference": "friday-riders",
                "recipients": [
                  {
                    "reference": "driver_8814",
                    "name": "Kwame Mensah",
                    "msisdn": "0244000000",
                    "network": "MTN",
                    "amount": 120
                  },
                  {
                    "reference": "driver_8815",
                    "name": "Ama Boateng",
                    "msisdn": "0559876543",
                    "network": "TELECEL",
                    "amount_minor": 8500
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              },
              "x-wr-mode": {
                "$ref": "#/components/headers/x-wr-mode"
              },
              "x-wr-upstream": {
                "$ref": "#/components/headers/x-wr-upstream"
              },
              "x-wr-test-mode": {
                "$ref": "#/components/headers/x-wr-test-mode"
              },
              "x-wr-attribution-warning": {
                "$ref": "#/components/headers/x-wr-attribution-warning"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Disbursement"
                }
              }
            }
          },
          "400": {
            "description": "Invalid parameters.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "amount must be > 0",
                  "request_id": "6f3c7d1e-9a2b-4c8f-b5e0-2b1a7e9d0c33"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid API key"
                }
              }
            }
          },
          "403": {
            "description": "Key lacks permission, business not approved for live, or the calling site is blocked.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "domain_blocked",
                  "reason": "This site was blocked after review"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              },
              "retry-after": {
                "$ref": "#/components/headers/retry-after"
              },
              "x-ratelimit-limit": {
                "$ref": "#/components/headers/x-ratelimit-limit"
              },
              "x-ratelimit-remaining": {
                "$ref": "#/components/headers/x-ratelimit-remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Rate limit exceeded",
                  "request_id": "6f3c7d1e-9a2b-4c8f-b5e0-2b1a7e9d0c33"
                }
              }
            }
          }
        }
      }
    },
    "/v1/disbursements/{id}": {
      "get": {
        "tags": [
          "Payments"
        ],
        "operationId": "getDisbursement",
        "summary": "Retrieve a disbursement",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "$ref": "#/components/parameters/HttpReferer"
          },
          {
            "$ref": "#/components/parameters/AppTitle"
          }
        ],
        "responses": {
          "200": {
            "description": "Disbursement.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              },
              "x-wr-mode": {
                "$ref": "#/components/headers/x-wr-mode"
              },
              "x-wr-upstream": {
                "$ref": "#/components/headers/x-wr-upstream"
              },
              "x-wr-test-mode": {
                "$ref": "#/components/headers/x-wr-test-mode"
              },
              "x-wr-attribution-warning": {
                "$ref": "#/components/headers/x-wr-attribution-warning"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Disbursement"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid API key"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              },
              "retry-after": {
                "$ref": "#/components/headers/retry-after"
              },
              "x-ratelimit-limit": {
                "$ref": "#/components/headers/x-ratelimit-limit"
              },
              "x-ratelimit-remaining": {
                "$ref": "#/components/headers/x-ratelimit-remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Rate limit exceeded",
                  "request_id": "6f3c7d1e-9a2b-4c8f-b5e0-2b1a7e9d0c33"
                }
              }
            }
          }
        }
      }
    },
    "/v1/disbursements/{id}/cancel": {
      "post": {
        "tags": [
          "Payments"
        ],
        "operationId": "cancelDisbursement",
        "summary": "Cancel a disbursement",
        "description": "Cancels a batch that has not been released yet. Requires a write key.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "$ref": "#/components/parameters/HttpReferer"
          },
          {
            "$ref": "#/components/parameters/AppTitle"
          }
        ],
        "responses": {
          "200": {
            "description": "Cancelled.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              },
              "x-wr-mode": {
                "$ref": "#/components/headers/x-wr-mode"
              },
              "x-wr-upstream": {
                "$ref": "#/components/headers/x-wr-upstream"
              },
              "x-wr-test-mode": {
                "$ref": "#/components/headers/x-wr-test-mode"
              },
              "x-wr-attribution-warning": {
                "$ref": "#/components/headers/x-wr-attribution-warning"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Disbursement"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid API key"
                }
              }
            }
          },
          "403": {
            "description": "Key lacks permission, business not approved for live, or the calling site is blocked.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "domain_blocked",
                  "reason": "This site was blocked after review"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              },
              "retry-after": {
                "$ref": "#/components/headers/retry-after"
              },
              "x-ratelimit-limit": {
                "$ref": "#/components/headers/x-ratelimit-limit"
              },
              "x-ratelimit-remaining": {
                "$ref": "#/components/headers/x-ratelimit-remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Rate limit exceeded",
                  "request_id": "6f3c7d1e-9a2b-4c8f-b5e0-2b1a7e9d0c33"
                }
              }
            }
          }
        }
      }
    },
    "/v1/sub-accounts": {
      "get": {
        "tags": [
          "Payments"
        ],
        "operationId": "listSubAccounts",
        "summary": "List sub-accounts",
        "description": "Every split-payment recipient in the key’s mode, newest first, with live balances.",
        "parameters": [
          {
            "$ref": "#/components/parameters/HttpReferer"
          },
          {
            "$ref": "#/components/parameters/AppTitle"
          }
        ],
        "responses": {
          "200": {
            "description": "Sub-accounts.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              },
              "x-wr-mode": {
                "$ref": "#/components/headers/x-wr-mode"
              },
              "x-wr-upstream": {
                "$ref": "#/components/headers/x-wr-upstream"
              },
              "x-wr-test-mode": {
                "$ref": "#/components/headers/x-wr-test-mode"
              },
              "x-wr-attribution-warning": {
                "$ref": "#/components/headers/x-wr-attribution-warning"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubAccountList"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid API key"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              },
              "retry-after": {
                "$ref": "#/components/headers/retry-after"
              },
              "x-ratelimit-limit": {
                "$ref": "#/components/headers/x-ratelimit-limit"
              },
              "x-ratelimit-remaining": {
                "$ref": "#/components/headers/x-ratelimit-remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Rate limit exceeded",
                  "request_id": "6f3c7d1e-9a2b-4c8f-b5e0-2b1a7e9d0c33"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Payments"
        ],
        "operationId": "createSubAccount",
        "summary": "Create a sub-account",
        "description": "Registers a settlement destination — a driver, courier or vendor — that collections can be split to. Requires a write key.",
        "parameters": [
          {
            "$ref": "#/components/parameters/HttpReferer"
          },
          {
            "$ref": "#/components/parameters/AppTitle"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SubAccountCreateRequest"
              },
              "example": {
                "name": "Kwame Mensah",
                "reference": "driver_8814",
                "settlement_method": "momo",
                "account_number": "0244000000",
                "network": "MTN"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              },
              "x-wr-mode": {
                "$ref": "#/components/headers/x-wr-mode"
              },
              "x-wr-upstream": {
                "$ref": "#/components/headers/x-wr-upstream"
              },
              "x-wr-test-mode": {
                "$ref": "#/components/headers/x-wr-test-mode"
              },
              "x-wr-attribution-warning": {
                "$ref": "#/components/headers/x-wr-attribution-warning"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubAccount"
                }
              }
            }
          },
          "400": {
            "description": "Invalid parameters.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "amount must be > 0",
                  "request_id": "6f3c7d1e-9a2b-4c8f-b5e0-2b1a7e9d0c33"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid API key"
                }
              }
            }
          },
          "403": {
            "description": "Key lacks permission, business not approved for live, or the calling site is blocked.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "domain_blocked",
                  "reason": "This site was blocked after review"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              },
              "retry-after": {
                "$ref": "#/components/headers/retry-after"
              },
              "x-ratelimit-limit": {
                "$ref": "#/components/headers/x-ratelimit-limit"
              },
              "x-ratelimit-remaining": {
                "$ref": "#/components/headers/x-ratelimit-remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Rate limit exceeded",
                  "request_id": "6f3c7d1e-9a2b-4c8f-b5e0-2b1a7e9d0c33"
                }
              }
            }
          }
        }
      }
    },
    "/v1/sub-accounts/{reference}": {
      "get": {
        "tags": [
          "Payments"
        ],
        "operationId": "getSubAccount",
        "summary": "Retrieve a sub-account",
        "parameters": [
          {
            "name": "reference",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Sub-account reference or id."
          },
          {
            "$ref": "#/components/parameters/HttpReferer"
          },
          {
            "$ref": "#/components/parameters/AppTitle"
          }
        ],
        "responses": {
          "200": {
            "description": "Sub-account.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              },
              "x-wr-mode": {
                "$ref": "#/components/headers/x-wr-mode"
              },
              "x-wr-upstream": {
                "$ref": "#/components/headers/x-wr-upstream"
              },
              "x-wr-test-mode": {
                "$ref": "#/components/headers/x-wr-test-mode"
              },
              "x-wr-attribution-warning": {
                "$ref": "#/components/headers/x-wr-attribution-warning"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubAccount"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid API key"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              },
              "retry-after": {
                "$ref": "#/components/headers/retry-after"
              },
              "x-ratelimit-limit": {
                "$ref": "#/components/headers/x-ratelimit-limit"
              },
              "x-ratelimit-remaining": {
                "$ref": "#/components/headers/x-ratelimit-remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Rate limit exceeded",
                  "request_id": "6f3c7d1e-9a2b-4c8f-b5e0-2b1a7e9d0c33"
                }
              }
            }
          }
        }
      },
      "patch": {
        "tags": [
          "Payments"
        ],
        "operationId": "updateSubAccount",
        "summary": "Update a sub-account",
        "parameters": [
          {
            "name": "reference",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Sub-account reference or id."
          },
          {
            "$ref": "#/components/parameters/HttpReferer"
          },
          {
            "$ref": "#/components/parameters/AppTitle"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SubAccountUpdateRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              },
              "x-wr-mode": {
                "$ref": "#/components/headers/x-wr-mode"
              },
              "x-wr-upstream": {
                "$ref": "#/components/headers/x-wr-upstream"
              },
              "x-wr-test-mode": {
                "$ref": "#/components/headers/x-wr-test-mode"
              },
              "x-wr-attribution-warning": {
                "$ref": "#/components/headers/x-wr-attribution-warning"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubAccount"
                }
              }
            }
          },
          "400": {
            "description": "Invalid parameters.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "amount must be > 0",
                  "request_id": "6f3c7d1e-9a2b-4c8f-b5e0-2b1a7e9d0c33"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid API key"
                }
              }
            }
          },
          "403": {
            "description": "Key lacks permission, business not approved for live, or the calling site is blocked.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "domain_blocked",
                  "reason": "This site was blocked after review"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              },
              "retry-after": {
                "$ref": "#/components/headers/retry-after"
              },
              "x-ratelimit-limit": {
                "$ref": "#/components/headers/x-ratelimit-limit"
              },
              "x-ratelimit-remaining": {
                "$ref": "#/components/headers/x-ratelimit-remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Rate limit exceeded",
                  "request_id": "6f3c7d1e-9a2b-4c8f-b5e0-2b1a7e9d0c33"
                }
              }
            }
          }
        }
      }
    },
    "/v1/sub-accounts/{reference}/ledger": {
      "get": {
        "tags": [
          "Payments"
        ],
        "operationId": "listSubAccountLedger",
        "summary": "List sub-account ledger entries",
        "parameters": [
          {
            "name": "reference",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Sub-account reference or id."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          },
          {
            "$ref": "#/components/parameters/HttpReferer"
          },
          {
            "$ref": "#/components/parameters/AppTitle"
          }
        ],
        "responses": {
          "200": {
            "description": "Ledger entries, newest first.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              },
              "x-wr-mode": {
                "$ref": "#/components/headers/x-wr-mode"
              },
              "x-wr-upstream": {
                "$ref": "#/components/headers/x-wr-upstream"
              },
              "x-wr-test-mode": {
                "$ref": "#/components/headers/x-wr-test-mode"
              },
              "x-wr-attribution-warning": {
                "$ref": "#/components/headers/x-wr-attribution-warning"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubAccountLedger"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid API key"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              },
              "retry-after": {
                "$ref": "#/components/headers/retry-after"
              },
              "x-ratelimit-limit": {
                "$ref": "#/components/headers/x-ratelimit-limit"
              },
              "x-ratelimit-remaining": {
                "$ref": "#/components/headers/x-ratelimit-remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Rate limit exceeded",
                  "request_id": "6f3c7d1e-9a2b-4c8f-b5e0-2b1a7e9d0c33"
                }
              }
            }
          }
        }
      }
    },
    "/v1/splits/preview": {
      "post": {
        "tags": [
          "Payments"
        ],
        "operationId": "previewSplit",
        "summary": "Preview a split",
        "description": "Runs the same integer allocation used at capture, without creating anything.",
        "parameters": [
          {
            "$ref": "#/components/parameters/HttpReferer"
          },
          {
            "$ref": "#/components/parameters/AppTitle"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SplitPreviewRequest"
              },
              "example": {
                "amount_minor": 5000,
                "split": {
                  "fee_bearer": "merchant",
                  "recipients": [
                    {
                      "sub_account": "driver_8814",
                      "percentage": 85
                    }
                  ]
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Allocation.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              },
              "x-wr-mode": {
                "$ref": "#/components/headers/x-wr-mode"
              },
              "x-wr-upstream": {
                "$ref": "#/components/headers/x-wr-upstream"
              },
              "x-wr-test-mode": {
                "$ref": "#/components/headers/x-wr-test-mode"
              },
              "x-wr-attribution-warning": {
                "$ref": "#/components/headers/x-wr-attribution-warning"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SplitPreview"
                }
              }
            }
          },
          "400": {
            "description": "Invalid parameters.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "amount must be > 0",
                  "request_id": "6f3c7d1e-9a2b-4c8f-b5e0-2b1a7e9d0c33"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid API key"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              },
              "retry-after": {
                "$ref": "#/components/headers/retry-after"
              },
              "x-ratelimit-limit": {
                "$ref": "#/components/headers/x-ratelimit-limit"
              },
              "x-ratelimit-remaining": {
                "$ref": "#/components/headers/x-ratelimit-remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Rate limit exceeded",
                  "request_id": "6f3c7d1e-9a2b-4c8f-b5e0-2b1a7e9d0c33"
                }
              }
            }
          }
        }
      }
    },
    "/v1/transactions": {
      "get": {
        "tags": [
          "Payments"
        ],
        "operationId": "listTransactions",
        "summary": "List transactions",
        "description": "Newest first, scoped to the key’s business and mode. Cursor pagination on `created_at`.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Pass `next_cursor` from the previous page."
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/TransactionStatus"
            }
          },
          {
            "name": "channel",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "momo",
                "card"
              ]
            }
          },
          {
            "name": "type",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "collection",
                "payout"
              ]
            }
          },
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "idempotency_key",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Look up the transaction created under a given Idempotency-Key. Returns at most one item."
          },
          {
            "$ref": "#/components/parameters/HttpReferer"
          },
          {
            "$ref": "#/components/parameters/AppTitle"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of transactions.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              },
              "x-wr-mode": {
                "$ref": "#/components/headers/x-wr-mode"
              },
              "x-wr-upstream": {
                "$ref": "#/components/headers/x-wr-upstream"
              },
              "x-wr-test-mode": {
                "$ref": "#/components/headers/x-wr-test-mode"
              },
              "x-wr-attribution-warning": {
                "$ref": "#/components/headers/x-wr-attribution-warning"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TransactionList"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid API key"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              },
              "retry-after": {
                "$ref": "#/components/headers/retry-after"
              },
              "x-ratelimit-limit": {
                "$ref": "#/components/headers/x-ratelimit-limit"
              },
              "x-ratelimit-remaining": {
                "$ref": "#/components/headers/x-ratelimit-remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Rate limit exceeded",
                  "request_id": "6f3c7d1e-9a2b-4c8f-b5e0-2b1a7e9d0c33"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "internal_error",
                  "request_id": "6f3c7d1e-9a2b-4c8f-b5e0-2b1a7e9d0c33"
                }
              }
            }
          }
        }
      }
    },
    "/v1/transactions/{transaction_id}": {
      "get": {
        "tags": [
          "Payments"
        ],
        "operationId": "getTransaction",
        "summary": "Retrieve a transaction",
        "description": "Pending and approved rows are re-checked against the provider before we answer, so polling is never blocked waiting on a callback. Unknown ids return 404 rather than a synthetic verdict.",
        "parameters": [
          {
            "name": "transaction_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^\\d{12}$"
            }
          },
          {
            "$ref": "#/components/parameters/HttpReferer"
          },
          {
            "$ref": "#/components/parameters/AppTitle"
          }
        ],
        "responses": {
          "200": {
            "description": "Current state of the transaction.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              },
              "x-wr-mode": {
                "$ref": "#/components/headers/x-wr-mode"
              },
              "x-wr-upstream": {
                "$ref": "#/components/headers/x-wr-upstream"
              },
              "x-wr-test-mode": {
                "$ref": "#/components/headers/x-wr-test-mode"
              },
              "x-wr-attribution-warning": {
                "$ref": "#/components/headers/x-wr-attribution-warning"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TransactionStatusResponse"
                }
              }
            }
          },
          "400": {
            "description": "Invalid parameters.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "amount must be > 0",
                  "request_id": "6f3c7d1e-9a2b-4c8f-b5e0-2b1a7e9d0c33"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid API key"
                }
              }
            }
          },
          "404": {
            "description": "No transaction with that id for this business and mode.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "transaction_not_found",
                  "transaction_id": "521888807466"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              },
              "retry-after": {
                "$ref": "#/components/headers/retry-after"
              },
              "x-ratelimit-limit": {
                "$ref": "#/components/headers/x-ratelimit-limit"
              },
              "x-ratelimit-remaining": {
                "$ref": "#/components/headers/x-ratelimit-remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Rate limit exceeded",
                  "request_id": "6f3c7d1e-9a2b-4c8f-b5e0-2b1a7e9d0c33"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "internal_error",
                  "request_id": "6f3c7d1e-9a2b-4c8f-b5e0-2b1a7e9d0c33"
                }
              }
            }
          }
        }
      }
    },
    "/v1/payout/momo": {
      "post": {
        "tags": [
          "Payments"
        ],
        "operationId": "payoutMomo",
        "summary": "Payouts are not available on the public API",
        "description": "Always returns 501. Withdrawals are initiated from the merchant dashboard.",
        "responses": {
          "501": {
            "description": "Not implemented.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "provider_unsupported: payouts are initiated from the merchant dashboard, not the public API"
                }
              }
            }
          }
        }
      }
    },
    "/v1/payout/bank": {
      "post": {
        "tags": [
          "Payments"
        ],
        "operationId": "payoutBank",
        "summary": "Payouts are not available on the public API",
        "description": "Always returns 501. Withdrawals are initiated from the merchant dashboard.",
        "responses": {
          "501": {
            "description": "Not implemented.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "provider_unsupported: payouts are initiated from the merchant dashboard, not the public API"
                }
              }
            }
          }
        }
      }
    },
    "/v1/bills": {
      "post": {
        "tags": [
          "Bills"
        ],
        "operationId": "bills",
        "summary": "Airtime, data bundles and bill payments",
        "description": "One action-based endpoint. `catalog` lists billers and networks, `bundles` lists data bundles for a network, `lookup` resolves an account name, `purchase` spends your settled balance, `status` refreshes one purchase and `list` pages through history. A purchase returns 200 when it is accepted, not when it is delivered — poll `status` or subscribe to `bill.completed` / `bill.failed`. Failed purchases are refunded to your balance in full. Send an `Idempotency-Key` to make retries safe.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "action"
                ],
                "properties": {
                  "action": {
                    "type": "string",
                    "enum": [
                      "catalog",
                      "bundles",
                      "lookup",
                      "purchase",
                      "status",
                      "list"
                    ],
                    "default": "list"
                  },
                  "kind": {
                    "type": "string",
                    "enum": [
                      "airtime",
                      "data",
                      "bill"
                    ],
                    "description": "Required for action=purchase."
                  },
                  "amount": {
                    "type": "number",
                    "description": "Cedis. Required for action=purchase."
                  },
                  "account_number": {
                    "type": "string",
                    "description": "Mobile number for airtime and data; bill account number for a bill payment."
                  },
                  "account_name": {
                    "type": "string"
                  },
                  "phone_number": {
                    "type": "string",
                    "description": "Contact number for bill payments when it differs from the account."
                  },
                  "network": {
                    "type": "string",
                    "enum": [
                      "MTN",
                      "AT",
                      "TELECEL"
                    ],
                    "description": "Required for airtime, data and action=bundles. `telco` is accepted as an alias."
                  },
                  "biller_slug": {
                    "type": "string",
                    "description": "Required for kind=bill and action=lookup."
                  },
                  "biller_name": {
                    "type": "string"
                  },
                  "data_id": {
                    "type": "string",
                    "description": "Bundle id from action=bundles. Required for kind=data."
                  },
                  "bundle_name": {
                    "type": "string"
                  },
                  "narration": {
                    "type": "string",
                    "maxLength": 100
                  },
                  "id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "For action=status."
                  },
                  "reference": {
                    "type": "string",
                    "description": "For action=status, as an alternative to id."
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "pending",
                      "approved",
                      "failed"
                    ],
                    "description": "Filter for action=list."
                  },
                  "page": {
                    "type": "integer",
                    "minimum": 1,
                    "default": 1
                  },
                  "limit": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 100,
                    "default": 25
                  }
                }
              },
              "example": {
                "action": "purchase",
                "kind": "airtime",
                "network": "MTN",
                "account_number": "0240000000",
                "amount": 5
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Accepted, or the requested catalogue / status / history payload.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "bill": {
                      "$ref": "#/components/schemas/BillPurchase"
                    },
                    "bills": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/BillPurchase"
                      }
                    },
                    "total": {
                      "type": "integer"
                    },
                    "page": {
                      "type": "integer"
                    },
                    "limit": {
                      "type": "integer"
                    },
                    "telcos": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "billers": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "additionalProperties": true
                      }
                    },
                    "bundles": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "additionalProperties": true
                      }
                    },
                    "ok": {
                      "type": "boolean",
                      "description": "action=lookup only."
                    },
                    "account_name": {
                      "type": "string",
                      "description": "action=lookup only."
                    }
                  }
                },
                "example": {
                  "bill": {
                    "id": "9f2a5f0e-6b51-4a4b-9a4f-2f0c6e1b7d30",
                    "reference": "air_8FQ2K7X1",
                    "kind": "airtime",
                    "status": "pending",
                    "amount": 5,
                    "fee": 0,
                    "total": 5,
                    "currency": "GHS",
                    "account_number": "0240000000",
                    "mode": "live",
                    "created_at": "2026-09-19T08:12:04.000Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation failure.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "invalid_request",
                  "message": "kind must be airtime, data or bill"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid API key"
                }
              }
            }
          },
          "402": {
            "description": "Not enough settled balance.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "insufficient_balance",
                  "message": "Not enough settled balance for this purchase"
                }
              }
            }
          },
          "403": {
            "description": "Read-only key, or the business is not approved for live mode.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "insufficient_scope",
                  "message": "This API key is read-only"
                }
              }
            }
          },
          "404": {
            "description": "Unknown purchase for action=status.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "not_found",
                  "message": "Bill purchase not found"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reused with a different body, or still in flight.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "idempotency_conflict",
                  "message": "Idempotency-Key reused with a different request body"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              },
              "retry-after": {
                "$ref": "#/components/headers/retry-after"
              },
              "x-ratelimit-limit": {
                "$ref": "#/components/headers/x-ratelimit-limit"
              },
              "x-ratelimit-remaining": {
                "$ref": "#/components/headers/x-ratelimit-remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Rate limit exceeded",
                  "request_id": "6f3c7d1e-9a2b-4c8f-b5e0-2b1a7e9d0c33"
                }
              }
            }
          },
          "502": {
            "description": "The provider rejected the purchase. Your balance is refunded.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "provider_error",
                  "message": "Provider rejected the purchase"
                }
              }
            }
          }
        }
      }
    },
    "/v1/messaging/sms": {
      "post": {
        "tags": [
          "Messaging"
        ],
        "operationId": "sendSms",
        "summary": "Send an SMS campaign",
        "description": "Debits your messaging wallet, submits to the network, and refunds credits for any recipient the network rejects. Test mode is fully simulated. `business_id` and `mode` are derived from the API key when omitted. Send an `Idempotency-Key` to make retries safe.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SmsSendRequest"
              },
              "example": {
                "sender": "WRB",
                "message": "Your driver is 2 minutes away.",
                "recipients": [
                  "0240000000"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Campaign accepted.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SmsSendResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation failure.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "invalid_request",
                  "message": "Add at least one valid recipient"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid API key"
                }
              }
            }
          },
          "402": {
            "description": "Not enough messaging credits.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "insufficient_credits",
                  "message": "Not enough messaging credits. Top up your wallet."
                }
              }
            }
          },
          "403": {
            "description": "Read-only key, wrong workspace, or key belongs to a different product.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "insufficient_scope",
                  "message": "This API key is read-only"
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reused with a different body, or still in flight.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "idempotency_conflict",
                  "message": "Idempotency-Key reused with a different request body"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              },
              "retry-after": {
                "$ref": "#/components/headers/retry-after"
              },
              "x-ratelimit-limit": {
                "$ref": "#/components/headers/x-ratelimit-limit"
              },
              "x-ratelimit-remaining": {
                "$ref": "#/components/headers/x-ratelimit-remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Rate limit exceeded",
                  "request_id": "6f3c7d1e-9a2b-4c8f-b5e0-2b1a7e9d0c33"
                }
              }
            }
          },
          "502": {
            "description": "The network rejected the campaign. Credits are refunded.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "provider_error",
                  "message": "Provider rejected the campaign"
                }
              }
            }
          }
        }
      }
    },
    "/v1/messaging/status/{campaign_id}": {
      "get": {
        "tags": [
          "Messaging"
        ],
        "operationId": "smsStatus",
        "summary": "Delivery report for a campaign",
        "description": "Pulls fresh delivery reports from the network and reconciles the campaign. Status reads have their own, higher rate-limit budget.",
        "parameters": [
          {
            "name": "campaign_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The `campaign_id` returned by the send call."
          }
        ],
        "responses": {
          "200": {
            "description": "Reconciled.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SmsStatusResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid API key"
                }
              }
            }
          },
          "404": {
            "description": "Campaign not found.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "not_found",
                  "message": "Campaign not found"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              },
              "retry-after": {
                "$ref": "#/components/headers/retry-after"
              },
              "x-ratelimit-limit": {
                "$ref": "#/components/headers/x-ratelimit-limit"
              },
              "x-ratelimit-remaining": {
                "$ref": "#/components/headers/x-ratelimit-remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Rate limit exceeded",
                  "request_id": "6f3c7d1e-9a2b-4c8f-b5e0-2b1a7e9d0c33"
                }
              }
            }
          }
        }
      }
    },
    "/v1/messaging/otp": {
      "post": {
        "tags": [
          "Messaging"
        ],
        "operationId": "otp",
        "summary": "Send a one-time passcode",
        "description": "Issues a code, hashes it before storage, sends it, and expires it for you — the plaintext code never leaves the platform except over SMS (test mode returns it). Length, wording and expiry come from the per-call fields, then the saved preset for `purpose`, then your workspace defaults. Send `action: \"verify\"` here, or POST /v1/messaging/otp/verify, to check a code.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/OtpSendRequest"
                  },
                  {
                    "$ref": "#/components/schemas/OtpVerifyRequest"
                  }
                ]
              },
              "example": {
                "phone": "0240000000",
                "purpose": "login",
                "code_length": 6
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Code issued, or code verified.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/OtpSendResponse"
                    },
                    {
                      "$ref": "#/components/schemas/OtpVerifyResponse"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid number or template, wrong code, or expired code.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OtpError"
                },
                "example": {
                  "error": "invalid_code",
                  "message": "That code is not correct",
                  "retryable": true,
                  "attempts_remaining": 3
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid API key"
                }
              }
            }
          },
          "402": {
            "description": "Not enough messaging credits.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "insufficient_credits",
                  "message": "Top up your wallet."
                }
              }
            }
          },
          "404": {
            "description": "Unknown request_id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OtpError"
                },
                "example": {
                  "error": "not_found",
                  "message": "Unknown request_id",
                  "retryable": false
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reused with a different body.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "idempotency_conflict",
                  "message": "Idempotency-Key reused with a different request body"
                }
              }
            }
          },
          "429": {
            "description": "Five failed attempts on this code, or rate limited.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OtpError"
                },
                "example": {
                  "error": "too_many_attempts",
                  "message": "Too many attempts for this code. Send a new one.",
                  "retryable": false,
                  "attempts_remaining": 0
                }
              }
            }
          },
          "502": {
            "description": "The network rejected the OTP.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "provider_error",
                  "message": "Provider rejected the OTP"
                }
              }
            }
          }
        }
      }
    },
    "/v1/messaging/otp/verify": {
      "post": {
        "tags": [
          "Messaging"
        ],
        "operationId": "verifyOtp",
        "summary": "Verify a one-time passcode",
        "description": "Sugar for `POST /v1/messaging/otp` with `action: \"verify\"`. Verifying an already-verified code returns 200 with `already_verified: true`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OtpVerifyRequest"
              },
              "example": {
                "request_id": "9c71f0d2-1c7a-4c1a-8c39-2b6c9b1a4d55",
                "code": "123456"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Code verified.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OtpVerifyResponse"
                }
              }
            }
          },
          "400": {
            "description": "invalid_code or otp_expired.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OtpError"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid API key"
                }
              }
            }
          },
          "404": {
            "description": "Unknown request_id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OtpError"
                }
              }
            }
          },
          "429": {
            "description": "too_many_attempts, or rate limited.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OtpError"
                }
              }
            }
          }
        }
      }
    },
    "/v1/messaging/opt-outs": {
      "post": {
        "tags": [
          "Messaging"
        ],
        "operationId": "messagingOptOuts",
        "summary": "Do-not-contact list",
        "description": "Manage the numbers that must never be messaged. `action` is `list` (default), `add` or `remove`. Opted-out numbers are dropped before a send is priced, so they are never charged for, and additions are mirrored to the network list.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "action": {
                    "type": "string",
                    "enum": [
                      "list",
                      "add",
                      "remove"
                    ]
                  },
                  "business_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "mode": {
                    "$ref": "#/components/schemas/Mode"
                  },
                  "msisdn": {
                    "type": "string"
                  },
                  "msisdns": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "source": {
                    "type": "string"
                  },
                  "sender_name": {
                    "type": "string"
                  }
                },
                "additionalProperties": false
              },
              "example": {
                "action": "add",
                "msisdns": [
                  "0240000000"
                ],
                "source": "stop_reply"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The list, or the result of the change.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Invalid parameters.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "amount must be > 0",
                  "request_id": "6f3c7d1e-9a2b-4c8f-b5e0-2b1a7e9d0c33"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid API key"
                }
              }
            }
          },
          "403": {
            "description": "Not a member of this business.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "forbidden",
                  "message": "Not a member of this business"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              },
              "retry-after": {
                "$ref": "#/components/headers/retry-after"
              },
              "x-ratelimit-limit": {
                "$ref": "#/components/headers/x-ratelimit-limit"
              },
              "x-ratelimit-remaining": {
                "$ref": "#/components/headers/x-ratelimit-remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Rate limit exceeded",
                  "request_id": "6f3c7d1e-9a2b-4c8f-b5e0-2b1a7e9d0c33"
                }
              }
            }
          }
        }
      }
    },
    "/v1/messaging/lookup": {
      "post": {
        "tags": [
          "Messaging"
        ],
        "operationId": "messagingLookup",
        "summary": "Number lookup (HLR)",
        "description": "Check whether a line is live and which operator holds it. Results are cached for 30 days and reused free of charge; failed checks are refunded. `action: \"history\"` returns past checks.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "action": {
                    "type": "string",
                    "enum": [
                      "lookup",
                      "history"
                    ]
                  },
                  "business_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "mode": {
                    "$ref": "#/components/schemas/Mode"
                  },
                  "msisdn": {
                    "type": "string"
                  },
                  "msisdns": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "maxItems": 1000
                  },
                  "refresh": {
                    "type": "boolean"
                  }
                },
                "additionalProperties": false
              },
              "example": {
                "msisdns": [
                  "0240000000"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Lookup results.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Invalid parameters.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "amount must be > 0",
                  "request_id": "6f3c7d1e-9a2b-4c8f-b5e0-2b1a7e9d0c33"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid API key"
                }
              }
            }
          },
          "402": {
            "description": "Not enough messaging credits.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "insufficient_credits",
                  "message": "Top up your wallet."
                }
              }
            }
          },
          "403": {
            "description": "Number lookup is switched off for this account.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "lookup_disabled",
                  "message": "Number lookup is switched off for this account"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              },
              "retry-after": {
                "$ref": "#/components/headers/retry-after"
              },
              "x-ratelimit-limit": {
                "$ref": "#/components/headers/x-ratelimit-limit"
              },
              "x-ratelimit-remaining": {
                "$ref": "#/components/headers/x-ratelimit-remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Rate limit exceeded",
                  "request_id": "6f3c7d1e-9a2b-4c8f-b5e0-2b1a7e9d0c33"
                }
              }
            }
          }
        }
      }
    },
    "/v1/messaging/whatsapp": {
      "post": {
        "tags": [
          "Messaging"
        ],
        "operationId": "messagingWhatsapp",
        "summary": "WhatsApp templates and campaigns",
        "description": "List or refresh approved WhatsApp templates, send a template campaign or a WhatsApp one-time code, and pull delivery reports. Delivery is also pushed on the signed `message.delivered` / `message.failed` webhooks.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "action": {
                    "type": "string",
                    "enum": [
                      "templates",
                      "sync_templates",
                      "template",
                      "create_template",
                      "send",
                      "otp",
                      "campaigns",
                      "status"
                    ]
                  },
                  "business_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "mode": {
                    "$ref": "#/components/schemas/Mode"
                  },
                  "name": {
                    "type": "string"
                  },
                  "template_id": {
                    "type": "string"
                  },
                  "recipients": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "group_ids": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    }
                  },
                  "sample": {
                    "type": "string"
                  },
                  "button_example": {
                    "type": "string"
                  },
                  "schedule_at": {
                    "type": "string",
                    "format": "date-time",
                    "nullable": true
                  },
                  "timezone_id": {
                    "type": "string"
                  },
                  "phone": {
                    "type": "string"
                  },
                  "code": {
                    "type": "string"
                  },
                  "campaign_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "template": {
                    "type": "object",
                    "description": "Used with `action: \"create_template\"`. Submitted to WhatsApp for approval; test mode stores a local pending draft.",
                    "required": [
                      "name",
                      "body_text"
                    ],
                    "properties": {
                      "name": {
                        "type": "string",
                        "description": "3-60 lowercase letters, digits or underscores."
                      },
                      "category_id": {
                        "type": "integer",
                        "description": "1 marketing, 2 utility, 3 authentication."
                      },
                      "language": {
                        "type": "integer",
                        "description": "Network language id. 1 is English."
                      },
                      "header_text": {
                        "type": "string",
                        "nullable": true
                      },
                      "body_text": {
                        "type": "string",
                        "description": "Use {{1}}, {{2}} for variables."
                      },
                      "example_body_text": {
                        "type": "string",
                        "nullable": true
                      },
                      "footer_text": {
                        "type": "string",
                        "nullable": true
                      },
                      "variables": {
                        "type": "integer",
                        "description": "How many variables the body uses."
                      },
                      "component_format_id": {
                        "type": "integer",
                        "description": "Header format: text, image or document."
                      },
                      "button_type_id": {
                        "type": "integer",
                        "description": "Quick reply, call, link or one-time code."
                      },
                      "button_text": {
                        "type": "string"
                      },
                      "button_url": {
                        "type": "string"
                      },
                      "button_phone_number": {
                        "type": "string"
                      },
                      "button_example": {
                        "type": "string"
                      },
                      "otp_type": {
                        "type": "string"
                      },
                      "whatsapp_number": {
                        "type": "string"
                      }
                    }
                  }
                },
                "additionalProperties": false
              },
              "example": {
                "action": "send",
                "template_id": "1042",
                "recipients": [
                  "0240000000"
                ],
                "sample": "Ama, 12 Sept"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Templates, campaigns, or the result of a send.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Invalid parameters.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "amount must be > 0",
                  "request_id": "6f3c7d1e-9a2b-4c8f-b5e0-2b1a7e9d0c33"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid API key"
                }
              }
            }
          },
          "402": {
            "description": "Not enough messaging credits.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "insufficient_credits",
                  "message": "Top up your wallet."
                }
              }
            }
          },
          "404": {
            "description": "Unknown campaign.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "not_found",
                  "message": "Campaign not found"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              },
              "retry-after": {
                "$ref": "#/components/headers/retry-after"
              },
              "x-ratelimit-limit": {
                "$ref": "#/components/headers/x-ratelimit-limit"
              },
              "x-ratelimit-remaining": {
                "$ref": "#/components/headers/x-ratelimit-remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Rate limit exceeded",
                  "request_id": "6f3c7d1e-9a2b-4c8f-b5e0-2b1a7e9d0c33"
                }
              }
            }
          },
          "502": {
            "description": "WhatsApp rejected the send.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "provider_error",
                  "message": "WhatsApp rejected every recipient"
                }
              }
            }
          }
        }
      }
    },
    "/v1/messaging/verify": {
      "post": {
        "tags": [
          "Messaging"
        ],
        "operationId": "messagingVerify",
        "summary": "Phone verification (network, flash call or WhatsApp)",
        "description": "Two-step phone verification. `method: \"network\"` lets the operator generate, send and check the code; `flash` places a missed call whose caller-ID digits are the code; `whatsapp` delivers the code on WhatsApp. Codes expire after 10 minutes and allow five attempts. In test mode nothing is sent and `123456` is always accepted.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "action": {
                    "type": "string",
                    "enum": [
                      "send",
                      "check",
                      "list"
                    ]
                  },
                  "business_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "mode": {
                    "$ref": "#/components/schemas/Mode"
                  },
                  "method": {
                    "type": "string",
                    "enum": [
                      "network",
                      "flash",
                      "whatsapp"
                    ]
                  },
                  "phone": {
                    "type": "string"
                  },
                  "brand": {
                    "type": "string"
                  },
                  "sender_name": {
                    "type": "string"
                  },
                  "template_id": {
                    "type": "string"
                  },
                  "caller": {
                    "type": "string"
                  },
                  "verification_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "code": {
                    "type": "string"
                  }
                },
                "additionalProperties": false
              },
              "example": {
                "method": "network",
                "phone": "0240000000"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Verification started, or the code was accepted.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Invalid parameters.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "amount must be > 0",
                  "request_id": "6f3c7d1e-9a2b-4c8f-b5e0-2b1a7e9d0c33"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid API key"
                }
              }
            }
          },
          "402": {
            "description": "Not enough messaging credits.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "insufficient_credits",
                  "message": "Top up your wallet."
                }
              }
            }
          },
          "404": {
            "description": "Unknown verification.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "not_found",
                  "message": "Verification not found"
                }
              }
            }
          },
          "429": {
            "description": "Too many attempts on this code.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "too_many_attempts",
                  "message": "Too many attempts. Ask for a new code."
                }
              }
            }
          },
          "502": {
            "description": "The network refused the verification.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "provider_error",
                  "message": "Provider error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/messaging/contacts": {
      "post": {
        "tags": [
          "Messaging"
        ],
        "operationId": "messagingContacts",
        "summary": "Contacts",
        "description": "List, create, import, update and delete contacts. Contacts belong to your business alone and are never written to, or read back from, a shared network address book.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "action"
                ],
                "properties": {
                  "action": {
                    "type": "string",
                    "enum": [
                      "list",
                      "create",
                      "import",
                      "update",
                      "delete"
                    ]
                  },
                  "business_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "mode": {
                    "$ref": "#/components/schemas/Mode"
                  },
                  "contact_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "phone": {
                    "type": "string",
                    "example": "0240000000"
                  },
                  "first_name": {
                    "type": "string"
                  },
                  "last_name": {
                    "type": "string"
                  },
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "birthday": {
                    "type": "string",
                    "format": "date",
                    "nullable": true
                  },
                  "tag": {
                    "type": "string",
                    "description": "Free-text label used to segment your audience."
                  },
                  "group_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "limit": {
                    "type": "integer",
                    "maximum": 2000,
                    "description": "Used with `action: \"list\"`."
                  },
                  "contacts": {
                    "type": "array",
                    "description": "Used with `action: \"import\"`.",
                    "items": {
                      "type": "object",
                      "required": [
                        "phone"
                      ],
                      "properties": {
                        "phone": {
                          "type": "string"
                        },
                        "first_name": {
                          "type": "string"
                        },
                        "last_name": {
                          "type": "string"
                        },
                        "email": {
                          "type": "string"
                        },
                        "tag": {
                          "type": "string"
                        }
                      }
                    }
                  }
                },
                "additionalProperties": false
              },
              "example": {
                "action": "create",
                "phone": "0240000000",
                "first_name": "Ama"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Contacts, or the result of the change.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "description": "Invalid parameters.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "amount must be > 0",
                  "request_id": "6f3c7d1e-9a2b-4c8f-b5e0-2b1a7e9d0c33"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid API key"
                }
              }
            }
          },
          "403": {
            "description": "Not a member of this business.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "forbidden",
                  "message": "Not a member of this business"
                }
              }
            }
          },
          "404": {
            "description": "Unknown contact.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "not_found",
                  "message": "Contact not found"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              },
              "retry-after": {
                "$ref": "#/components/headers/retry-after"
              },
              "x-ratelimit-limit": {
                "$ref": "#/components/headers/x-ratelimit-limit"
              },
              "x-ratelimit-remaining": {
                "$ref": "#/components/headers/x-ratelimit-remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Rate limit exceeded",
                  "request_id": "6f3c7d1e-9a2b-4c8f-b5e0-2b1a7e9d0c33"
                }
              }
            }
          }
        }
      }
    },
    "/v1/messaging/reference": {
      "get": {
        "tags": [
          "Messaging"
        ],
        "operationId": "messagingReference",
        "summary": "Network reference data (countries, time zones, currencies)",
        "description": "The network ids you need for scheduling and pricing. Use the time-zone ids with `timezone_id` on a scheduled send.",
        "parameters": [
          {
            "name": "kind",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "country",
                "timezone",
                "currency"
              ]
            },
            "description": "Omit to get every list."
          }
        ],
        "responses": {
          "200": {
            "description": "Reference lists.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid API key"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              },
              "retry-after": {
                "$ref": "#/components/headers/retry-after"
              },
              "x-ratelimit-limit": {
                "$ref": "#/components/headers/x-ratelimit-limit"
              },
              "x-ratelimit-remaining": {
                "$ref": "#/components/headers/x-ratelimit-remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Rate limit exceeded",
                  "request_id": "6f3c7d1e-9a2b-4c8f-b5e0-2b1a7e9d0c33"
                }
              }
            }
          }
        }
      }
    },
    "/v1/messaging/balance": {
      "get": {
        "tags": [
          "Messaging"
        ],
        "operationId": "messagingBalance",
        "summary": "Messaging wallet balance",
        "parameters": [
          {
            "name": "mode",
            "in": "query",
            "required": false,
            "schema": {
              "$ref": "#/components/schemas/Mode"
            },
            "description": "Defaults to the key mode."
          },
          {
            "name": "business_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Required only for dashboard-session calls."
          }
        ],
        "responses": {
          "200": {
            "description": "Wallet balance.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessagingBalanceResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid API key"
                }
              }
            }
          },
          "403": {
            "description": "Not a member of this business.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "forbidden",
                  "message": "Not a member of this business"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              },
              "retry-after": {
                "$ref": "#/components/headers/retry-after"
              },
              "x-ratelimit-limit": {
                "$ref": "#/components/headers/x-ratelimit-limit"
              },
              "x-ratelimit-remaining": {
                "$ref": "#/components/headers/x-ratelimit-remaining"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Rate limit exceeded",
                  "request_id": "6f3c7d1e-9a2b-4c8f-b5e0-2b1a7e9d0c33"
                }
              }
            }
          }
        }
      }
    },
    "/v1/messaging/voice": {
      "post": {
        "tags": [
          "Messaging"
        ],
        "operationId": "voice",
        "summary": "Outbound voice (currently unavailable)",
        "description": "Always returns 503 on the current network. The route stays reachable so existing integrations get a clear answer.",
        "responses": {
          "503": {
            "description": "Voice is not offered today.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "voice_unavailable",
                  "message": "Outbound voice calling is not currently offered. Existing voice history stays available in the dashboard.",
                  "retryable": false
                }
              }
            }
          }
        }
      }
    },
    "/messaging-send": {
      "servers": [
        {
          "url": "https://eydjkasswyygiycitnml.supabase.co/functions/v1",
          "description": "Messaging (legacy)"
        }
      ],
      "post": {
        "tags": [
          "Messaging"
        ],
        "operationId": "sendSmsLegacy",
        "summary": "Send an SMS campaign (legacy URL)",
        "description": "Identical behaviour to POST /v1/messaging/sms. Kept for integrations built before 9 September 2026.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SmsSendRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Campaign accepted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SmsSendResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid API key"
                }
              }
            }
          }
        }
      }
    },
    "/messaging-status": {
      "servers": [
        {
          "url": "https://eydjkasswyygiycitnml.supabase.co/functions/v1",
          "description": "Messaging (legacy)"
        }
      ],
      "post": {
        "tags": [
          "Messaging"
        ],
        "operationId": "smsStatusLegacy",
        "summary": "Delivery report for a campaign (legacy URL)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SmsStatusRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Reconciled.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SmsStatusResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid API key"
                }
              }
            }
          },
          "404": {
            "description": "Campaign not found.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "not_found",
                  "message": "Campaign not found"
                }
              }
            }
          }
        }
      }
    },
    "/messaging-otp": {
      "servers": [
        {
          "url": "https://eydjkasswyygiycitnml.supabase.co/functions/v1",
          "description": "Messaging (legacy)"
        }
      ],
      "post": {
        "tags": [
          "Messaging"
        ],
        "operationId": "otpLegacy",
        "summary": "Send or verify a one-time passcode (legacy URL)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/OtpSendRequest"
                  },
                  {
                    "$ref": "#/components/schemas/OtpVerifyRequest"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Code issued, or code verified.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/OtpSendResponse"
                    },
                    {
                      "$ref": "#/components/schemas/OtpVerifyResponse"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid API key"
                }
              }
            }
          }
        }
      }
    },
    "/messaging-balance": {
      "servers": [
        {
          "url": "https://eydjkasswyygiycitnml.supabase.co/functions/v1",
          "description": "Messaging (legacy)"
        }
      ],
      "post": {
        "tags": [
          "Messaging"
        ],
        "operationId": "messagingBalanceLegacy",
        "summary": "Messaging wallet balance (legacy URL)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MessagingBalanceRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Wallet balance.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessagingBalanceResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, revoked or expired API key.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "Invalid API key"
                }
              }
            }
          }
        }
      }
    },
    "/messaging-voice": {
      "servers": [
        {
          "url": "https://eydjkasswyygiycitnml.supabase.co/functions/v1",
          "description": "Messaging (legacy)"
        }
      ],
      "post": {
        "tags": [
          "Messaging"
        ],
        "operationId": "voiceLegacy",
        "summary": "Outbound voice (legacy URL, currently unavailable)",
        "responses": {
          "503": {
            "description": "Voice is not offered today.",
            "headers": {
              "x-request-id": {
                "$ref": "#/components/headers/x-request-id"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": "voice_unavailable"
                }
              }
            }
          }
        }
      }
    }
  },
  "webhooks": {
    "collection.approved": {
      "post": {
        "summary": "collection.approved event",
        "description": "Delivered to every enabled endpoint subscribed to this type. Respond `2xx` within 10 seconds; anything else is retried with exponential backoff. The signature is computed over the exact bytes we send.",
        "operationId": "webhook_collection_approved",
        "parameters": [
          {
            "name": "Webrabbit-Signature",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^t=\\d+,v1=[0-9a-f]{64}$"
            },
            "description": "t=<unix seconds>,v1=<hex HMAC-SHA256 of `${t}.${rawBody}`>",
            "in": "header"
          },
          {
            "name": "Webrabbit-Event-Id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Canonical event id. Identical to the body `id`, identical on every retry, and identical across endpoints. De-duplicate on this, never on Webrabbit-Delivery-Id.",
            "in": "header"
          },
          {
            "name": "Webrabbit-Event-Type",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/WebhookEventType"
            },
            "in": "header"
          },
          {
            "name": "Webrabbit-Delivery-Id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Unique per delivery attempt chain.",
            "in": "header"
          },
          {
            "name": "Webrabbit-Attempt",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            },
            "in": "header"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "type",
                  "mode",
                  "created_at",
                  "data"
                ],
                "additionalProperties": false,
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "Stable event id, prefixed `evt_`. De-duplicate on this."
                  },
                  "type": {
                    "const": "collection.approved"
                  },
                  "mode": {
                    "$ref": "#/components/schemas/Mode"
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "required": [
                      "object",
                      "resource_type",
                      "resource_id"
                    ],
                    "additionalProperties": false,
                    "properties": {
                      "object": {
                        "$ref": "#/components/schemas/CollectionApproved"
                      },
                      "resource_type": {
                        "type": "string",
                        "enum": [
                          "transaction",
                          "payout",
                          "sms_topup",
                          "message",
                          "disbursement",
                          "bill",
                          "order",
                          "product",
                          "invoice"
                        ]
                      },
                      "resource_id": {
                        "type": "string"
                      }
                    }
                  }
                }
              },
              "example": {
                "id": "evt_9f2c1a8e-3b21-4f7a-8b0e-2c9a1d4f5e60",
                "type": "collection.approved",
                "mode": "live",
                "created_at": "2026-09-08T10:22:31.104Z",
                "data": {
                  "object": {
                    "transaction_id": "521888807466",
                    "provider_transaction_id": "LP-9F23A1",
                    "status": "approved",
                    "code": "000",
                    "reason": "Transaction successful",
                    "subscriber_number": "0240000000",
                    "channel": "momo",
                    "gross_amount": 100,
                    "fee_amount": 15,
                    "provider_fee": 1.5,
                    "net_amount": 85,
                    "currency": "GHS",
                    "created_at": "2026-09-08T10:22:31.104Z"
                  },
                  "resource_type": "transaction",
                  "resource_id": "521888807466"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledged. Any 2xx stops retries."
          }
        }
      }
    },
    "collection.failed": {
      "post": {
        "summary": "collection.failed event",
        "description": "Delivered to every enabled endpoint subscribed to this type. Respond `2xx` within 10 seconds; anything else is retried with exponential backoff. The signature is computed over the exact bytes we send.",
        "operationId": "webhook_collection_failed",
        "parameters": [
          {
            "name": "Webrabbit-Signature",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^t=\\d+,v1=[0-9a-f]{64}$"
            },
            "description": "t=<unix seconds>,v1=<hex HMAC-SHA256 of `${t}.${rawBody}`>",
            "in": "header"
          },
          {
            "name": "Webrabbit-Event-Id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Canonical event id. Identical to the body `id`, identical on every retry, and identical across endpoints. De-duplicate on this, never on Webrabbit-Delivery-Id.",
            "in": "header"
          },
          {
            "name": "Webrabbit-Event-Type",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/WebhookEventType"
            },
            "in": "header"
          },
          {
            "name": "Webrabbit-Delivery-Id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Unique per delivery attempt chain.",
            "in": "header"
          },
          {
            "name": "Webrabbit-Attempt",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            },
            "in": "header"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "type",
                  "mode",
                  "created_at",
                  "data"
                ],
                "additionalProperties": false,
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "Stable event id, prefixed `evt_`. De-duplicate on this."
                  },
                  "type": {
                    "const": "collection.failed"
                  },
                  "mode": {
                    "$ref": "#/components/schemas/Mode"
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "required": [
                      "object",
                      "resource_type",
                      "resource_id"
                    ],
                    "additionalProperties": false,
                    "properties": {
                      "object": {
                        "$ref": "#/components/schemas/CollectionFailed"
                      },
                      "resource_type": {
                        "type": "string",
                        "enum": [
                          "transaction",
                          "payout",
                          "sms_topup",
                          "message",
                          "disbursement",
                          "bill",
                          "order",
                          "product",
                          "invoice"
                        ]
                      },
                      "resource_id": {
                        "type": "string"
                      }
                    }
                  }
                }
              },
              "example": {
                "id": "evt_9f2c1a8e-3b21-4f7a-8b0e-2c9a1d4f5e60",
                "type": "collection.failed",
                "mode": "live",
                "created_at": "2026-09-08T10:22:31.104Z",
                "data": {
                  "object": {
                    "transaction_id": "521888807467",
                    "status": "failed",
                    "code": "101",
                    "reason": "Insufficient funds",
                    "subscriber_number": "0240000000",
                    "channel": "momo",
                    "gross_amount": 100,
                    "currency": "GHS",
                    "created_at": "2026-09-08T10:22:31.104Z"
                  },
                  "resource_type": "transaction",
                  "resource_id": "521888807467"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledged. Any 2xx stops retries."
          }
        }
      }
    },
    "collection.reversed": {
      "post": {
        "summary": "collection.reversed event",
        "description": "Delivered to every enabled endpoint subscribed to this type. Respond `2xx` within 10 seconds; anything else is retried with exponential backoff. The signature is computed over the exact bytes we send.",
        "operationId": "webhook_collection_reversed",
        "parameters": [
          {
            "name": "Webrabbit-Signature",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^t=\\d+,v1=[0-9a-f]{64}$"
            },
            "description": "t=<unix seconds>,v1=<hex HMAC-SHA256 of `${t}.${rawBody}`>",
            "in": "header"
          },
          {
            "name": "Webrabbit-Event-Id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Canonical event id. Identical to the body `id`, identical on every retry, and identical across endpoints. De-duplicate on this, never on Webrabbit-Delivery-Id.",
            "in": "header"
          },
          {
            "name": "Webrabbit-Event-Type",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/WebhookEventType"
            },
            "in": "header"
          },
          {
            "name": "Webrabbit-Delivery-Id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Unique per delivery attempt chain.",
            "in": "header"
          },
          {
            "name": "Webrabbit-Attempt",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            },
            "in": "header"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "type",
                  "mode",
                  "created_at",
                  "data"
                ],
                "additionalProperties": false,
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "Stable event id, prefixed `evt_`. De-duplicate on this."
                  },
                  "type": {
                    "const": "collection.reversed"
                  },
                  "mode": {
                    "$ref": "#/components/schemas/Mode"
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "required": [
                      "object",
                      "resource_type",
                      "resource_id"
                    ],
                    "additionalProperties": false,
                    "properties": {
                      "object": {
                        "$ref": "#/components/schemas/CollectionReversed"
                      },
                      "resource_type": {
                        "type": "string",
                        "enum": [
                          "transaction",
                          "payout",
                          "sms_topup",
                          "message",
                          "disbursement",
                          "bill",
                          "order",
                          "product",
                          "invoice"
                        ]
                      },
                      "resource_id": {
                        "type": "string"
                      }
                    }
                  }
                }
              },
              "example": {
                "id": "evt_9f2c1a8e-3b21-4f7a-8b0e-2c9a1d4f5e60",
                "type": "collection.reversed",
                "mode": "live",
                "created_at": "2026-09-08T10:22:31.104Z",
                "data": {
                  "object": {
                    "transaction_id": "521888807466",
                    "status": "reversed",
                    "reason": "Reversed by provider",
                    "gross_amount": 100,
                    "fee_amount": 15,
                    "net_amount": 85,
                    "currency": "GHS",
                    "reversed_at": "2026-09-08T10:22:31.104Z"
                  },
                  "resource_type": "transaction",
                  "resource_id": "521888807466"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledged. Any 2xx stops retries."
          }
        }
      }
    },
    "refund.created": {
      "post": {
        "summary": "refund.created event",
        "description": "Delivered to every enabled endpoint subscribed to this type. Respond `2xx` within 10 seconds; anything else is retried with exponential backoff. The signature is computed over the exact bytes we send.",
        "operationId": "webhook_refund_created",
        "parameters": [
          {
            "name": "Webrabbit-Signature",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^t=\\d+,v1=[0-9a-f]{64}$"
            },
            "description": "t=<unix seconds>,v1=<hex HMAC-SHA256 of `${t}.${rawBody}`>",
            "in": "header"
          },
          {
            "name": "Webrabbit-Event-Id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Canonical event id. Identical to the body `id`, identical on every retry, and identical across endpoints. De-duplicate on this, never on Webrabbit-Delivery-Id.",
            "in": "header"
          },
          {
            "name": "Webrabbit-Event-Type",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/WebhookEventType"
            },
            "in": "header"
          },
          {
            "name": "Webrabbit-Delivery-Id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Unique per delivery attempt chain.",
            "in": "header"
          },
          {
            "name": "Webrabbit-Attempt",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            },
            "in": "header"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "type",
                  "mode",
                  "created_at",
                  "data"
                ],
                "additionalProperties": false,
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "Stable event id, prefixed `evt_`. De-duplicate on this."
                  },
                  "type": {
                    "const": "refund.created"
                  },
                  "mode": {
                    "$ref": "#/components/schemas/Mode"
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "required": [
                      "object",
                      "resource_type",
                      "resource_id"
                    ],
                    "additionalProperties": false,
                    "properties": {
                      "object": {
                        "$ref": "#/components/schemas/Refund"
                      },
                      "resource_type": {
                        "type": "string",
                        "enum": [
                          "transaction",
                          "payout",
                          "sms_topup",
                          "message",
                          "disbursement",
                          "bill",
                          "order",
                          "product",
                          "invoice"
                        ]
                      },
                      "resource_id": {
                        "type": "string"
                      }
                    }
                  }
                }
              },
              "example": {
                "id": "evt_9f2c1a8e-3b21-4f7a-8b0e-2c9a1d4f5e60",
                "type": "refund.created",
                "mode": "live",
                "created_at": "2026-09-08T10:22:31.104Z",
                "data": {
                  "object": {
                    "refund_id": "730114552901",
                    "transaction_id": "521888807466",
                    "status": "pending",
                    "type": "partial",
                    "amount": 40,
                    "amount_minor": 4000,
                    "fee_amount": 2,
                    "fee_amount_minor": 200,
                    "currency": "GHS",
                    "destination": "233240000000",
                    "network": "MTN",
                    "reason": "Customer request",
                    "provider_reason": null,
                    "reason_code": null,
                    "code": null,
                    "created_at": "2026-09-08T10:22:31.104Z",
                    "completed_at": null
                  },
                  "resource_type": "refund",
                  "resource_id": "730114552901"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledged. Any 2xx stops retries."
          }
        }
      }
    },
    "refund.succeeded": {
      "post": {
        "summary": "refund.succeeded event",
        "description": "Delivered to every enabled endpoint subscribed to this type. Respond `2xx` within 10 seconds; anything else is retried with exponential backoff. The signature is computed over the exact bytes we send.",
        "operationId": "webhook_refund_succeeded",
        "parameters": [
          {
            "name": "Webrabbit-Signature",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^t=\\d+,v1=[0-9a-f]{64}$"
            },
            "description": "t=<unix seconds>,v1=<hex HMAC-SHA256 of `${t}.${rawBody}`>",
            "in": "header"
          },
          {
            "name": "Webrabbit-Event-Id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Canonical event id. Identical to the body `id`, identical on every retry, and identical across endpoints. De-duplicate on this, never on Webrabbit-Delivery-Id.",
            "in": "header"
          },
          {
            "name": "Webrabbit-Event-Type",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/WebhookEventType"
            },
            "in": "header"
          },
          {
            "name": "Webrabbit-Delivery-Id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Unique per delivery attempt chain.",
            "in": "header"
          },
          {
            "name": "Webrabbit-Attempt",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            },
            "in": "header"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "type",
                  "mode",
                  "created_at",
                  "data"
                ],
                "additionalProperties": false,
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "Stable event id, prefixed `evt_`. De-duplicate on this."
                  },
                  "type": {
                    "const": "refund.succeeded"
                  },
                  "mode": {
                    "$ref": "#/components/schemas/Mode"
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "required": [
                      "object",
                      "resource_type",
                      "resource_id"
                    ],
                    "additionalProperties": false,
                    "properties": {
                      "object": {
                        "$ref": "#/components/schemas/Refund"
                      },
                      "resource_type": {
                        "type": "string",
                        "enum": [
                          "transaction",
                          "payout",
                          "sms_topup",
                          "message",
                          "disbursement",
                          "bill",
                          "order",
                          "product",
                          "invoice"
                        ]
                      },
                      "resource_id": {
                        "type": "string"
                      }
                    }
                  }
                }
              },
              "example": {
                "id": "evt_9f2c1a8e-3b21-4f7a-8b0e-2c9a1d4f5e60",
                "type": "refund.succeeded",
                "mode": "live",
                "created_at": "2026-09-08T10:22:31.104Z",
                "data": {
                  "object": {
                    "refund_id": "730114552901",
                    "transaction_id": "521888807466",
                    "status": "succeeded",
                    "type": "partial",
                    "amount": 40,
                    "amount_minor": 4000,
                    "fee_amount": 2,
                    "fee_amount_minor": 200,
                    "currency": "GHS",
                    "destination": "233240000000",
                    "network": "MTN",
                    "reason": "Customer request",
                    "provider_reason": "Transaction successful",
                    "reason_code": "success",
                    "code": "01",
                    "created_at": "2026-09-08T10:22:31.104Z",
                    "completed_at": "2026-09-08T10:22:31.104Z"
                  },
                  "resource_type": "refund",
                  "resource_id": "730114552901"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledged. Any 2xx stops retries."
          }
        }
      }
    },
    "refund.failed": {
      "post": {
        "summary": "refund.failed event",
        "description": "Delivered to every enabled endpoint subscribed to this type. Respond `2xx` within 10 seconds; anything else is retried with exponential backoff. The signature is computed over the exact bytes we send.",
        "operationId": "webhook_refund_failed",
        "parameters": [
          {
            "name": "Webrabbit-Signature",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^t=\\d+,v1=[0-9a-f]{64}$"
            },
            "description": "t=<unix seconds>,v1=<hex HMAC-SHA256 of `${t}.${rawBody}`>",
            "in": "header"
          },
          {
            "name": "Webrabbit-Event-Id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Canonical event id. Identical to the body `id`, identical on every retry, and identical across endpoints. De-duplicate on this, never on Webrabbit-Delivery-Id.",
            "in": "header"
          },
          {
            "name": "Webrabbit-Event-Type",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/WebhookEventType"
            },
            "in": "header"
          },
          {
            "name": "Webrabbit-Delivery-Id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Unique per delivery attempt chain.",
            "in": "header"
          },
          {
            "name": "Webrabbit-Attempt",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            },
            "in": "header"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "type",
                  "mode",
                  "created_at",
                  "data"
                ],
                "additionalProperties": false,
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "Stable event id, prefixed `evt_`. De-duplicate on this."
                  },
                  "type": {
                    "const": "refund.failed"
                  },
                  "mode": {
                    "$ref": "#/components/schemas/Mode"
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "required": [
                      "object",
                      "resource_type",
                      "resource_id"
                    ],
                    "additionalProperties": false,
                    "properties": {
                      "object": {
                        "$ref": "#/components/schemas/Refund"
                      },
                      "resource_type": {
                        "type": "string",
                        "enum": [
                          "transaction",
                          "payout",
                          "sms_topup",
                          "message",
                          "disbursement",
                          "bill",
                          "order",
                          "product",
                          "invoice"
                        ]
                      },
                      "resource_id": {
                        "type": "string"
                      }
                    }
                  }
                }
              },
              "example": {
                "id": "evt_9f2c1a8e-3b21-4f7a-8b0e-2c9a1d4f5e60",
                "type": "refund.failed",
                "mode": "live",
                "created_at": "2026-09-08T10:22:31.104Z",
                "data": {
                  "object": {
                    "refund_id": "730114552902",
                    "transaction_id": "521888807466",
                    "status": "failed",
                    "type": "full",
                    "amount": 100,
                    "amount_minor": 10000,
                    "fee_amount": 2,
                    "fee_amount_minor": 200,
                    "currency": "GHS",
                    "destination": "233240000000",
                    "network": "MTN",
                    "reason": "Duplicate charge",
                    "provider_reason": "Wallet not reachable",
                    "reason_code": "failed",
                    "code": "102",
                    "created_at": "2026-09-08T10:22:31.104Z",
                    "completed_at": "2026-09-08T10:22:31.104Z"
                  },
                  "resource_type": "refund",
                  "resource_id": "730114552902"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledged. Any 2xx stops retries."
          }
        }
      }
    },
    "refund.reversed": {
      "post": {
        "summary": "refund.reversed event",
        "description": "Delivered to every enabled endpoint subscribed to this type. Respond `2xx` within 10 seconds; anything else is retried with exponential backoff. The signature is computed over the exact bytes we send.",
        "operationId": "webhook_refund_reversed",
        "parameters": [
          {
            "name": "Webrabbit-Signature",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^t=\\d+,v1=[0-9a-f]{64}$"
            },
            "description": "t=<unix seconds>,v1=<hex HMAC-SHA256 of `${t}.${rawBody}`>",
            "in": "header"
          },
          {
            "name": "Webrabbit-Event-Id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Canonical event id. Identical to the body `id`, identical on every retry, and identical across endpoints. De-duplicate on this, never on Webrabbit-Delivery-Id.",
            "in": "header"
          },
          {
            "name": "Webrabbit-Event-Type",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/WebhookEventType"
            },
            "in": "header"
          },
          {
            "name": "Webrabbit-Delivery-Id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Unique per delivery attempt chain.",
            "in": "header"
          },
          {
            "name": "Webrabbit-Attempt",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            },
            "in": "header"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "type",
                  "mode",
                  "created_at",
                  "data"
                ],
                "additionalProperties": false,
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "Stable event id, prefixed `evt_`. De-duplicate on this."
                  },
                  "type": {
                    "const": "refund.reversed"
                  },
                  "mode": {
                    "$ref": "#/components/schemas/Mode"
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "required": [
                      "object",
                      "resource_type",
                      "resource_id"
                    ],
                    "additionalProperties": false,
                    "properties": {
                      "object": {
                        "$ref": "#/components/schemas/Refund"
                      },
                      "resource_type": {
                        "type": "string",
                        "enum": [
                          "transaction",
                          "payout",
                          "sms_topup",
                          "message",
                          "disbursement",
                          "bill",
                          "order",
                          "product",
                          "invoice"
                        ]
                      },
                      "resource_id": {
                        "type": "string"
                      }
                    }
                  }
                }
              },
              "example": {
                "id": "evt_9f2c1a8e-3b21-4f7a-8b0e-2c9a1d4f5e60",
                "type": "refund.reversed",
                "mode": "live",
                "created_at": "2026-09-08T10:22:31.104Z",
                "data": {
                  "object": {
                    "refund_id": "730114552903",
                    "transaction_id": "521888807466",
                    "status": "reversed",
                    "type": "full",
                    "amount": 100,
                    "amount_minor": 10000,
                    "fee_amount": 2,
                    "fee_amount_minor": 200,
                    "currency": "GHS",
                    "destination": "233240000000",
                    "network": "MTN",
                    "reason": "Refund payout reversed by the provider",
                    "created_at": "2026-09-08T10:22:31.104Z",
                    "completed_at": "2026-09-08T10:22:31.104Z"
                  },
                  "resource_type": "refund",
                  "resource_id": "730114552903"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledged. Any 2xx stops retries."
          }
        }
      }
    },
    "split.settled": {
      "post": {
        "summary": "split.settled event",
        "description": "Delivered to every enabled endpoint subscribed to this type. Respond `2xx` within 10 seconds; anything else is retried with exponential backoff. The signature is computed over the exact bytes we send.",
        "operationId": "webhook_split_settled",
        "parameters": [
          {
            "name": "Webrabbit-Signature",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^t=\\d+,v1=[0-9a-f]{64}$"
            },
            "description": "t=<unix seconds>,v1=<hex HMAC-SHA256 of `${t}.${rawBody}`>",
            "in": "header"
          },
          {
            "name": "Webrabbit-Event-Id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Canonical event id. Identical to the body `id`, identical on every retry, and identical across endpoints. De-duplicate on this, never on Webrabbit-Delivery-Id.",
            "in": "header"
          },
          {
            "name": "Webrabbit-Event-Type",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/WebhookEventType"
            },
            "in": "header"
          },
          {
            "name": "Webrabbit-Delivery-Id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Unique per delivery attempt chain.",
            "in": "header"
          },
          {
            "name": "Webrabbit-Attempt",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            },
            "in": "header"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "type",
                  "mode",
                  "created_at",
                  "data"
                ],
                "additionalProperties": false,
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "Stable event id, prefixed `evt_`. De-duplicate on this."
                  },
                  "type": {
                    "const": "split.settled"
                  },
                  "mode": {
                    "$ref": "#/components/schemas/Mode"
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "required": [
                      "object",
                      "resource_type",
                      "resource_id"
                    ],
                    "additionalProperties": false,
                    "properties": {
                      "object": {
                        "$ref": "#/components/schemas/SplitEvent"
                      },
                      "resource_type": {
                        "type": "string",
                        "enum": [
                          "transaction",
                          "payout",
                          "sms_topup",
                          "message",
                          "disbursement",
                          "bill",
                          "order",
                          "product",
                          "invoice"
                        ]
                      },
                      "resource_id": {
                        "type": "string"
                      }
                    }
                  }
                }
              },
              "example": {
                "id": "evt_9f2c1a8e-3b21-4f7a-8b0e-2c9a1d4f5e60",
                "type": "split.settled",
                "mode": "live",
                "created_at": "2026-09-08T10:22:31.104Z",
                "data": {
                  "object": {
                    "transaction_id": "521888807466",
                    "status": "approved",
                    "split_status": "settled",
                    "gross_amount": 50,
                    "gross_amount_minor": 5000,
                    "fee_amount": 7.5,
                    "fee_amount_minor": 750,
                    "net_amount": 42.5,
                    "net_amount_minor": 4250,
                    "merchant_amount_minor": 0,
                    "currency": "GHS",
                    "splits": [
                      {
                        "sub_account": "driver_8814",
                        "type": "percentage",
                        "value": 85,
                        "amount": 42.5,
                        "amount_minor": 4250,
                        "status": "settled",
                        "settled_at": "2026-09-08T10:22:31.104Z",
                        "reversed_at": null
                      }
                    ],
                    "settled_at": "2026-09-08T10:22:31.104Z",
                    "created_at": "2026-09-08T10:22:31.104Z"
                  },
                  "resource_type": "transaction",
                  "resource_id": "521888807466"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledged. Any 2xx stops retries."
          }
        }
      }
    },
    "split.reversed": {
      "post": {
        "summary": "split.reversed event",
        "description": "Delivered to every enabled endpoint subscribed to this type. Respond `2xx` within 10 seconds; anything else is retried with exponential backoff. The signature is computed over the exact bytes we send.",
        "operationId": "webhook_split_reversed",
        "parameters": [
          {
            "name": "Webrabbit-Signature",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^t=\\d+,v1=[0-9a-f]{64}$"
            },
            "description": "t=<unix seconds>,v1=<hex HMAC-SHA256 of `${t}.${rawBody}`>",
            "in": "header"
          },
          {
            "name": "Webrabbit-Event-Id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Canonical event id. Identical to the body `id`, identical on every retry, and identical across endpoints. De-duplicate on this, never on Webrabbit-Delivery-Id.",
            "in": "header"
          },
          {
            "name": "Webrabbit-Event-Type",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/WebhookEventType"
            },
            "in": "header"
          },
          {
            "name": "Webrabbit-Delivery-Id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Unique per delivery attempt chain.",
            "in": "header"
          },
          {
            "name": "Webrabbit-Attempt",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            },
            "in": "header"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "type",
                  "mode",
                  "created_at",
                  "data"
                ],
                "additionalProperties": false,
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "Stable event id, prefixed `evt_`. De-duplicate on this."
                  },
                  "type": {
                    "const": "split.reversed"
                  },
                  "mode": {
                    "$ref": "#/components/schemas/Mode"
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "required": [
                      "object",
                      "resource_type",
                      "resource_id"
                    ],
                    "additionalProperties": false,
                    "properties": {
                      "object": {
                        "$ref": "#/components/schemas/SplitEvent"
                      },
                      "resource_type": {
                        "type": "string",
                        "enum": [
                          "transaction",
                          "payout",
                          "sms_topup",
                          "message",
                          "disbursement",
                          "bill",
                          "order",
                          "product",
                          "invoice"
                        ]
                      },
                      "resource_id": {
                        "type": "string"
                      }
                    }
                  }
                }
              },
              "example": {
                "id": "evt_9f2c1a8e-3b21-4f7a-8b0e-2c9a1d4f5e60",
                "type": "split.reversed",
                "mode": "live",
                "created_at": "2026-09-08T10:22:31.104Z",
                "data": {
                  "object": {
                    "transaction_id": "521888807466",
                    "status": "reversed",
                    "split_status": "reversed",
                    "gross_amount": 50,
                    "gross_amount_minor": 5000,
                    "fee_amount": 7.5,
                    "fee_amount_minor": 750,
                    "net_amount": 42.5,
                    "net_amount_minor": 4250,
                    "merchant_amount_minor": 0,
                    "currency": "GHS",
                    "splits": [
                      {
                        "sub_account": "driver_8814",
                        "type": "percentage",
                        "value": 85,
                        "amount": 42.5,
                        "amount_minor": 4250,
                        "status": "reversed",
                        "settled_at": "2026-09-08T10:22:31.104Z",
                        "reversed_at": "2026-09-08T10:22:31.104Z"
                      }
                    ],
                    "reversed_at": "2026-09-08T10:22:31.104Z",
                    "created_at": "2026-09-08T10:22:31.104Z"
                  },
                  "resource_type": "transaction",
                  "resource_id": "521888807466"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledged. Any 2xx stops retries."
          }
        }
      }
    },
    "payout.queued": {
      "post": {
        "summary": "payout.queued event",
        "description": "Delivered to every enabled endpoint subscribed to this type. Respond `2xx` within 10 seconds; anything else is retried with exponential backoff. The signature is computed over the exact bytes we send.",
        "operationId": "webhook_payout_queued",
        "parameters": [
          {
            "name": "Webrabbit-Signature",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^t=\\d+,v1=[0-9a-f]{64}$"
            },
            "description": "t=<unix seconds>,v1=<hex HMAC-SHA256 of `${t}.${rawBody}`>",
            "in": "header"
          },
          {
            "name": "Webrabbit-Event-Id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Canonical event id. Identical to the body `id`, identical on every retry, and identical across endpoints. De-duplicate on this, never on Webrabbit-Delivery-Id.",
            "in": "header"
          },
          {
            "name": "Webrabbit-Event-Type",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/WebhookEventType"
            },
            "in": "header"
          },
          {
            "name": "Webrabbit-Delivery-Id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Unique per delivery attempt chain.",
            "in": "header"
          },
          {
            "name": "Webrabbit-Attempt",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            },
            "in": "header"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "type",
                  "mode",
                  "created_at",
                  "data"
                ],
                "additionalProperties": false,
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "Stable event id, prefixed `evt_`. De-duplicate on this."
                  },
                  "type": {
                    "const": "payout.queued"
                  },
                  "mode": {
                    "$ref": "#/components/schemas/Mode"
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "required": [
                      "object",
                      "resource_type",
                      "resource_id"
                    ],
                    "additionalProperties": false,
                    "properties": {
                      "object": {
                        "$ref": "#/components/schemas/Disbursement"
                      },
                      "resource_type": {
                        "type": "string",
                        "enum": [
                          "transaction",
                          "payout",
                          "sms_topup",
                          "message",
                          "disbursement",
                          "bill",
                          "order",
                          "product",
                          "invoice"
                        ]
                      },
                      "resource_id": {
                        "type": "string"
                      }
                    }
                  }
                }
              },
              "example": {
                "id": "evt_9f2c1a8e-3b21-4f7a-8b0e-2c9a1d4f5e60",
                "type": "payout.queued",
                "mode": "live",
                "created_at": "2026-09-08T10:22:31.104Z",
                "data": {
                  "object": {
                    "id": "94afd213-8dbe-450e-958d-53b6d8f852f8",
                    "object": "disbursement",
                    "status": "pending_review",
                    "mode": "live",
                    "channel": "momo",
                    "tier": "business",
                    "currency": "GHS",
                    "amount": 550,
                    "amount_minor": 55000,
                    "fee": 2,
                    "item_count": 2,
                    "reference": "friday-riders",
                    "scheduled_for": "2026-09-08T10:22:31.104Z",
                    "released_at": null,
                    "created_at": "2026-09-08T10:22:31.104Z",
                    "recipients": []
                  },
                  "resource_type": "disbursement",
                  "resource_id": "94afd213-8dbe-450e-958d-53b6d8f852f8"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledged. Any 2xx stops retries."
          }
        }
      }
    },
    "payout.completed": {
      "post": {
        "summary": "payout.completed event",
        "description": "Delivered to every enabled endpoint subscribed to this type. Respond `2xx` within 10 seconds; anything else is retried with exponential backoff. The signature is computed over the exact bytes we send.",
        "operationId": "webhook_payout_completed",
        "parameters": [
          {
            "name": "Webrabbit-Signature",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^t=\\d+,v1=[0-9a-f]{64}$"
            },
            "description": "t=<unix seconds>,v1=<hex HMAC-SHA256 of `${t}.${rawBody}`>",
            "in": "header"
          },
          {
            "name": "Webrabbit-Event-Id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Canonical event id. Identical to the body `id`, identical on every retry, and identical across endpoints. De-duplicate on this, never on Webrabbit-Delivery-Id.",
            "in": "header"
          },
          {
            "name": "Webrabbit-Event-Type",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/WebhookEventType"
            },
            "in": "header"
          },
          {
            "name": "Webrabbit-Delivery-Id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Unique per delivery attempt chain.",
            "in": "header"
          },
          {
            "name": "Webrabbit-Attempt",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            },
            "in": "header"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "type",
                  "mode",
                  "created_at",
                  "data"
                ],
                "additionalProperties": false,
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "Stable event id, prefixed `evt_`. De-duplicate on this."
                  },
                  "type": {
                    "const": "payout.completed"
                  },
                  "mode": {
                    "$ref": "#/components/schemas/Mode"
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "required": [
                      "object",
                      "resource_type",
                      "resource_id"
                    ],
                    "additionalProperties": false,
                    "properties": {
                      "object": {
                        "$ref": "#/components/schemas/PayoutCompleted"
                      },
                      "resource_type": {
                        "type": "string",
                        "enum": [
                          "transaction",
                          "payout",
                          "sms_topup",
                          "message",
                          "disbursement",
                          "bill",
                          "order",
                          "product",
                          "invoice"
                        ]
                      },
                      "resource_id": {
                        "type": "string"
                      }
                    }
                  }
                }
              },
              "example": {
                "id": "evt_9f2c1a8e-3b21-4f7a-8b0e-2c9a1d4f5e60",
                "type": "payout.completed",
                "mode": "live",
                "created_at": "2026-09-08T10:22:31.104Z",
                "data": {
                  "object": {
                    "payout_id": "b0d2f1c4-2f28-4a1f-9a3d-6b1f0a9c1122",
                    "status": "success",
                    "amount": 500,
                    "fee": 0,
                    "net_amount": 500,
                    "currency": "GHS",
                    "destination": "MTN ••0332",
                    "completed_at": "2026-09-08T10:22:31.104Z"
                  },
                  "resource_type": "payout",
                  "resource_id": "b0d2f1c4-2f28-4a1f-9a3d-6b1f0a9c1122"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledged. Any 2xx stops retries."
          }
        }
      }
    },
    "payout.failed": {
      "post": {
        "summary": "payout.failed event",
        "description": "Delivered to every enabled endpoint subscribed to this type. Respond `2xx` within 10 seconds; anything else is retried with exponential backoff. The signature is computed over the exact bytes we send.",
        "operationId": "webhook_payout_failed",
        "parameters": [
          {
            "name": "Webrabbit-Signature",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^t=\\d+,v1=[0-9a-f]{64}$"
            },
            "description": "t=<unix seconds>,v1=<hex HMAC-SHA256 of `${t}.${rawBody}`>",
            "in": "header"
          },
          {
            "name": "Webrabbit-Event-Id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Canonical event id. Identical to the body `id`, identical on every retry, and identical across endpoints. De-duplicate on this, never on Webrabbit-Delivery-Id.",
            "in": "header"
          },
          {
            "name": "Webrabbit-Event-Type",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/WebhookEventType"
            },
            "in": "header"
          },
          {
            "name": "Webrabbit-Delivery-Id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Unique per delivery attempt chain.",
            "in": "header"
          },
          {
            "name": "Webrabbit-Attempt",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            },
            "in": "header"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "type",
                  "mode",
                  "created_at",
                  "data"
                ],
                "additionalProperties": false,
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "Stable event id, prefixed `evt_`. De-duplicate on this."
                  },
                  "type": {
                    "const": "payout.failed"
                  },
                  "mode": {
                    "$ref": "#/components/schemas/Mode"
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "required": [
                      "object",
                      "resource_type",
                      "resource_id"
                    ],
                    "additionalProperties": false,
                    "properties": {
                      "object": {
                        "$ref": "#/components/schemas/PayoutFailed"
                      },
                      "resource_type": {
                        "type": "string",
                        "enum": [
                          "transaction",
                          "payout",
                          "sms_topup",
                          "message",
                          "disbursement",
                          "bill",
                          "order",
                          "product",
                          "invoice"
                        ]
                      },
                      "resource_id": {
                        "type": "string"
                      }
                    }
                  }
                }
              },
              "example": {
                "id": "evt_9f2c1a8e-3b21-4f7a-8b0e-2c9a1d4f5e60",
                "type": "payout.failed",
                "mode": "live",
                "created_at": "2026-09-08T10:22:31.104Z",
                "data": {
                  "object": {
                    "payout_id": "b0d2f1c4-2f28-4a1f-9a3d-6b1f0a9c1122",
                    "status": "failed",
                    "reason": "Invalid recipient account",
                    "amount": 500,
                    "currency": "GHS",
                    "failed_at": "2026-09-08T10:22:31.104Z"
                  },
                  "resource_type": "payout",
                  "resource_id": "b0d2f1c4-2f28-4a1f-9a3d-6b1f0a9c1122"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledged. Any 2xx stops retries."
          }
        }
      }
    },
    "bill.completed": {
      "post": {
        "summary": "bill.completed event",
        "description": "Delivered to every enabled endpoint subscribed to this type. Respond `2xx` within 10 seconds; anything else is retried with exponential backoff. The signature is computed over the exact bytes we send.",
        "operationId": "webhook_bill_completed",
        "parameters": [
          {
            "name": "Webrabbit-Signature",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^t=\\d+,v1=[0-9a-f]{64}$"
            },
            "description": "t=<unix seconds>,v1=<hex HMAC-SHA256 of `${t}.${rawBody}`>",
            "in": "header"
          },
          {
            "name": "Webrabbit-Event-Id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Canonical event id. Identical to the body `id`, identical on every retry, and identical across endpoints. De-duplicate on this, never on Webrabbit-Delivery-Id.",
            "in": "header"
          },
          {
            "name": "Webrabbit-Event-Type",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/WebhookEventType"
            },
            "in": "header"
          },
          {
            "name": "Webrabbit-Delivery-Id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Unique per delivery attempt chain.",
            "in": "header"
          },
          {
            "name": "Webrabbit-Attempt",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            },
            "in": "header"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "type",
                  "mode",
                  "created_at",
                  "data"
                ],
                "additionalProperties": false,
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "Stable event id, prefixed `evt_`. De-duplicate on this."
                  },
                  "type": {
                    "const": "bill.completed"
                  },
                  "mode": {
                    "$ref": "#/components/schemas/Mode"
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "required": [
                      "object",
                      "resource_type",
                      "resource_id"
                    ],
                    "additionalProperties": false,
                    "properties": {
                      "object": {
                        "$ref": "#/components/schemas/BillPurchase"
                      },
                      "resource_type": {
                        "type": "string",
                        "enum": [
                          "transaction",
                          "payout",
                          "sms_topup",
                          "message",
                          "disbursement",
                          "bill",
                          "order",
                          "product",
                          "invoice"
                        ]
                      },
                      "resource_id": {
                        "type": "string"
                      }
                    }
                  }
                }
              },
              "example": {
                "id": "evt_9f2c1a8e-3b21-4f7a-8b0e-2c9a1d4f5e60",
                "type": "bill.completed",
                "mode": "live",
                "created_at": "2026-09-08T10:22:31.104Z",
                "data": {
                  "object": {
                    "id": "6f2a1b90-0f2a-4c5b-9e2d-1a7c3d5e9f01",
                    "reference": "AIRLZ8F2K3QW9T",
                    "kind": "airtime",
                    "status": "approved",
                    "amount": 20,
                    "fee": 0,
                    "total": 20,
                    "currency": "GHS",
                    "account_number": "233551234567",
                    "account_name": "Ama Mensah",
                    "mode": "live",
                    "created_at": "2026-09-08T10:22:31.104Z",
                    "completed_at": "2026-09-08T10:22:31.104Z"
                  },
                  "resource_type": "bill_purchase",
                  "resource_id": "6f2a1b90-0f2a-4c5b-9e2d-1a7c3d5e9f01"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledged. Any 2xx stops retries."
          }
        }
      }
    },
    "bill.failed": {
      "post": {
        "summary": "bill.failed event",
        "description": "Delivered to every enabled endpoint subscribed to this type. Respond `2xx` within 10 seconds; anything else is retried with exponential backoff. The signature is computed over the exact bytes we send.",
        "operationId": "webhook_bill_failed",
        "parameters": [
          {
            "name": "Webrabbit-Signature",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^t=\\d+,v1=[0-9a-f]{64}$"
            },
            "description": "t=<unix seconds>,v1=<hex HMAC-SHA256 of `${t}.${rawBody}`>",
            "in": "header"
          },
          {
            "name": "Webrabbit-Event-Id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Canonical event id. Identical to the body `id`, identical on every retry, and identical across endpoints. De-duplicate on this, never on Webrabbit-Delivery-Id.",
            "in": "header"
          },
          {
            "name": "Webrabbit-Event-Type",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/WebhookEventType"
            },
            "in": "header"
          },
          {
            "name": "Webrabbit-Delivery-Id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Unique per delivery attempt chain.",
            "in": "header"
          },
          {
            "name": "Webrabbit-Attempt",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            },
            "in": "header"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "type",
                  "mode",
                  "created_at",
                  "data"
                ],
                "additionalProperties": false,
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "Stable event id, prefixed `evt_`. De-duplicate on this."
                  },
                  "type": {
                    "const": "bill.failed"
                  },
                  "mode": {
                    "$ref": "#/components/schemas/Mode"
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "required": [
                      "object",
                      "resource_type",
                      "resource_id"
                    ],
                    "additionalProperties": false,
                    "properties": {
                      "object": {
                        "$ref": "#/components/schemas/BillPurchase"
                      },
                      "resource_type": {
                        "type": "string",
                        "enum": [
                          "transaction",
                          "payout",
                          "sms_topup",
                          "message",
                          "disbursement",
                          "bill",
                          "order",
                          "product",
                          "invoice"
                        ]
                      },
                      "resource_id": {
                        "type": "string"
                      }
                    }
                  }
                }
              },
              "example": {
                "id": "evt_9f2c1a8e-3b21-4f7a-8b0e-2c9a1d4f5e60",
                "type": "bill.failed",
                "mode": "live",
                "created_at": "2026-09-08T10:22:31.104Z",
                "data": {
                  "object": {
                    "id": "6f2a1b90-0f2a-4c5b-9e2d-1a7c3d5e9f01",
                    "reference": "AIRLZ8F2K3QW9T",
                    "kind": "airtime",
                    "status": "failed",
                    "amount": 20,
                    "fee": 0,
                    "total": 20,
                    "currency": "GHS",
                    "account_number": "233551234567",
                    "message": "Recipient number is not active",
                    "mode": "live",
                    "created_at": "2026-09-08T10:22:31.104Z",
                    "completed_at": "2026-09-08T10:22:31.104Z"
                  },
                  "resource_type": "bill_purchase",
                  "resource_id": "6f2a1b90-0f2a-4c5b-9e2d-1a7c3d5e9f01"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledged. Any 2xx stops retries."
          }
        }
      }
    },
    "sms_topup.approved": {
      "post": {
        "summary": "sms_topup.approved event",
        "description": "Delivered to every enabled endpoint subscribed to this type. Respond `2xx` within 10 seconds; anything else is retried with exponential backoff. The signature is computed over the exact bytes we send.",
        "operationId": "webhook_sms_topup_approved",
        "parameters": [
          {
            "name": "Webrabbit-Signature",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^t=\\d+,v1=[0-9a-f]{64}$"
            },
            "description": "t=<unix seconds>,v1=<hex HMAC-SHA256 of `${t}.${rawBody}`>",
            "in": "header"
          },
          {
            "name": "Webrabbit-Event-Id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Canonical event id. Identical to the body `id`, identical on every retry, and identical across endpoints. De-duplicate on this, never on Webrabbit-Delivery-Id.",
            "in": "header"
          },
          {
            "name": "Webrabbit-Event-Type",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/WebhookEventType"
            },
            "in": "header"
          },
          {
            "name": "Webrabbit-Delivery-Id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Unique per delivery attempt chain.",
            "in": "header"
          },
          {
            "name": "Webrabbit-Attempt",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            },
            "in": "header"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "type",
                  "mode",
                  "created_at",
                  "data"
                ],
                "additionalProperties": false,
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "Stable event id, prefixed `evt_`. De-duplicate on this."
                  },
                  "type": {
                    "const": "sms_topup.approved"
                  },
                  "mode": {
                    "$ref": "#/components/schemas/Mode"
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "required": [
                      "object",
                      "resource_type",
                      "resource_id"
                    ],
                    "additionalProperties": false,
                    "properties": {
                      "object": {
                        "$ref": "#/components/schemas/SmsTopupApproved"
                      },
                      "resource_type": {
                        "type": "string",
                        "enum": [
                          "transaction",
                          "payout",
                          "sms_topup",
                          "message",
                          "disbursement",
                          "bill",
                          "order",
                          "product",
                          "invoice"
                        ]
                      },
                      "resource_id": {
                        "type": "string"
                      }
                    }
                  }
                }
              },
              "example": {
                "id": "evt_9f2c1a8e-3b21-4f7a-8b0e-2c9a1d4f5e60",
                "type": "sms_topup.approved",
                "mode": "live",
                "created_at": "2026-09-08T10:22:31.104Z",
                "data": {
                  "object": {
                    "topup_id": "7c9c1b56-1c1e-4a3e-b0b8-9a1e2b7fbb01",
                    "status": "approved",
                    "amount": 50,
                    "currency": "GHS",
                    "balance_after": 120.5,
                    "credited_at": "2026-09-08T10:22:31.104Z"
                  },
                  "resource_type": "sms_topup",
                  "resource_id": "7c9c1b56-1c1e-4a3e-b0b8-9a1e2b7fbb01"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledged. Any 2xx stops retries."
          }
        }
      }
    },
    "message.delivered": {
      "post": {
        "summary": "message.delivered event",
        "description": "Delivered to every enabled endpoint subscribed to this type. Respond `2xx` within 10 seconds; anything else is retried with exponential backoff. The signature is computed over the exact bytes we send.",
        "operationId": "webhook_message_delivered",
        "parameters": [
          {
            "name": "Webrabbit-Signature",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^t=\\d+,v1=[0-9a-f]{64}$"
            },
            "description": "t=<unix seconds>,v1=<hex HMAC-SHA256 of `${t}.${rawBody}`>",
            "in": "header"
          },
          {
            "name": "Webrabbit-Event-Id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Canonical event id. Identical to the body `id`, identical on every retry, and identical across endpoints. De-duplicate on this, never on Webrabbit-Delivery-Id.",
            "in": "header"
          },
          {
            "name": "Webrabbit-Event-Type",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/WebhookEventType"
            },
            "in": "header"
          },
          {
            "name": "Webrabbit-Delivery-Id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Unique per delivery attempt chain.",
            "in": "header"
          },
          {
            "name": "Webrabbit-Attempt",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            },
            "in": "header"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "type",
                  "mode",
                  "created_at",
                  "data"
                ],
                "additionalProperties": false,
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "Stable event id, prefixed `evt_`. De-duplicate on this."
                  },
                  "type": {
                    "const": "message.delivered"
                  },
                  "mode": {
                    "$ref": "#/components/schemas/Mode"
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "required": [
                      "object",
                      "resource_type",
                      "resource_id"
                    ],
                    "additionalProperties": false,
                    "properties": {
                      "object": {
                        "$ref": "#/components/schemas/MessageReceipt"
                      },
                      "resource_type": {
                        "type": "string",
                        "enum": [
                          "transaction",
                          "payout",
                          "sms_topup",
                          "message",
                          "disbursement",
                          "bill",
                          "order",
                          "product",
                          "invoice"
                        ]
                      },
                      "resource_id": {
                        "type": "string"
                      }
                    }
                  }
                }
              },
              "example": {
                "id": "evt_9f2c1a8e-3b21-4f7a-8b0e-2c9a1d4f5e60",
                "type": "message.delivered",
                "mode": "live",
                "created_at": "2026-09-08T10:22:31.104Z",
                "data": {
                  "object": {
                    "message_id": "a3f1c0de-6b21-4d38-9c1a-2f70b8d51e44",
                    "campaign_id": "c14b7c2e-9a4d-4c07-9e12-0f3b6f2a8d55",
                    "to": "+233240000000",
                    "sender": "WRB",
                    "mode": "live",
                    "status": "delivered",
                    "provider_status": "DELIVERED",
                    "provider_message_id": "SM-88213004",
                    "error_reason": null,
                    "segments": 1,
                    "cost": 0.045,
                    "submitted_at": "2026-09-08T10:22:31.104Z",
                    "settled_at": "2026-09-08T10:22:31.104Z"
                  },
                  "resource_type": "message",
                  "resource_id": "a3f1c0de-6b21-4d38-9c1a-2f70b8d51e44"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledged. Any 2xx stops retries."
          }
        }
      }
    },
    "message.failed": {
      "post": {
        "summary": "message.failed event",
        "description": "Delivered to every enabled endpoint subscribed to this type. Respond `2xx` within 10 seconds; anything else is retried with exponential backoff. The signature is computed over the exact bytes we send.",
        "operationId": "webhook_message_failed",
        "parameters": [
          {
            "name": "Webrabbit-Signature",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^t=\\d+,v1=[0-9a-f]{64}$"
            },
            "description": "t=<unix seconds>,v1=<hex HMAC-SHA256 of `${t}.${rawBody}`>",
            "in": "header"
          },
          {
            "name": "Webrabbit-Event-Id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Canonical event id. Identical to the body `id`, identical on every retry, and identical across endpoints. De-duplicate on this, never on Webrabbit-Delivery-Id.",
            "in": "header"
          },
          {
            "name": "Webrabbit-Event-Type",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/WebhookEventType"
            },
            "in": "header"
          },
          {
            "name": "Webrabbit-Delivery-Id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Unique per delivery attempt chain.",
            "in": "header"
          },
          {
            "name": "Webrabbit-Attempt",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            },
            "in": "header"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "type",
                  "mode",
                  "created_at",
                  "data"
                ],
                "additionalProperties": false,
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "Stable event id, prefixed `evt_`. De-duplicate on this."
                  },
                  "type": {
                    "const": "message.failed"
                  },
                  "mode": {
                    "$ref": "#/components/schemas/Mode"
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "required": [
                      "object",
                      "resource_type",
                      "resource_id"
                    ],
                    "additionalProperties": false,
                    "properties": {
                      "object": {
                        "$ref": "#/components/schemas/MessageReceipt"
                      },
                      "resource_type": {
                        "type": "string",
                        "enum": [
                          "transaction",
                          "payout",
                          "sms_topup",
                          "message",
                          "disbursement",
                          "bill",
                          "order",
                          "product",
                          "invoice"
                        ]
                      },
                      "resource_id": {
                        "type": "string"
                      }
                    }
                  }
                }
              },
              "example": {
                "id": "evt_9f2c1a8e-3b21-4f7a-8b0e-2c9a1d4f5e60",
                "type": "message.failed",
                "mode": "live",
                "created_at": "2026-09-08T10:22:31.104Z",
                "data": {
                  "object": {
                    "message_id": "a3f1c0de-6b21-4d38-9c1a-2f70b8d51e44",
                    "campaign_id": "c14b7c2e-9a4d-4c07-9e12-0f3b6f2a8d55",
                    "to": "+233240000000",
                    "sender": "WRB",
                    "mode": "live",
                    "status": "undelivered",
                    "provider_status": "NO_DLR",
                    "provider_message_id": "SM-88213004",
                    "error_reason": "no_delivery_report",
                    "segments": 1,
                    "cost": 0.045,
                    "submitted_at": "2026-09-08T10:22:31.104Z",
                    "settled_at": "2026-09-08T10:22:31.104Z"
                  },
                  "resource_type": "message",
                  "resource_id": "a3f1c0de-6b21-4d38-9c1a-2f70b8d51e44"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledged. Any 2xx stops retries."
          }
        }
      }
    },
    "message.rejected": {
      "post": {
        "summary": "message.rejected event",
        "description": "Delivered to every enabled endpoint subscribed to this type. Respond `2xx` within 10 seconds; anything else is retried with exponential backoff. The signature is computed over the exact bytes we send.",
        "operationId": "webhook_message_rejected",
        "parameters": [
          {
            "name": "Webrabbit-Signature",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^t=\\d+,v1=[0-9a-f]{64}$"
            },
            "description": "t=<unix seconds>,v1=<hex HMAC-SHA256 of `${t}.${rawBody}`>",
            "in": "header"
          },
          {
            "name": "Webrabbit-Event-Id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Canonical event id. Identical to the body `id`, identical on every retry, and identical across endpoints. De-duplicate on this, never on Webrabbit-Delivery-Id.",
            "in": "header"
          },
          {
            "name": "Webrabbit-Event-Type",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/WebhookEventType"
            },
            "in": "header"
          },
          {
            "name": "Webrabbit-Delivery-Id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Unique per delivery attempt chain.",
            "in": "header"
          },
          {
            "name": "Webrabbit-Attempt",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            },
            "in": "header"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "type",
                  "mode",
                  "created_at",
                  "data"
                ],
                "additionalProperties": false,
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "Stable event id, prefixed `evt_`. De-duplicate on this."
                  },
                  "type": {
                    "const": "message.rejected"
                  },
                  "mode": {
                    "$ref": "#/components/schemas/Mode"
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "required": [
                      "object",
                      "resource_type",
                      "resource_id"
                    ],
                    "additionalProperties": false,
                    "properties": {
                      "object": {
                        "$ref": "#/components/schemas/MessageReceipt"
                      },
                      "resource_type": {
                        "type": "string",
                        "enum": [
                          "transaction",
                          "payout",
                          "sms_topup",
                          "message",
                          "disbursement",
                          "bill",
                          "order",
                          "product",
                          "invoice"
                        ]
                      },
                      "resource_id": {
                        "type": "string"
                      }
                    }
                  }
                }
              },
              "example": {
                "id": "evt_9f2c1a8e-3b21-4f7a-8b0e-2c9a1d4f5e60",
                "type": "message.rejected",
                "mode": "live",
                "created_at": "2026-09-08T10:22:31.104Z",
                "data": {
                  "object": {
                    "message_id": "a3f1c0de-6b21-4d38-9c1a-2f70b8d51e44",
                    "campaign_id": "c14b7c2e-9a4d-4c07-9e12-0f3b6f2a8d55",
                    "to": "+233240000000",
                    "sender": "WRB",
                    "mode": "live",
                    "status": "rejected",
                    "provider_status": "REJECTED",
                    "provider_message_id": null,
                    "error_reason": "Sender ID not permitted",
                    "segments": 1,
                    "cost": 0.045,
                    "submitted_at": "2026-09-08T10:22:31.104Z",
                    "settled_at": "2026-09-08T10:22:31.104Z"
                  },
                  "resource_type": "message",
                  "resource_id": "a3f1c0de-6b21-4d38-9c1a-2f70b8d51e44"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledged. Any 2xx stops retries."
          }
        }
      }
    },
    "order.paid": {
      "post": {
        "summary": "order.paid event",
        "description": "Delivered to every enabled endpoint subscribed to this type. Respond `2xx` within 10 seconds; anything else is retried with exponential backoff. The signature is computed over the exact bytes we send.",
        "operationId": "webhook_order_paid",
        "parameters": [
          {
            "name": "Webrabbit-Signature",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^t=\\d+,v1=[0-9a-f]{64}$"
            },
            "description": "t=<unix seconds>,v1=<hex HMAC-SHA256 of `${t}.${rawBody}`>",
            "in": "header"
          },
          {
            "name": "Webrabbit-Event-Id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Canonical event id. Identical to the body `id`, identical on every retry, and identical across endpoints. De-duplicate on this, never on Webrabbit-Delivery-Id.",
            "in": "header"
          },
          {
            "name": "Webrabbit-Event-Type",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/WebhookEventType"
            },
            "in": "header"
          },
          {
            "name": "Webrabbit-Delivery-Id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Unique per delivery attempt chain.",
            "in": "header"
          },
          {
            "name": "Webrabbit-Attempt",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            },
            "in": "header"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "type",
                  "mode",
                  "created_at",
                  "data"
                ],
                "additionalProperties": false,
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "Stable event id, prefixed `evt_`. De-duplicate on this."
                  },
                  "type": {
                    "const": "order.paid"
                  },
                  "mode": {
                    "$ref": "#/components/schemas/Mode"
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "required": [
                      "object",
                      "resource_type",
                      "resource_id"
                    ],
                    "additionalProperties": false,
                    "properties": {
                      "object": {
                        "$ref": "#/components/schemas/CommerceOrderEvent"
                      },
                      "resource_type": {
                        "type": "string",
                        "enum": [
                          "transaction",
                          "payout",
                          "sms_topup",
                          "message",
                          "disbursement",
                          "bill",
                          "order",
                          "product",
                          "invoice"
                        ]
                      },
                      "resource_id": {
                        "type": "string"
                      }
                    }
                  }
                }
              },
              "example": {
                "id": "evt_9f2c1a8e-3b21-4f7a-8b0e-2c9a1d4f5e60",
                "type": "order.paid",
                "mode": "live",
                "created_at": "2026-09-08T10:22:31.104Z",
                "data": {
                  "object": {
                    "id": "c1d2e3f4-0000-4000-8000-000000000001",
                    "order_number": "ORD-000123",
                    "status": "paid",
                    "fulfilment_status": "unfulfilled",
                    "total": 185,
                    "currency": "GHS",
                    "customer_email": "ama@example.com",
                    "created_at": "2026-09-08T10:22:31.104Z"
                  },
                  "resource_type": "order",
                  "resource_id": "c1d2e3f4-0000-4000-8000-000000000001"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledged. Any 2xx stops retries."
          }
        }
      }
    },
    "order.failed": {
      "post": {
        "summary": "order.failed event",
        "description": "Delivered to every enabled endpoint subscribed to this type. Respond `2xx` within 10 seconds; anything else is retried with exponential backoff. The signature is computed over the exact bytes we send.",
        "operationId": "webhook_order_failed",
        "parameters": [
          {
            "name": "Webrabbit-Signature",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^t=\\d+,v1=[0-9a-f]{64}$"
            },
            "description": "t=<unix seconds>,v1=<hex HMAC-SHA256 of `${t}.${rawBody}`>",
            "in": "header"
          },
          {
            "name": "Webrabbit-Event-Id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Canonical event id. Identical to the body `id`, identical on every retry, and identical across endpoints. De-duplicate on this, never on Webrabbit-Delivery-Id.",
            "in": "header"
          },
          {
            "name": "Webrabbit-Event-Type",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/WebhookEventType"
            },
            "in": "header"
          },
          {
            "name": "Webrabbit-Delivery-Id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Unique per delivery attempt chain.",
            "in": "header"
          },
          {
            "name": "Webrabbit-Attempt",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            },
            "in": "header"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "type",
                  "mode",
                  "created_at",
                  "data"
                ],
                "additionalProperties": false,
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "Stable event id, prefixed `evt_`. De-duplicate on this."
                  },
                  "type": {
                    "const": "order.failed"
                  },
                  "mode": {
                    "$ref": "#/components/schemas/Mode"
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "required": [
                      "object",
                      "resource_type",
                      "resource_id"
                    ],
                    "additionalProperties": false,
                    "properties": {
                      "object": {
                        "$ref": "#/components/schemas/CommerceOrderEvent"
                      },
                      "resource_type": {
                        "type": "string",
                        "enum": [
                          "transaction",
                          "payout",
                          "sms_topup",
                          "message",
                          "disbursement",
                          "bill",
                          "order",
                          "product",
                          "invoice"
                        ]
                      },
                      "resource_id": {
                        "type": "string"
                      }
                    }
                  }
                }
              },
              "example": {
                "id": "evt_9f2c1a8e-3b21-4f7a-8b0e-2c9a1d4f5e60",
                "type": "order.failed",
                "mode": "live",
                "created_at": "2026-09-08T10:22:31.104Z",
                "data": {
                  "object": {
                    "id": "c1d2e3f4-0000-4000-8000-000000000001",
                    "order_number": "ORD-000123",
                    "status": "failed",
                    "fulfilment_status": "unfulfilled",
                    "total": 185,
                    "currency": "GHS",
                    "customer_email": "ama@example.com",
                    "created_at": "2026-09-08T10:22:31.104Z"
                  },
                  "resource_type": "order",
                  "resource_id": "c1d2e3f4-0000-4000-8000-000000000001"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledged. Any 2xx stops retries."
          }
        }
      }
    },
    "order.fulfilled": {
      "post": {
        "summary": "order.fulfilled event",
        "description": "Delivered to every enabled endpoint subscribed to this type. Respond `2xx` within 10 seconds; anything else is retried with exponential backoff. The signature is computed over the exact bytes we send.",
        "operationId": "webhook_order_fulfilled",
        "parameters": [
          {
            "name": "Webrabbit-Signature",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^t=\\d+,v1=[0-9a-f]{64}$"
            },
            "description": "t=<unix seconds>,v1=<hex HMAC-SHA256 of `${t}.${rawBody}`>",
            "in": "header"
          },
          {
            "name": "Webrabbit-Event-Id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Canonical event id. Identical to the body `id`, identical on every retry, and identical across endpoints. De-duplicate on this, never on Webrabbit-Delivery-Id.",
            "in": "header"
          },
          {
            "name": "Webrabbit-Event-Type",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/WebhookEventType"
            },
            "in": "header"
          },
          {
            "name": "Webrabbit-Delivery-Id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Unique per delivery attempt chain.",
            "in": "header"
          },
          {
            "name": "Webrabbit-Attempt",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            },
            "in": "header"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "type",
                  "mode",
                  "created_at",
                  "data"
                ],
                "additionalProperties": false,
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "Stable event id, prefixed `evt_`. De-duplicate on this."
                  },
                  "type": {
                    "const": "order.fulfilled"
                  },
                  "mode": {
                    "$ref": "#/components/schemas/Mode"
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "required": [
                      "object",
                      "resource_type",
                      "resource_id"
                    ],
                    "additionalProperties": false,
                    "properties": {
                      "object": {
                        "$ref": "#/components/schemas/CommerceOrderEvent"
                      },
                      "resource_type": {
                        "type": "string",
                        "enum": [
                          "transaction",
                          "payout",
                          "sms_topup",
                          "message",
                          "disbursement",
                          "bill",
                          "order",
                          "product",
                          "invoice"
                        ]
                      },
                      "resource_id": {
                        "type": "string"
                      }
                    }
                  }
                }
              },
              "example": {
                "id": "evt_9f2c1a8e-3b21-4f7a-8b0e-2c9a1d4f5e60",
                "type": "order.fulfilled",
                "mode": "live",
                "created_at": "2026-09-08T10:22:31.104Z",
                "data": {
                  "object": {
                    "id": "c1d2e3f4-0000-4000-8000-000000000001",
                    "order_number": "ORD-000123",
                    "status": "paid",
                    "fulfilment_status": "fulfilled",
                    "total": 185,
                    "currency": "GHS",
                    "customer_email": "ama@example.com",
                    "created_at": "2026-09-08T10:22:31.104Z"
                  },
                  "resource_type": "order",
                  "resource_id": "c1d2e3f4-0000-4000-8000-000000000001"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledged. Any 2xx stops retries."
          }
        }
      }
    },
    "inventory.low": {
      "post": {
        "summary": "inventory.low event",
        "description": "Delivered to every enabled endpoint subscribed to this type. Respond `2xx` within 10 seconds; anything else is retried with exponential backoff. The signature is computed over the exact bytes we send.",
        "operationId": "webhook_inventory_low",
        "parameters": [
          {
            "name": "Webrabbit-Signature",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^t=\\d+,v1=[0-9a-f]{64}$"
            },
            "description": "t=<unix seconds>,v1=<hex HMAC-SHA256 of `${t}.${rawBody}`>",
            "in": "header"
          },
          {
            "name": "Webrabbit-Event-Id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Canonical event id. Identical to the body `id`, identical on every retry, and identical across endpoints. De-duplicate on this, never on Webrabbit-Delivery-Id.",
            "in": "header"
          },
          {
            "name": "Webrabbit-Event-Type",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/WebhookEventType"
            },
            "in": "header"
          },
          {
            "name": "Webrabbit-Delivery-Id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Unique per delivery attempt chain.",
            "in": "header"
          },
          {
            "name": "Webrabbit-Attempt",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            },
            "in": "header"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "type",
                  "mode",
                  "created_at",
                  "data"
                ],
                "additionalProperties": false,
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "Stable event id, prefixed `evt_`. De-duplicate on this."
                  },
                  "type": {
                    "const": "inventory.low"
                  },
                  "mode": {
                    "$ref": "#/components/schemas/Mode"
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "required": [
                      "object",
                      "resource_type",
                      "resource_id"
                    ],
                    "additionalProperties": false,
                    "properties": {
                      "object": {
                        "$ref": "#/components/schemas/InventoryLow"
                      },
                      "resource_type": {
                        "type": "string",
                        "enum": [
                          "transaction",
                          "payout",
                          "sms_topup",
                          "message",
                          "disbursement",
                          "bill",
                          "order",
                          "product",
                          "invoice"
                        ]
                      },
                      "resource_id": {
                        "type": "string"
                      }
                    }
                  }
                }
              },
              "example": {
                "id": "evt_9f2c1a8e-3b21-4f7a-8b0e-2c9a1d4f5e60",
                "type": "inventory.low",
                "mode": "live",
                "created_at": "2026-09-08T10:22:31.104Z",
                "data": {
                  "object": {
                    "product_id": "d1e2f3a4-0000-4000-8000-000000000002",
                    "name": "T-shirt",
                    "stock": 3,
                    "threshold": 5
                  },
                  "resource_type": "product",
                  "resource_id": "d1e2f3a4-0000-4000-8000-000000000002"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledged. Any 2xx stops retries."
          }
        }
      }
    },
    "invoice.paid": {
      "post": {
        "summary": "invoice.paid event",
        "description": "Delivered to every enabled endpoint subscribed to this type. Respond `2xx` within 10 seconds; anything else is retried with exponential backoff. The signature is computed over the exact bytes we send.",
        "operationId": "webhook_invoice_paid",
        "parameters": [
          {
            "name": "Webrabbit-Signature",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^t=\\d+,v1=[0-9a-f]{64}$"
            },
            "description": "t=<unix seconds>,v1=<hex HMAC-SHA256 of `${t}.${rawBody}`>",
            "in": "header"
          },
          {
            "name": "Webrabbit-Event-Id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Canonical event id. Identical to the body `id`, identical on every retry, and identical across endpoints. De-duplicate on this, never on Webrabbit-Delivery-Id.",
            "in": "header"
          },
          {
            "name": "Webrabbit-Event-Type",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/WebhookEventType"
            },
            "in": "header"
          },
          {
            "name": "Webrabbit-Delivery-Id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Unique per delivery attempt chain.",
            "in": "header"
          },
          {
            "name": "Webrabbit-Attempt",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            },
            "in": "header"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "type",
                  "mode",
                  "created_at",
                  "data"
                ],
                "additionalProperties": false,
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "Stable event id, prefixed `evt_`. De-duplicate on this."
                  },
                  "type": {
                    "const": "invoice.paid"
                  },
                  "mode": {
                    "$ref": "#/components/schemas/Mode"
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "required": [
                      "object",
                      "resource_type",
                      "resource_id"
                    ],
                    "additionalProperties": false,
                    "properties": {
                      "object": {
                        "$ref": "#/components/schemas/InvoiceEvent"
                      },
                      "resource_type": {
                        "type": "string",
                        "enum": [
                          "transaction",
                          "payout",
                          "sms_topup",
                          "message",
                          "disbursement",
                          "bill",
                          "order",
                          "product",
                          "invoice"
                        ]
                      },
                      "resource_id": {
                        "type": "string"
                      }
                    }
                  }
                }
              },
              "example": {
                "id": "evt_9f2c1a8e-3b21-4f7a-8b0e-2c9a1d4f5e60",
                "type": "invoice.paid",
                "mode": "live",
                "created_at": "2026-09-08T10:22:31.104Z",
                "data": {
                  "object": {
                    "id": "e1f2a3b4-0000-4000-8000-000000000003",
                    "invoice_number": "INV-0001",
                    "status": "paid",
                    "total": 100,
                    "amount_paid": 100,
                    "currency": "GHS",
                    "paid_at": "2026-09-08T10:22:31.104Z"
                  },
                  "resource_type": "invoice",
                  "resource_id": "e1f2a3b4-0000-4000-8000-000000000003"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledged. Any 2xx stops retries."
          }
        }
      }
    },
    "invoice.partially_paid": {
      "post": {
        "summary": "invoice.partially_paid event",
        "description": "Delivered to every enabled endpoint subscribed to this type. Respond `2xx` within 10 seconds; anything else is retried with exponential backoff. The signature is computed over the exact bytes we send.",
        "operationId": "webhook_invoice_partially_paid",
        "parameters": [
          {
            "name": "Webrabbit-Signature",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^t=\\d+,v1=[0-9a-f]{64}$"
            },
            "description": "t=<unix seconds>,v1=<hex HMAC-SHA256 of `${t}.${rawBody}`>",
            "in": "header"
          },
          {
            "name": "Webrabbit-Event-Id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Canonical event id. Identical to the body `id`, identical on every retry, and identical across endpoints. De-duplicate on this, never on Webrabbit-Delivery-Id.",
            "in": "header"
          },
          {
            "name": "Webrabbit-Event-Type",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/WebhookEventType"
            },
            "in": "header"
          },
          {
            "name": "Webrabbit-Delivery-Id",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Unique per delivery attempt chain.",
            "in": "header"
          },
          {
            "name": "Webrabbit-Attempt",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 1
            },
            "in": "header"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "id",
                  "type",
                  "mode",
                  "created_at",
                  "data"
                ],
                "additionalProperties": false,
                "properties": {
                  "id": {
                    "type": "string",
                    "description": "Stable event id, prefixed `evt_`. De-duplicate on this."
                  },
                  "type": {
                    "const": "invoice.partially_paid"
                  },
                  "mode": {
                    "$ref": "#/components/schemas/Mode"
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "required": [
                      "object",
                      "resource_type",
                      "resource_id"
                    ],
                    "additionalProperties": false,
                    "properties": {
                      "object": {
                        "$ref": "#/components/schemas/InvoiceEvent"
                      },
                      "resource_type": {
                        "type": "string",
                        "enum": [
                          "transaction",
                          "payout",
                          "sms_topup",
                          "message",
                          "disbursement",
                          "bill",
                          "order",
                          "product",
                          "invoice"
                        ]
                      },
                      "resource_id": {
                        "type": "string"
                      }
                    }
                  }
                }
              },
              "example": {
                "id": "evt_9f2c1a8e-3b21-4f7a-8b0e-2c9a1d4f5e60",
                "type": "invoice.partially_paid",
                "mode": "live",
                "created_at": "2026-09-08T10:22:31.104Z",
                "data": {
                  "object": {
                    "id": "e1f2a3b4-0000-4000-8000-000000000003",
                    "invoice_number": "INV-0001",
                    "status": "partially_paid",
                    "total": 100,
                    "amount_paid": 40,
                    "currency": "GHS",
                    "paid_at": "2026-09-08T10:22:31.104Z"
                  },
                  "resource_type": "invoice",
                  "resource_id": "e1f2a3b4-0000-4000-8000-000000000003"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Acknowledged. Any 2xx stops retries."
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "SecretKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "Your secret API key as `Authorization: Bearer wr_live_…` or `wr_test_…`. Payments keys and Messaging keys are separate; a Messaging key on a Payments route (or vice versa) returns 403 `wrong_product`."
      }
    },
    "parameters": {
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "schema": {
          "type": "string",
          "maxLength": 255
        },
        "description": "Safely retry a charge. Replaying the same key with the same body returns the original response with `idempotent-replayed: true`; a different body returns 409."
      },
      "HttpReferer": {
        "name": "HTTP-Referer",
        "in": "header",
        "required": false,
        "schema": {
          "type": "string",
          "format": "uri"
        },
        "description": "The site the call is made from. Optional but strongly recommended — undeclared live traffic is recorded as `(undeclared)`. Tell us before you change domain or rebrand."
      },
      "AppTitle": {
        "name": "X-Webrabbitmedia-Title",
        "in": "header",
        "required": false,
        "schema": {
          "type": "string"
        },
        "description": "Human-readable name of the app making the call."
      }
    },
    "headers": {
      "x-request-id": {
        "description": "Present on every response. Quote it in support requests.",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "x-wr-mode": {
        "description": "`test` or `live` — the mode the key actually ran in.",
        "schema": {
          "$ref": "#/components/schemas/Mode"
        }
      },
      "x-wr-upstream": {
        "description": "Rail the request reached: `simulator`, `360pay` or `junipay`.",
        "schema": {
          "type": "string"
        }
      },
      "x-wr-test-mode": {
        "description": "Test-mode responses only: `simulated` or `live_rails`.",
        "schema": {
          "type": "string"
        }
      },
      "x-wr-attribution-warning": {
        "description": "Present when the attribution headers were missing. Informational only — the request is still served.",
        "schema": {
          "type": "string"
        }
      },
      "x-ratelimit-limit": {
        "description": "Requests allowed in the current window.",
        "schema": {
          "type": "integer"
        }
      },
      "x-ratelimit-remaining": {
        "description": "Requests left in the current window.",
        "schema": {
          "type": "integer"
        }
      },
      "retry-after": {
        "description": "Seconds to wait after a 429.",
        "schema": {
          "type": "integer"
        }
      },
      "idempotent-replayed": {
        "description": "`true` when the response was replayed from a previous identical request.",
        "schema": {
          "type": "string"
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "description": "Every non-2xx response uses this envelope. `error` is always present. `request_id` is present on errors raised at the edge (rate limits, unknown routes, upstream failures); the `x-request-id` response header is present on every response and is the reliable source.",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "Machine-readable code or human-readable message. Safe to log; never show verbatim to a customer."
          },
          "code": {
            "type": [
              "string",
              "null"
            ],
            "description": "Machine-readable code when the payment layer can classify the failure (e.g. account_not_found)."
          },
          "reason": {
            "type": [
              "string",
              "null"
            ],
            "description": "Provider or validation detail behind `code`."
          },
          "message": {
            "type": [
              "string",
              "null"
            ],
            "description": "Human-readable detail. Present on messaging errors."
          },
          "request_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Edge request id. Absent on errors raised inside the payment layer — read `x-request-id` instead."
          }
        },
        "additionalProperties": true
      },
      "Currency": {
        "type": "string",
        "enum": [
          "GHS"
        ],
        "description": "Only Ghana Cedi is settled today."
      },
      "Mode": {
        "type": "string",
        "enum": [
          "test",
          "live"
        ]
      },
      "Network": {
        "type": "string",
        "enum": [
          "MTN",
          "AT",
          "TELECEL",
          "GMONEY"
        ],
        "description": "Mobile money network. `AT` is AirtelTigo. Common aliases (`VODAFONE`, `VDF`, `AIRTELTIGO`, `TIGO`, `TGO`, `AIRTEL`, `ATL`) are normalised on input; responses always use the canonical value."
      },
      "TransactionStatus": {
        "type": "string",
        "enum": [
          "pending",
          "approved",
          "failed",
          "reversed"
        ],
        "description": "`pending` means the customer has not yet acted (or the provider has not confirmed). `approved` is final and credited. `failed` is final and nothing moved. `reversed` means a previously approved charge was unwound."
      },
      "Me": {
        "type": "object",
        "required": [
          "mode",
          "business_id",
          "business_name",
          "business_status",
          "live_ready",
          "api_key_id",
          "scopes",
          "commission_bps",
          "app"
        ],
        "properties": {
          "mode": {
            "$ref": "#/components/schemas/Mode"
          },
          "upstream": {
            "type": "string",
            "enum": [
              "simulator",
              "360pay",
              "junipay"
            ],
            "description": "Which rail this key actually reaches. Present on test-mode keys and on live keys. `simulator` never contacts a provider and never moves money."
          },
          "test_mode": {
            "type": "string",
            "enum": [
              "simulated",
              "live_rails"
            ],
            "description": "Present only when `mode` is `test`. `live_rails` is an explicit, per-business escape hatch."
          },
          "test_moves_real_money": {
            "type": "boolean",
            "description": "Present only when `mode` is `test`. `false` for the simulator."
          },
          "test_max_amount": {
            "type": "number",
            "description": "Present only when `mode` is `test`. Hard per-transaction cap in GHS (10 for the simulator)."
          },
          "business_id": {
            "type": "string",
            "format": "uuid"
          },
          "business_name": {
            "type": "string"
          },
          "business_status": {
            "type": "string",
            "enum": [
              "pending",
              "submitted",
              "in_review",
              "approved",
              "rejected",
              "suspended"
            ]
          },
          "live_ready": {
            "type": "boolean",
            "description": "True only when `business_status` is `approved`."
          },
          "api_key_id": {
            "type": "string",
            "format": "uuid"
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "read",
                "write"
              ]
            }
          },
          "commission_bps": {
            "type": "integer",
            "description": "Platform fee in basis points applied to approved collections for this business. 1000 = 10%, the starting rate."
          },
          "app": {
            "type": "object",
            "description": "The site declared through the attribution headers, as we currently hold it.",
            "required": [
              "site",
              "name",
              "status"
            ],
            "properties": {
              "site": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Declared host, or null when no site has ever been declared."
              },
              "name": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "status": {
                "type": "string",
                "enum": [
                  "undeclared",
                  "pending",
                  "approved",
                  "blocked"
                ],
                "description": "`undeclared` for integrations that never send the attribution headers — still fully served."
              }
            }
          },
          "enabled": {
            "type": "object",
            "description": "What this business can collect and pay out: country, currency, methods, Mobile Money networks, phone format and limits.",
            "properties": {
              "country": {
                "type": "string"
              },
              "currency": {
                "type": "string"
              },
              "collection_methods": {
                "type": "array",
                "items": {
                  "type": "string",
                  "enum": [
                    "mobile_money",
                    "bank_transfer",
                    "card"
                  ]
                }
              },
              "mobile_money_networks": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "phone_format": {
                "type": "object",
                "properties": {
                  "dial_code": {
                    "type": "string"
                  },
                  "local_digits": {
                    "type": "integer"
                  }
                }
              },
              "payout_methods": {
                "type": "array",
                "items": {
                  "type": "string",
                  "enum": [
                    "mobile_money",
                    "bank"
                  ]
                }
              },
              "limits": {
                "type": "object",
                "additionalProperties": true
              },
              "cross_border_collection": {
                "type": "boolean",
                "description": "False today: a business collects only in its registered country's currency."
              }
            }
          }
        },
        "additionalProperties": false
      },
      "CollectMomoRequest": {
        "type": "object",
        "description": "Send exactly one of `amount` or `amount_minor`. Sending both with different values is rejected with `amount_conflict`.",
        "required": [
          "subscriber_number",
          "network"
        ],
        "properties": {
          "amount": {
            "type": "number",
            "exclusiveMinimum": 0,
            "description": "Gross amount in GHS, max 2 decimals. In simulated test mode the hard cap is 10."
          },
          "amount_minor": {
            "type": "integer",
            "minimum": 1,
            "description": "Gross amount in pesewas (1 GHS = 100). Exact integer alternative to `amount`."
          },
          "split": {
            "$ref": "#/components/schemas/SplitInstruction"
          },
          "subscriber_number": {
            "type": "string",
            "description": "Customer wallet number as `0XXXXXXXXX` or `233XXXXXXXXX`. Echoed back in local `0XXXXXXXXX` form."
          },
          "network": {
            "$ref": "#/components/schemas/Network"
          },
          "desc": {
            "type": "string",
            "maxLength": 100,
            "default": "Mobile Money Payment"
          },
          "customer_email": {
            "type": "string",
            "description": "Optional. Falls back to the resolved wallet name when omitted."
          },
          "reference": {
            "type": "string",
            "maxLength": 120,
            "description": "Your order reference, returned on collection.* webhooks and transaction retrieve."
          },
          "metadata": {
            "type": "object",
            "additionalProperties": {
              "type": [
                "string",
                "number",
                "boolean"
              ]
            },
            "maxProperties": 20,
            "description": "Up to 20 flat keys, 2 KB."
          },
          "currency": {
            "type": "string",
            "description": "Optional. Must equal your business currency (GET /v1/me → enabled.currency), otherwise `422 currency_not_enabled`."
          },
          "country": {
            "type": "string",
            "description": "Optional ISO country. Must be your registered country."
          }
        },
        "additionalProperties": false
      },
      "Charge": {
        "type": "object",
        "description": "Result of a collection request. Fee fields are always numbers, never null and never absent: on a `pending` charge `fee_amount` is `0` and `net_amount` equals `gross_amount`, because the platform fee is only assessed once the charge is approved.",
        "required": [
          "transaction_id",
          "provider_transaction_id",
          "status",
          "code",
          "reason_code",
          "reason",
          "subscriber_number",
          "gross_amount",
          "fee_amount",
          "net_amount",
          "currency"
        ],
        "properties": {
          "transaction_id": {
            "type": "string",
            "pattern": "^\\d{12}$",
            "description": "Our reference. Use it for /v1/transactions/{transaction_id}."
          },
          "provider_transaction_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Upstream reference. Null while the provider has not issued one, and null on simulated charges."
          },
          "status": {
            "$ref": "#/components/schemas/TransactionStatus"
          },
          "code": {
            "type": [
              "string",
              "null"
            ],
            "description": "Upstream provider numeric status code (00 success, 01 failed, 02 pending, 03 processing). Null when the provider returned none. Never a word — use `reason_code` for logic."
          },
          "reason_code": {
            "type": "string",
            "enum": [
              "approved",
              "pending",
              "prompt_sent",
              "prompt_expired",
              "insufficient_funds",
              "declined",
              "cancelled",
              "reversed",
              "failed",
              "settled_in_error",
              "upstream_error",
              "unsupported_network",
              "unsupported_bank"
            ],
            "description": "Machine-readable outcome. Switch on this, not on `reason`."
          },
          "reason": {
            "type": [
              "string",
              "null"
            ],
            "description": "Provider message, for display. Null when the provider returned none."
          },
          "account_name": {
            "type": "string",
            "description": "Registered wallet holder name. Always present on a 2xx collection response, because the name lookup must succeed before any debit is attempted. Absent on the 422 `account_not_found` response."
          },
          "subscriber_number": {
            "type": "string",
            "description": "Local `0XXXXXXXXX` form."
          },
          "gross_amount": {
            "type": "number"
          },
          "gross_amount_minor": {
            "type": "integer",
            "description": "Exact `gross_amount` in pesewas."
          },
          "fee_amount": {
            "type": "number",
            "description": "Platform fee. `0` unless `status` is `approved`."
          },
          "fee_amount_minor": {
            "type": "integer",
            "description": "Exact `fee_amount` in pesewas."
          },
          "net_amount": {
            "type": "number",
            "description": "`gross_amount - fee_amount`. Equals gross unless `status` is `approved`."
          },
          "net_amount_minor": {
            "type": "integer",
            "description": "Exact `net_amount` in pesewas."
          },
          "split_status": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "pending",
              "settled",
              "reversed",
              null
            ],
            "description": "Present when the charge carried a split instruction."
          },
          "splits": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SplitLeg"
            },
            "description": "Split legs, when a split instruction was attached."
          },
          "currency": {
            "$ref": "#/components/schemas/Currency"
          },
          "test_mode": {
            "type": "string",
            "enum": [
              "simulated",
              "live_rails"
            ],
            "description": "Test-mode responses only."
          },
          "test_moves_real_money": {
            "type": "boolean",
            "description": "Test-mode responses only."
          },
          "test_max_amount": {
            "type": "number",
            "description": "Test-mode responses only."
          },
          "upstream": {
            "type": "string",
            "enum": [
              "simulator",
              "360pay",
              "junipay"
            ],
            "description": "Test-mode responses only."
          }
        },
        "additionalProperties": false
      },
      "Transaction": {
        "type": "object",
        "description": "A transaction as returned by the list endpoint.",
        "required": [
          "provider_transaction_id",
          "mode",
          "type",
          "channel",
          "gross_amount",
          "fee_amount",
          "net_amount",
          "currency",
          "status",
          "created_at"
        ],
        "properties": {
          "provider_transaction_id": {
            "type": "string",
            "description": "Our 12-digit reference — the same value returned as `transaction_id` when the charge was created."
          },
          "mode": {
            "$ref": "#/components/schemas/Mode"
          },
          "type": {
            "type": "string",
            "enum": [
              "collection",
              "payout"
            ]
          },
          "channel": {
            "type": "string",
            "enum": [
              "momo",
              "card"
            ]
          },
          "subscriber_number": {
            "type": [
              "string",
              "null"
            ]
          },
          "account_number": {
            "type": [
              "string",
              "null"
            ],
            "description": "Bank account for payout rows. Null for collections."
          },
          "r_switch": {
            "type": [
              "string",
              "null"
            ],
            "description": "Mobile money network for momo rows."
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "customer_email": {
            "type": [
              "string",
              "null"
            ]
          },
          "gross_amount": {
            "type": "number"
          },
          "gross_amount_minor": {
            "type": "integer"
          },
          "fee_amount": {
            "type": "number"
          },
          "fee_amount_minor": {
            "type": "integer"
          },
          "net_amount": {
            "type": "number"
          },
          "net_amount_minor": {
            "type": "integer"
          },
          "split_status": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "pending",
              "settled",
              "reversed",
              null
            ]
          },
          "currency": {
            "$ref": "#/components/schemas/Currency"
          },
          "status": {
            "$ref": "#/components/schemas/TransactionStatus"
          },
          "provider_code": {
            "type": [
              "string",
              "null"
            ],
            "description": "Always stringified when present."
          },
          "provider_reason": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "additionalProperties": false
      },
      "TransactionList": {
        "type": "object",
        "required": [
          "items",
          "next_cursor",
          "limit"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Transaction"
            }
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque cursor encoding `(created_at, id)` of the last row, so pagination is stable when rows share a timestamp. Pass it back verbatim as `cursor`; do not parse or construct it. Null on the last page. Legacy ISO-8601 timestamp cursors are still accepted."
          },
          "limit": {
            "type": "integer",
            "minimum": 1,
            "maximum": 100
          }
        },
        "additionalProperties": false
      },
      "TransactionStatusResponse": {
        "type": "object",
        "required": [
          "transaction_id",
          "provider_transaction_id",
          "code",
          "reason_code",
          "reason",
          "status",
          "resolved_status",
          "gross_amount",
          "fee_amount",
          "net_amount",
          "currency",
          "created_at"
        ],
        "properties": {
          "transaction_id": {
            "type": "string",
            "pattern": "^\\d{12}$"
          },
          "reference": {
            "type": [
              "string",
              "null"
            ],
            "description": "Your `reference` from the collection or checkout session."
          },
          "metadata": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true,
            "description": "Your `metadata` from the collection or checkout session."
          },
          "checkout_session_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Checkout session token (`cs_…`) when the payment came from Hosted Checkout."
          },
          "currency": {
            "$ref": "#/components/schemas/Currency"
          },
          "provider_transaction_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "code": {
            "type": [
              "string",
              "null"
            ],
            "description": "Upstream provider numeric status code (00/01/02/03) or null. Never a word."
          },
          "reason_code": {
            "type": "string",
            "enum": [
              "approved",
              "pending",
              "prompt_sent",
              "prompt_expired",
              "insufficient_funds",
              "declined",
              "cancelled",
              "reversed",
              "failed",
              "settled_in_error",
              "upstream_error",
              "unsupported_network",
              "unsupported_bank"
            ],
            "description": "Machine-readable outcome. Switch on this, not on `reason`."
          },
          "reason": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "$ref": "#/components/schemas/TransactionStatus"
          },
          "resolved_status": {
            "$ref": "#/components/schemas/TransactionStatus",
            "description": "Status after a live provider check. Identical to `status`; kept for backwards compatibility. Only ever `approved` once the provider has returned an explicit terminal success — a request acknowledgement never approves a charge."
          },
          "subscriber_number": {
            "type": [
              "string",
              "null"
            ]
          },
          "gross_amount": {
            "type": "number"
          },
          "fee_amount": {
            "type": "number"
          },
          "net_amount": {
            "type": "number"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "settled_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "reversed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "corrected_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Set only when a transaction reported `approved` in error was put right. Null otherwise."
          },
          "correction": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Correction"
              },
              {
                "type": "null"
              }
            ],
            "description": "Non-null only on a corrected transaction."
          }
        },
        "additionalProperties": false
      },
      "CheckoutSessionRequest": {
        "type": "object",
        "required": [
          "amount"
        ],
        "properties": {
          "amount": {
            "type": "number",
            "exclusiveMinimum": 0,
            "maximum": 1000000
          },
          "currency": {
            "$ref": "#/components/schemas/Currency"
          },
          "desc": {
            "type": "string",
            "maxLength": 100,
            "default": "Checkout"
          },
          "description": {
            "type": "string",
            "maxLength": 100,
            "description": "Alias of `desc`."
          },
          "channel": {
            "type": "string",
            "enum": [
              "ANY",
              "MOMO",
              "CARD"
            ],
            "default": "ANY",
            "description": "Which payment methods the hosted page offers."
          },
          "network": {
            "$ref": "#/components/schemas/Network"
          },
          "subscriber_number": {
            "type": "string",
            "description": "Pre-fills the wallet number on the hosted page."
          },
          "customer_name": {
            "type": "string",
            "maxLength": 120
          },
          "customer_email": {
            "type": "string",
            "maxLength": 160
          },
          "brand_id": {
            "type": "string",
            "format": "uuid",
            "description": "Render the page under one of your brands."
          },
          "line_items": {
            "type": "array",
            "description": "Optional itemised breakdown shown on the page.",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string"
                },
                "quantity": {
                  "type": "integer",
                  "minimum": 1
                },
                "amount": {
                  "type": "number"
                }
              }
            }
          },
          "billing": {
            "type": "object",
            "additionalProperties": true,
            "description": "Optional billing address collected up front."
          },
          "success_url": {
            "type": "string",
            "format": "uri",
            "description": "https URL the customer returns to after paying."
          },
          "cancel_url": {
            "type": "string",
            "format": "uri"
          },
          "country": {
            "type": "string",
            "description": "Optional ISO country. Must be your business's registered country, otherwise `422 currency_not_enabled`."
          },
          "reference": {
            "type": "string",
            "maxLength": 120,
            "description": "Your order reference. Returned on the session, the transaction and collection.* webhooks."
          },
          "metadata": {
            "type": "object",
            "additionalProperties": {
              "type": [
                "string",
                "number",
                "boolean"
              ]
            },
            "maxProperties": 20,
            "description": "Up to 20 flat keys, 2 KB. Echoed back on retrieval and on collection.* webhooks."
          }
        },
        "additionalProperties": false
      },
      "CheckoutSession": {
        "type": "object",
        "required": [
          "id",
          "status",
          "checkout_url",
          "gross_amount",
          "currency",
          "expires_at",
          "mode"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Session token, prefixed `cs_`. Use it to retrieve the session."
          },
          "status": {
            "type": "string",
            "enum": [
              "open",
              "processing",
              "paid",
              "failed",
              "expired",
              "cancelled"
            ]
          },
          "checkout_url": {
            "type": "string",
            "format": "uri",
            "description": "Send the customer here."
          },
          "access_code": {
            "type": "string",
            "description": "Same value as `id`."
          },
          "gross_amount": {
            "type": "number"
          },
          "currency": {
            "$ref": "#/components/schemas/Currency"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "mode": {
            "$ref": "#/components/schemas/Mode"
          }
        },
        "additionalProperties": true
      },
      "Health": {
        "type": "object",
        "required": [
          "ok",
          "service",
          "request_id"
        ],
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "service": {
            "type": "string",
            "enum": [
              "webrabbit-api"
            ]
          },
          "request_id": {
            "type": "string",
            "format": "uuid"
          }
        },
        "additionalProperties": false
      },
      "SmsSendRequest": {
        "type": "object",
        "required": [
          "message"
        ],
        "description": "Provide at least one of `recipients` or `group_ids`. `business_id` and `mode` are optional when the call is authenticated with a Messaging API key — the key carries both. They are required for dashboard-session calls, and a value that disagrees with the key is rejected.",
        "properties": {
          "business_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Optional for API-key calls; derived from the key."
          },
          "mode": {
            "$ref": "#/components/schemas/Mode"
          },
          "name": {
            "type": "string",
            "description": "Campaign label. Defaults to a timestamped name."
          },
          "sender": {
            "type": "string",
            "pattern": "^[A-Za-z0-9 ]{3,11}$",
            "description": "Approved sender name. Use `WRB` if you have no approved sender of your own."
          },
          "message": {
            "type": "string",
            "minLength": 1,
            "maxLength": 1600
          },
          "recipients": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "maxItems": 10000
          },
          "group_ids": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "schedule_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Hold the campaign until this time. Handed to the network scheduler when it accepts it, otherwise held here."
          },
          "timezone_id": {
            "type": [
              "string",
              "number"
            ],
            "description": "Network time-zone id used with `schedule_at`. Ids come from GET /v1/messaging/reference?kind=timezone."
          },
          "validity_seconds": {
            "type": "integer",
            "minimum": 60,
            "maximum": 259200,
            "description": "How long the network keeps retrying an unreachable handset before giving up."
          },
          "dlt_entity_id": {
            "type": [
              "string",
              "number"
            ],
            "description": "Regulator entity id, for markets that require registration (for example India DLT)."
          },
          "dlt_template_id": {
            "type": [
              "string",
              "number"
            ],
            "description": "Regulator template id."
          },
          "tlv": {
            "type": "array",
            "description": "Regulator TLV tags passed straight through to the network.",
            "items": {
              "type": "object",
              "required": [
                "tag",
                "tagValue"
              ],
              "properties": {
                "tag": {
                  "type": "integer"
                },
                "tagValue": {
                  "type": "string"
                }
              }
            }
          }
        },
        "additionalProperties": false
      },
      "SmsSendResponse": {
        "type": "object",
        "required": [
          "ok",
          "campaign_id"
        ],
        "properties": {
          "ok": {
            "type": "boolean",
            "enum": [
              true
            ]
          },
          "campaign_id": {
            "type": "string",
            "format": "uuid"
          },
          "provider_campaign_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Absent on simulated and scheduled sends."
          },
          "simulated": {
            "type": "boolean",
            "description": "Present and true for test-mode sends. Nothing leaves the platform."
          },
          "scheduled_at": {
            "type": "string",
            "format": "date-time",
            "description": "Present only for a scheduled campaign."
          },
          "scheduled_by": {
            "type": "string",
            "enum": [
              "network",
              "webrabbit"
            ],
            "description": "Who holds a scheduled campaign until it falls due."
          },
          "cost": {
            "type": "number",
            "description": "Credits debited, after refunds for rejected recipients. Priced per destination country."
          },
          "breakdown": {
            "type": "array",
            "description": "Cost grouped by the destination country each recipient belongs to.",
            "items": {
              "type": "object",
              "required": [
                "country",
                "iso2",
                "recipients",
                "rate_per_segment",
                "cost"
              ],
              "properties": {
                "country": {
                  "type": "string",
                  "example": "Ghana"
                },
                "iso2": {
                  "type": "string",
                  "example": "GH"
                },
                "recipients": {
                  "type": "integer",
                  "example": 10
                },
                "rate_per_segment": {
                  "type": "number",
                  "example": 0.2
                },
                "cost": {
                  "type": "number",
                  "example": 2
                }
              },
              "additionalProperties": false
            }
          },
          "recipients": {
            "type": "integer",
            "description": "Recipients actually submitted."
          },
          "rejected": {
            "type": "integer",
            "description": "Recipients the provider refused. Refunded. Absent on simulated/scheduled sends."
          },
          "credit_left": {
            "type": "number",
            "description": "Wallet balance after the send. Absent on simulated/scheduled sends."
          }
        },
        "additionalProperties": false
      },
      "SmsStatusRequest": {
        "type": "object",
        "required": [
          "campaign_id"
        ],
        "properties": {
          "campaign_id": {
            "type": "string",
            "format": "uuid"
          }
        },
        "additionalProperties": false
      },
      "SmsStatusResponse": {
        "type": "object",
        "required": [
          "ok",
          "updated"
        ],
        "properties": {
          "ok": {
            "type": "boolean",
            "enum": [
              true
            ]
          },
          "updated": {
            "type": "integer",
            "description": "Number of message rows whose delivery state changed."
          },
          "simulated": {
            "type": "boolean",
            "description": "Present and true for simulated campaigns."
          }
        },
        "additionalProperties": true
      },
      "OtpSendRequest": {
        "type": "object",
        "required": [
          "phone"
        ],
        "description": "`business_id` and `mode` are optional for API-key calls — the key carries both.",
        "properties": {
          "action": {
            "type": "string",
            "enum": [
              "send"
            ],
            "default": "send"
          },
          "business_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "mode": {
            "$ref": "#/components/schemas/Mode"
          },
          "phone": {
            "type": "string",
            "description": "Ghanaian number in local or international form."
          },
          "code_length": {
            "type": "integer",
            "minimum": 4,
            "maximum": 8,
            "description": "Per-call digit count. Defaults to the purpose preset, then your workspace default (6)."
          },
          "template": {
            "type": "string",
            "maxLength": 320,
            "description": "Per-call wording. Must contain {code}; {minutes} is substituted with the expiry."
          },
          "purpose": {
            "type": "string",
            "enum": [
              "login",
              "payment",
              "pin_reset",
              "custom"
            ],
            "default": "custom",
            "description": "Selects the saved preset (wording, length, expiry) for this kind of code and is recorded on the request for reporting."
          }
        },
        "additionalProperties": false
      },
      "OtpSendResponse": {
        "type": "object",
        "required": [
          "ok",
          "request_id",
          "expires_at"
        ],
        "properties": {
          "ok": {
            "type": "boolean",
            "enum": [
              true
            ]
          },
          "request_id": {
            "type": "string",
            "format": "uuid",
            "description": "Pass back when verifying."
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "simulated": {
            "type": "boolean",
            "description": "Test mode only."
          },
          "test_code": {
            "type": "string",
            "description": "Test mode only — the code that would have been sent."
          },
          "purpose": {
            "type": "string",
            "enum": [
              "login",
              "payment",
              "pin_reset",
              "custom"
            ]
          },
          "code_length": {
            "type": "integer",
            "description": "Digits actually issued."
          },
          "cost": {
            "type": "number",
            "description": "Credits debited."
          }
        },
        "additionalProperties": false
      },
      "OtpVerifyRequest": {
        "type": "object",
        "required": [
          "action",
          "request_id",
          "code"
        ],
        "properties": {
          "action": {
            "type": "string",
            "enum": [
              "verify"
            ]
          },
          "request_id": {
            "type": "string",
            "format": "uuid"
          },
          "code": {
            "type": "string"
          }
        },
        "additionalProperties": false
      },
      "OtpVerifyResponse": {
        "type": "object",
        "required": [
          "ok",
          "status"
        ],
        "properties": {
          "ok": {
            "type": "boolean",
            "enum": [
              true
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "verified"
            ]
          },
          "already_verified": {
            "type": "boolean",
            "description": "True when this code had been verified by an earlier call. Verifying twice is safe."
          },
          "request_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Edge request id."
          }
        },
        "additionalProperties": false
      },
      "OtpError": {
        "type": "object",
        "required": [
          "error"
        ],
        "description": "Verification failures are enumerated so a client can branch on them without string matching.",
        "properties": {
          "error": {
            "type": "string",
            "enum": [
              "invalid_code",
              "otp_expired",
              "too_many_attempts",
              "not_found"
            ]
          },
          "message": {
            "type": "string"
          },
          "retryable": {
            "type": "boolean",
            "description": "True only when another attempt on the same request_id can still succeed."
          },
          "attempts_remaining": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Attempts left on this request_id (5 per code)."
          },
          "request_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          }
        },
        "additionalProperties": true
      },
      "MessagingBalanceRequest": {
        "type": "object",
        "required": [],
        "properties": {
          "business_id": {
            "type": "string",
            "format": "uuid"
          },
          "mode": {
            "$ref": "#/components/schemas/Mode"
          }
        },
        "additionalProperties": false
      },
      "MessagingBalanceResponse": {
        "type": "object",
        "required": [
          "ok",
          "provider",
          "wallet",
          "sms",
          "voice"
        ],
        "properties": {
          "ok": {
            "type": "boolean",
            "enum": [
              true
            ]
          },
          "provider": {
            "type": "string",
            "enum": [
              "smsala"
            ]
          },
          "wallet": {
            "type": "object",
            "required": [
              "mode",
              "balance"
            ],
            "properties": {
              "mode": {
                "$ref": "#/components/schemas/Mode"
              },
              "balance": {
                "type": "number",
                "description": "Prepaid messaging credits in GHS."
              }
            }
          },
          "sms": {
            "type": "object",
            "required": [
              "supported"
            ],
            "properties": {
              "supported": {
                "type": "boolean",
                "description": "False — the upstream network publishes no account balance."
              }
            }
          },
          "voice": {
            "type": "object",
            "required": [
              "supported"
            ],
            "properties": {
              "supported": {
                "type": "boolean"
              }
            }
          }
        },
        "additionalProperties": false
      },
      "WebhookEnvelope": {
        "type": "object",
        "required": [
          "id",
          "type",
          "mode",
          "created_at",
          "data"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Stable event id, prefixed `evt_`. De-duplicate on this."
          },
          "type": {
            "$ref": "#/components/schemas/WebhookEventType"
          },
          "mode": {
            "$ref": "#/components/schemas/Mode"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "data": {
            "type": "object",
            "description": "`data` is the envelope, `data.object` is the resource. Read the transaction from `data.object` — not from `data`. This differs from GET /v1/transactions/{transaction_id}, which returns the transaction flat at the top level.",
            "required": [
              "object",
              "resource_type",
              "resource_id"
            ],
            "properties": {
              "object": {
                "type": "object",
                "additionalProperties": true,
                "description": "Event-specific payload — see the per-event schemas."
              },
              "resource_type": {
                "type": "string",
                "enum": [
                  "transaction",
                  "payout",
                  "sms_topup",
                  "message"
                ]
              },
              "resource_id": {
                "type": "string"
              }
            }
          }
        },
        "additionalProperties": false
      },
      "SplitInstruction": {
        "type": "object",
        "description": "Divide the collection between your platform and sub-accounts at capture. Legs are computed in integer pesewas; any rounding remainder goes to the largest leg.",
        "required": [
          "recipients"
        ],
        "properties": {
          "fee_bearer": {
            "type": "string",
            "enum": [
              "merchant",
              "recipients"
            ],
            "default": "merchant",
            "description": "`merchant` — the Web Rabbit fee comes out of your share. `recipients` — the fee is taken off the top before percentages apply."
          },
          "recipients": {
            "type": "array",
            "minItems": 1,
            "maxItems": 20,
            "items": {
              "type": "object",
              "required": [
                "sub_account"
              ],
              "properties": {
                "sub_account": {
                  "type": "string",
                  "description": "Sub-account reference or id, in the same mode as the key."
                },
                "percentage": {
                  "type": "number",
                  "minimum": 0,
                  "maximum": 100,
                  "description": "Share of the split base. Use this or `amount`/`amount_minor`."
                },
                "amount": {
                  "type": "number",
                  "exclusiveMinimum": 0,
                  "description": "Fixed leg in GHS."
                },
                "amount_minor": {
                  "type": "integer",
                  "minimum": 1,
                  "description": "Fixed leg in pesewas."
                }
              },
              "additionalProperties": false
            }
          }
        },
        "additionalProperties": false
      },
      "Correction": {
        "type": "object",
        "description": "Present on a transaction that was reported `approved` from a provider acknowledgement and has since been put right. The payment was never collected, so the transaction moves to `failed` — this is not a reversal, and `reversed_at` stays null. If you credited a customer on the earlier approval, unwind it.",
        "required": [
          "previously_reported_status",
          "corrected_at"
        ],
        "properties": {
          "previously_reported_status": {
            "type": "string",
            "enum": [
              "approved"
            ]
          },
          "corrected_at": {
            "type": "string",
            "format": "date-time"
          },
          "detail": {
            "type": "string"
          }
        },
        "additionalProperties": true
      },
      "SplitLeg": {
        "type": "object",
        "required": [
          "sub_account",
          "amount",
          "amount_minor",
          "status"
        ],
        "properties": {
          "sub_account": {
            "type": "string",
            "description": "Sub-account reference."
          },
          "type": {
            "type": "string",
            "enum": [
              "percentage",
              "fixed"
            ]
          },
          "value": {
            "type": "number",
            "description": "The percentage or fixed value that produced this leg."
          },
          "amount": {
            "type": "number"
          },
          "amount_minor": {
            "type": "integer"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "settled",
              "reversed"
            ]
          },
          "settled_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "reversed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        },
        "additionalProperties": false
      },
      "SubAccount": {
        "type": "object",
        "required": [
          "id",
          "reference",
          "name",
          "status",
          "settlement_method",
          "mode",
          "balance",
          "balance_minor",
          "currency"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "reference": {
            "type": "string",
            "description": "Your stable id for the recipient. Unique per business and mode."
          },
          "name": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "unverified",
              "verified",
              "disabled"
            ]
          },
          "settlement_method": {
            "type": "string",
            "enum": [
              "momo",
              "bank"
            ]
          },
          "account_number": {
            "type": [
              "string",
              "null"
            ]
          },
          "account_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "network": {
            "type": [
              "string",
              "null"
            ]
          },
          "bank_code": {
            "type": [
              "string",
              "null"
            ]
          },
          "mode": {
            "$ref": "#/components/schemas/Mode"
          },
          "balance": {
            "type": "number"
          },
          "balance_minor": {
            "type": "integer"
          },
          "currency": {
            "$ref": "#/components/schemas/Currency"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "additionalProperties": false
      },
      "SubAccountCreateRequest": {
        "type": "object",
        "required": [
          "name",
          "account_number"
        ],
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 120
          },
          "reference": {
            "type": "string",
            "description": "Letters, digits, `-` and `_`. Derived from `name` when omitted."
          },
          "settlement_method": {
            "type": "string",
            "enum": [
              "momo",
              "bank"
            ],
            "default": "momo"
          },
          "account_number": {
            "type": "string"
          },
          "account_name": {
            "type": "string"
          },
          "network": {
            "$ref": "#/components/schemas/Network"
          },
          "bank_code": {
            "type": "string",
            "description": "Required when `settlement_method` is `bank`."
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true
          }
        },
        "additionalProperties": false
      },
      "SubAccountUpdateRequest": {
        "type": "object",
        "description": "Changing `account_number` or `network` resets `status` to `unverified`.",
        "properties": {
          "name": {
            "type": "string"
          },
          "account_name": {
            "type": "string"
          },
          "account_number": {
            "type": "string"
          },
          "network": {
            "$ref": "#/components/schemas/Network"
          },
          "status": {
            "type": "string",
            "enum": [
              "disabled",
              "unverified"
            ],
            "description": "Only Web Rabbit sets `verified`."
          }
        },
        "additionalProperties": false
      },
      "SubAccountList": {
        "type": "object",
        "required": [
          "items",
          "count",
          "mode"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SubAccount"
            }
          },
          "count": {
            "type": "integer"
          },
          "mode": {
            "$ref": "#/components/schemas/Mode"
          }
        },
        "additionalProperties": false
      },
      "SubAccountLedger": {
        "type": "object",
        "required": [
          "sub_account",
          "items"
        ],
        "properties": {
          "sub_account": {
            "type": "string"
          },
          "items": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "id",
                "entry_type",
                "amount",
                "amount_minor",
                "created_at"
              ],
              "properties": {
                "id": {
                  "type": "string",
                  "format": "uuid"
                },
                "entry_type": {
                  "type": "string",
                  "enum": [
                    "credit",
                    "reversal"
                  ]
                },
                "amount": {
                  "type": "number"
                },
                "amount_minor": {
                  "type": "integer"
                },
                "balance_after": {
                  "type": "number"
                },
                "balance_after_minor": {
                  "type": "integer"
                },
                "currency": {
                  "$ref": "#/components/schemas/Currency"
                },
                "transaction_id": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "split_id": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "description": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "created_at": {
                  "type": "string",
                  "format": "date-time"
                }
              },
              "additionalProperties": true
            }
          }
        },
        "additionalProperties": false
      },
      "SplitPreviewRequest": {
        "type": "object",
        "required": [
          "split"
        ],
        "description": "Send exactly one of `amount` or `amount_minor`.",
        "properties": {
          "amount": {
            "type": "number",
            "exclusiveMinimum": 0
          },
          "amount_minor": {
            "type": "integer",
            "minimum": 1
          },
          "split": {
            "$ref": "#/components/schemas/SplitInstruction"
          }
        },
        "additionalProperties": false
      },
      "SplitPreview": {
        "type": "object",
        "required": [
          "currency",
          "gross_amount_minor",
          "fee_amount_minor",
          "net_amount_minor",
          "recipients"
        ],
        "properties": {
          "currency": {
            "$ref": "#/components/schemas/Currency"
          },
          "gross_amount": {
            "type": "number"
          },
          "gross_amount_minor": {
            "type": "integer"
          },
          "fee_amount": {
            "type": "number"
          },
          "fee_amount_minor": {
            "type": "integer"
          },
          "net_amount": {
            "type": "number"
          },
          "net_amount_minor": {
            "type": "integer"
          },
          "fee_bearer": {
            "type": "string",
            "enum": [
              "merchant",
              "recipients"
            ]
          },
          "merchant_amount": {
            "type": "number"
          },
          "merchant_amount_minor": {
            "type": "integer"
          },
          "recipients": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "sub_account",
                "amount_minor"
              ],
              "properties": {
                "sub_account": {
                  "type": "string"
                },
                "type": {
                  "type": "string",
                  "enum": [
                    "percentage",
                    "fixed"
                  ]
                },
                "value": {
                  "type": "number"
                },
                "amount": {
                  "type": "number"
                },
                "amount_minor": {
                  "type": "integer"
                }
              },
              "additionalProperties": false
            }
          }
        },
        "additionalProperties": false
      },
      "SplitEvent": {
        "type": "object",
        "required": [
          "transaction_id",
          "split_status",
          "currency",
          "splits"
        ],
        "properties": {
          "transaction_id": {
            "type": "string"
          },
          "status": {
            "$ref": "#/components/schemas/TransactionStatus"
          },
          "split_status": {
            "type": "string",
            "enum": [
              "settled",
              "reversed"
            ]
          },
          "gross_amount": {
            "type": "number"
          },
          "gross_amount_minor": {
            "type": "integer"
          },
          "fee_amount": {
            "type": "number"
          },
          "fee_amount_minor": {
            "type": "integer"
          },
          "net_amount": {
            "type": "number"
          },
          "net_amount_minor": {
            "type": "integer"
          },
          "merchant_amount_minor": {
            "type": "integer"
          },
          "currency": {
            "$ref": "#/components/schemas/Currency"
          },
          "splits": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SplitLeg"
            }
          },
          "settled_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "reversed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "additionalProperties": true
      },
      "DisbursementRecipient": {
        "type": "object",
        "description": "One transfer inside a disbursement batch.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "reference": {
            "type": "string",
            "description": "Your reference, or one we generate."
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "processing",
              "succeeded",
              "failed",
              "held",
              "reversed",
              "cancelled"
            ]
          },
          "name": {
            "type": "string"
          },
          "resolved_name": {
            "type": "string",
            "nullable": true,
            "description": "Account name as the bank or wallet reports it."
          },
          "account_number": {
            "type": "string"
          },
          "network": {
            "type": "string",
            "nullable": true,
            "enum": [
              "MTN",
              "TELECEL",
              "AT",
              "GMONEY",
              null
            ]
          },
          "bank_code": {
            "type": "string",
            "nullable": true
          },
          "amount": {
            "type": "number"
          },
          "amount_minor": {
            "type": "integer"
          },
          "fee": {
            "type": "number"
          },
          "provider_reference": {
            "type": "string",
            "nullable": true
          },
          "provider_code": {
            "type": "string",
            "nullable": true
          },
          "failure_reason": {
            "type": "string",
            "nullable": true
          }
        },
        "required": [
          "id",
          "reference",
          "status",
          "name",
          "account_number",
          "amount",
          "amount_minor",
          "fee"
        ]
      },
      "Disbursement": {
        "type": "object",
        "description": "A batch of transfers out of your settled balance to third parties.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "object": {
            "type": "string",
            "enum": [
              "disbursement"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "pending_review",
              "approved",
              "declined",
              "processing",
              "completed",
              "partially_completed",
              "failed",
              "held",
              "cancelled"
            ]
          },
          "mode": {
            "type": "string",
            "enum": [
              "test",
              "live"
            ]
          },
          "channel": {
            "type": "string",
            "enum": [
              "momo",
              "bank"
            ]
          },
          "tier": {
            "type": "string",
            "enum": [
              "individual",
              "business",
              "powered"
            ]
          },
          "currency": {
            "type": "string",
            "enum": [
              "GHS"
            ]
          },
          "amount": {
            "type": "number"
          },
          "amount_minor": {
            "type": "integer"
          },
          "fee": {
            "type": "number"
          },
          "item_count": {
            "type": "integer"
          },
          "reference": {
            "type": "string",
            "nullable": true
          },
          "scheduled_for": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "released_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "reviewed_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "decision_reason": {
            "type": "string",
            "nullable": true
          },
          "hold_reason": {
            "type": "string",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "recipients": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DisbursementRecipient"
            }
          }
        },
        "required": [
          "id",
          "object",
          "status",
          "mode",
          "channel",
          "tier",
          "currency",
          "amount",
          "amount_minor",
          "fee",
          "item_count",
          "created_at",
          "recipients"
        ]
      },
      "DisbursementCreateRequest": {
        "type": "object",
        "properties": {
          "channel": {
            "type": "string",
            "enum": [
              "momo",
              "bank"
            ],
            "default": "momo"
          },
          "reference": {
            "type": "string",
            "nullable": true
          },
          "idempotency_key": {
            "type": "string",
            "nullable": true,
            "description": "Also accepted as the Idempotency-Key header. A retry returns the original batch."
          },
          "recipients": {
            "type": "array",
            "minItems": 1,
            "maxItems": 500,
            "items": {
              "type": "object",
              "properties": {
                "reference": {
                  "type": "string",
                  "nullable": true
                },
                "name": {
                  "type": "string"
                },
                "msisdn": {
                  "type": "string",
                  "nullable": true,
                  "description": "Wallet number for channel=momo."
                },
                "network": {
                  "type": "string",
                  "nullable": true,
                  "enum": [
                    "MTN",
                    "TELECEL",
                    "AT",
                    "GMONEY",
                    null
                  ]
                },
                "account_number": {
                  "type": "string",
                  "nullable": true,
                  "description": "Bank account for channel=bank."
                },
                "bank_code": {
                  "type": "string",
                  "nullable": true
                },
                "amount": {
                  "type": "number",
                  "nullable": true,
                  "description": "GHS. Send this or amount_minor."
                },
                "amount_minor": {
                  "type": "integer",
                  "nullable": true,
                  "description": "Pesewas. Exact — preferred for machine-to-machine."
                },
                "narration": {
                  "type": "string",
                  "nullable": true
                },
                "idempotency_key": {
                  "type": "string",
                  "nullable": true
                }
              },
              "required": [
                "name"
              ]
            }
          }
        },
        "required": [
          "recipients"
        ]
      },
      "DisbursementList": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "enum": [
              "list"
            ]
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Disbursement"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": "string",
            "nullable": true
          },
          "tier": {
            "type": "string",
            "enum": [
              "individual",
              "business",
              "powered"
            ]
          },
          "disbursements_enabled": {
            "type": "boolean"
          }
        },
        "required": [
          "object",
          "data",
          "has_more"
        ]
      },
      "Refund": {
        "type": "object",
        "description": "A refund is an outbound transfer back to the wallet that paid. Neither rail exposes a reversal endpoint, so the original charge is not undone — money is sent back and linked to the payment.",
        "required": [
          "refund_id",
          "transaction_id",
          "status",
          "type",
          "amount",
          "currency",
          "created_at"
        ],
        "properties": {
          "refund_id": {
            "type": "string"
          },
          "transaction_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "Reference of the collection being refunded."
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "processing",
              "review",
              "succeeded",
              "failed",
              "cancelled"
            ]
          },
          "type": {
            "type": "string",
            "enum": [
              "full",
              "partial"
            ]
          },
          "amount": {
            "type": "number"
          },
          "amount_minor": {
            "type": "integer"
          },
          "fee_amount": {
            "type": "number",
            "description": "Refund charge taken from your balance. The original platform commission is not returned."
          },
          "fee_amount_minor": {
            "type": "integer"
          },
          "currency": {
            "type": "string"
          },
          "destination": {
            "type": [
              "string",
              "null"
            ]
          },
          "network": {
            "type": [
              "string",
              "null"
            ]
          },
          "reason": {
            "type": [
              "string",
              "null"
            ],
            "description": "Merchant-supplied reason."
          },
          "provider_reason": {
            "type": [
              "string",
              "null"
            ]
          },
          "reason_code": {
            "type": [
              "string",
              "null"
            ]
          },
          "code": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": "2026-09-08T10:22:31.104Z",
          "completed_at": {
            "0": "2",
            "1": "0",
            "2": "2",
            "3": "6",
            "4": "-",
            "5": "0",
            "6": "9",
            "7": "-",
            "8": "0",
            "9": "8",
            "10": "T",
            "11": "1",
            "12": "0",
            "13": ":",
            "14": "2",
            "15": "2",
            "16": ":",
            "17": "3",
            "18": "1",
            "19": ".",
            "20": "1",
            "21": "0",
            "22": "4",
            "23": "Z",
            "nullable": true
          }
        }
      },
      "WebhookEventType": {
        "type": "string",
        "description": "Exhaustive list of event types we send today. New types are announced in the API changelog before they ship.",
        "enum": [
          "collection.approved",
          "collection.failed",
          "collection.reversed",
          "refund.created",
          "refund.succeeded",
          "refund.failed",
          "refund.reversed",
          "split.settled",
          "split.reversed",
          "payout.queued",
          "payout.completed",
          "payout.failed",
          "bill.completed",
          "bill.failed",
          "sms_topup.approved",
          "message.delivered",
          "message.failed",
          "message.rejected",
          "order.paid",
          "order.failed",
          "order.fulfilled",
          "inventory.low",
          "invoice.paid",
          "invoice.partially_paid"
        ]
      },
      "CommerceOrderEvent": {
        "type": "object",
        "description": "A product or storefront order.",
        "properties": {
          "id": {
            "type": "string"
          },
          "order_number": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "fulfilment_status": {
            "type": "string"
          },
          "total": {
            "type": "number"
          },
          "currency": {
            "type": "string"
          },
          "customer_email": {
            "type": "string",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "InventoryLow": {
        "type": "object",
        "description": "A limited-stock product fell to or below its low-stock threshold.",
        "properties": {
          "product_id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "stock": {
            "type": "integer"
          },
          "threshold": {
            "type": "integer"
          }
        }
      },
      "InvoiceEvent": {
        "type": "object",
        "description": "An invoice that received a payment.",
        "properties": {
          "id": {
            "type": "string"
          },
          "invoice_number": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "total": {
            "type": "number"
          },
          "amount_paid": {
            "type": "number"
          },
          "currency": {
            "type": "string"
          },
          "paid_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CollectionApproved": {
        "type": "object",
        "required": [
          "transaction_id",
          "status",
          "gross_amount",
          "fee_amount",
          "net_amount",
          "currency",
          "created_at"
        ],
        "properties": {
          "transaction_id": {
            "type": "string"
          },
          "provider_transaction_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "approved"
            ]
          },
          "code": {
            "type": [
              "string",
              "null"
            ]
          },
          "reason": {
            "type": [
              "string",
              "null"
            ]
          },
          "subscriber_number": {
            "type": [
              "string",
              "null"
            ]
          },
          "channel": {
            "type": "string",
            "enum": [
              "momo",
              "card"
            ]
          },
          "gross_amount": {
            "type": "number"
          },
          "fee_amount": {
            "type": "number"
          },
          "provider_fee": {
            "type": [
              "number",
              "null"
            ]
          },
          "net_amount": {
            "type": "number"
          },
          "currency": {
            "$ref": "#/components/schemas/Currency"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "additionalProperties": false
      },
      "CollectionFailed": {
        "type": "object",
        "required": [
          "transaction_id",
          "status",
          "gross_amount",
          "currency",
          "created_at"
        ],
        "properties": {
          "transaction_id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "failed"
            ]
          },
          "code": {
            "type": [
              "string",
              "null"
            ]
          },
          "reason": {
            "type": [
              "string",
              "null"
            ]
          },
          "subscriber_number": {
            "type": [
              "string",
              "null"
            ]
          },
          "channel": {
            "type": "string",
            "enum": [
              "momo",
              "card"
            ]
          },
          "gross_amount": {
            "type": "number"
          },
          "currency": {
            "$ref": "#/components/schemas/Currency"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "additionalProperties": false
      },
      "CollectionReversed": {
        "type": "object",
        "required": [
          "transaction_id",
          "status",
          "gross_amount",
          "fee_amount",
          "net_amount",
          "currency",
          "reversed_at"
        ],
        "properties": {
          "transaction_id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "reversed"
            ]
          },
          "reason": {
            "type": [
              "string",
              "null"
            ]
          },
          "gross_amount": {
            "type": "number"
          },
          "fee_amount": {
            "type": "number",
            "description": "The fee that was charged and has now been unwound."
          },
          "net_amount": {
            "type": "number"
          },
          "currency": {
            "$ref": "#/components/schemas/Currency"
          },
          "reversed_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "additionalProperties": false
      },
      "PayoutCompleted": {
        "type": "object",
        "required": [
          "payout_id",
          "status",
          "amount",
          "net_amount",
          "currency",
          "completed_at"
        ],
        "properties": {
          "payout_id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "success"
            ]
          },
          "amount": {
            "type": "number"
          },
          "fee": {
            "type": "number"
          },
          "net_amount": {
            "type": "number"
          },
          "currency": {
            "$ref": "#/components/schemas/Currency"
          },
          "destination": {
            "type": [
              "string",
              "null"
            ],
            "description": "Masked destination, e.g. \"MTN ••0332\"."
          },
          "completed_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "additionalProperties": false
      },
      "PayoutFailed": {
        "type": "object",
        "required": [
          "payout_id",
          "status",
          "amount",
          "currency",
          "failed_at"
        ],
        "properties": {
          "payout_id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "failed"
            ]
          },
          "reason": {
            "type": [
              "string",
              "null"
            ]
          },
          "amount": {
            "type": "number"
          },
          "currency": {
            "$ref": "#/components/schemas/Currency"
          },
          "failed_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "additionalProperties": false
      },
      "SmsTopupApproved": {
        "type": "object",
        "required": [
          "topup_id",
          "status",
          "amount",
          "currency",
          "balance_after",
          "credited_at"
        ],
        "properties": {
          "topup_id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "approved"
            ]
          },
          "amount": {
            "type": "number"
          },
          "currency": {
            "$ref": "#/components/schemas/Currency"
          },
          "balance_after": {
            "type": "number"
          },
          "credited_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "additionalProperties": false
      },
      "BillPurchase": {
        "type": "object",
        "required": [
          "id",
          "reference",
          "kind",
          "status",
          "amount",
          "total",
          "currency",
          "account_number",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "reference": {
            "type": "string",
            "description": "Your reference for this purchase. Unique, and what status checks are keyed on."
          },
          "kind": {
            "type": "string",
            "enum": [
              "airtime",
              "data",
              "bill"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "approved",
              "failed"
            ]
          },
          "amount": {
            "type": "number"
          },
          "fee": {
            "type": "number"
          },
          "total": {
            "type": "number",
            "description": "Amount taken from your settled balance."
          },
          "currency": {
            "$ref": "#/components/schemas/Currency"
          },
          "biller_slug": {
            "type": "string",
            "nullable": true
          },
          "biller_name": {
            "type": "string",
            "nullable": true
          },
          "account_number": {
            "type": "string",
            "description": "Mobile number for airtime and data; bill account for a bill payment."
          },
          "account_name": {
            "type": "string",
            "nullable": true
          },
          "bundle_name": {
            "type": "string",
            "nullable": true
          },
          "data_id": {
            "type": "string",
            "nullable": true
          },
          "narration": {
            "type": "string",
            "nullable": true
          },
          "mode": {
            "type": "string",
            "enum": [
              "test",
              "live"
            ]
          },
          "message": {
            "type": "string",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "completed_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "MessageReceipt": {
        "type": "object",
        "required": [
          "message_id",
          "to",
          "mode",
          "status",
          "segments",
          "cost",
          "settled_at"
        ],
        "properties": {
          "message_id": {
            "type": "string",
            "format": "uuid"
          },
          "campaign_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "to": {
            "type": "string",
            "description": "Recipient MSISDN in the form it was submitted."
          },
          "sender": {
            "type": [
              "string",
              "null"
            ]
          },
          "mode": {
            "$ref": "#/components/schemas/Mode"
          },
          "status": {
            "type": "string",
            "enum": [
              "delivered",
              "failed",
              "undelivered",
              "rejected"
            ]
          },
          "provider_status": {
            "type": [
              "string",
              "null"
            ],
            "description": "Raw upstream status, uppercased. `NO_DLR` when we closed the message after 24 hours without a report."
          },
          "provider_message_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "error_reason": {
            "type": [
              "string",
              "null"
            ]
          },
          "segments": {
            "type": "integer",
            "minimum": 1
          },
          "cost": {
            "type": "number"
          },
          "submitted_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "settled_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "additionalProperties": false
      }
    }
  }
}
