{
  "openapi": "3.0.3",
  "info": {
    "title": "OlympusPay API",
    "version": "1.0.0",
    "description": "Complete REST API for OlympusPay — a neobank platform for African SMBs.\n\n## Authentication\nAll requests require two headers:\n```\nAuthorization: Bearer <USER_ACCESS_TOKEN>\napikey: <SUPABASE_ANON_KEY>\n```\nObtain `USER_ACCESS_TOKEN` by calling **POST /auth/v1/token** with your email and password.\n\n## Base URLs\n| Layer | URL |\n|---|---|\n| Auth | `https://hzxxenidupmnrpatbniq.supabase.co/auth/v1` |\n| Database (PostgREST) | `https://hzxxenidupmnrpatbniq.supabase.co/rest/v1` |\n| Edge Functions | `https://hzxxenidupmnrpatbniq.supabase.co/functions/v1` |\n\n## Rate Limiting\nEdge functions enforce per-user rate limits via a sliding-window counter in the database. Exceeding the limit returns `429 Too Many Requests`.\n\n## Postman Collections\n- [OlympusPay Core](/collections/OlympusPay.postman_collection.json)\n- [OlympusPay Travel](/collections/OlympusPay_Travel.postman_collection.json)\n- [Africa's Talking](/collections/AfricasTalking.postman_collection.json)\n- [Environment](/collections/OlympusPay.postman_environment.json)",
    "contact": {
      "name": "OlympusPay Developer Support",
      "email": "dev@olympuspay.co",
      "url": "https://olympuspay.co"
    },
    "license": {
      "name": "Proprietary"
    }
  },
  "servers": [
    {
      "url": "https://hzxxenidupmnrpatbniq.supabase.co",
      "description": "Production"
    }
  ],
  "security": [
    { "BearerAuth": [] },
    { "ApiKeyAuth": [] }
  ],
  "components": {
    "securitySchemes": {
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "JWT obtained from POST /auth/v1/token"
      },
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "apikey",
        "description": "Supabase anon key"
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": { "type": "string", "example": "Unauthorized" },
          "message": { "type": "string" }
        }
      },
      "Session": {
        "type": "object",
        "properties": {
          "access_token": { "type": "string" },
          "refresh_token": { "type": "string" },
          "expires_in": { "type": "integer", "example": 3600 },
          "token_type": { "type": "string", "example": "bearer" },
          "user": { "$ref": "#/components/schemas/User" }
        }
      },
      "User": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "email": { "type": "string", "format": "email" },
          "created_at": { "type": "string", "format": "date-time" },
          "user_metadata": { "type": "object" }
        }
      },
      "Profile": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "full_name": { "type": "string", "example": "Jane Doe" },
          "phone": { "type": "string", "example": "+26771000000" },
          "avatar_url": { "type": "string", "nullable": true },
          "locale": { "type": "string", "example": "en-BW" },
          "kyc_status": { "type": "string", "enum": ["not_started", "pending", "approved", "rejected"] },
          "account_tier": { "type": "string", "enum": ["starter", "personal", "business", "enterprise"] },
          "created_at": { "type": "string", "format": "date-time" }
        }
      },
      "BusinessProfile": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "user_id": { "type": "string", "format": "uuid" },
          "legal_name": { "type": "string", "example": "Acme (Pty) Ltd" },
          "trading_name": { "type": "string" },
          "entity_type": { "type": "string", "enum": ["sole_trader", "llc", "plc", "ngo", "other"] },
          "industry": { "type": "string" },
          "reg_number": { "type": "string" },
          "tax_id": { "type": "string" },
          "business_phone": { "type": "string" },
          "country": { "type": "string", "example": "BW" },
          "currency": { "type": "string", "example": "BWP" }
        }
      },
      "Account": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "user_id": { "type": "string", "format": "uuid" },
          "currency": { "type": "string", "example": "BWP" },
          "balance": { "type": "number", "example": 12500.00 },
          "available_balance": { "type": "number" },
          "account_number": { "type": "string" },
          "sort_code": { "type": "string" },
          "iban": { "type": "string", "nullable": true },
          "status": { "type": "string", "enum": ["active", "suspended", "closed"] }
        }
      },
      "Transaction": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "user_id": { "type": "string", "format": "uuid" },
          "type": { "type": "string", "enum": ["debit", "credit"] },
          "amount": { "type": "number" },
          "currency": { "type": "string" },
          "status": { "type": "string", "enum": ["pending", "completed", "failed", "cancelled"] },
          "recipient_name": { "type": "string" },
          "reference": { "type": "string" },
          "category": { "type": "string" },
          "payment_rail": { "type": "string", "enum": ["LOCAL", "ACH", "SWIFT", "BOOK_TRANSFER", "MOBILE_MONEY"] },
          "created_at": { "type": "string", "format": "date-time" }
        }
      },
      "Card": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "card_type": { "type": "string", "enum": ["virtual", "physical"] },
          "display_name": { "type": "string" },
          "last_four": { "type": "string", "example": "4242" },
          "masked_pan": { "type": "string", "example": "4*** **** **** 4242" },
          "status": { "type": "string", "enum": ["active", "frozen", "cancelled"] },
          "monthly_limit": { "type": "number" },
          "pin_set": { "type": "boolean" },
          "created_at": { "type": "string", "format": "date-time" }
        }
      },
      "Beneficiary": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "beneficiary_name": { "type": "string" },
          "account_number": { "type": "string" },
          "bank_code": { "type": "string" },
          "bank_name": { "type": "string" },
          "country": { "type": "string" },
          "payment_rail": { "type": "string", "enum": ["LOCAL", "ACH", "SWIFT", "MOBILE_MONEY"] }
        }
      },
      "Invoice": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "invoice_number": { "type": "string" },
          "client_name": { "type": "string" },
          "client_email": { "type": "string", "format": "email" },
          "status": { "type": "string", "enum": ["draft", "sent", "paid", "overdue", "cancelled"] },
          "subtotal": { "type": "number" },
          "tax": { "type": "number" },
          "total": { "type": "number" },
          "currency": { "type": "string" },
          "due_date": { "type": "string", "format": "date" },
          "created_at": { "type": "string", "format": "date-time" }
        }
      },
      "PaymentRequest": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "from_user_id": { "type": "string", "format": "uuid" },
          "to_email": { "type": "string", "format": "email" },
          "to_name": { "type": "string" },
          "amount": { "type": "number" },
          "currency": { "type": "string" },
          "status": { "type": "string", "enum": ["pending", "sent", "paid", "expired", "link_created"] },
          "payment_link": { "type": "string", "format": "uri" },
          "message": { "type": "string" },
          "created_at": { "type": "string", "format": "date-time" }
        }
      },
      "TravelBooking": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "booking_type": { "type": "string", "enum": ["flight", "hotel", "activity", "transfer"] },
          "provider": { "type": "string", "enum": ["duffel", "duffel_stays", "hotelbeds"] },
          "provider_order_id": { "type": "string" },
          "booking_reference": { "type": "string" },
          "status": { "type": "string", "enum": ["confirmed", "pending", "cancelled"] },
          "total_amount": { "type": "number" },
          "currency": { "type": "string" },
          "travel_date": { "type": "string", "format": "date" },
          "created_at": { "type": "string", "format": "date-time" }
        }
      },
      "KycSession": {
        "type": "object",
        "properties": {
          "session_id": { "type": "string" },
          "session_url": { "type": "string", "format": "uri" },
          "status": { "type": "string", "enum": ["pending", "in_progress", "completed", "failed"] },
          "entity_type": { "type": "string", "enum": ["individual", "business"] }
        }
      },
      "Passenger": {
        "type": "object",
        "required": ["given_name", "family_name", "gender", "born_on", "email", "phone_number"],
        "properties": {
          "given_name": { "type": "string" },
          "family_name": { "type": "string" },
          "title": { "type": "string", "enum": ["Mr", "Ms", "Mrs", "Dr", "Prof"] },
          "gender": { "type": "string", "enum": ["m", "f"] },
          "born_on": { "type": "string", "format": "date" },
          "email": { "type": "string", "format": "email" },
          "phone_number": { "type": "string" },
          "identity_documents": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "type": { "type": "string", "enum": ["passport", "national_id"] },
                "unique_identifier": { "type": "string" },
                "expires_on": { "type": "string", "format": "date" },
                "issuance_country": { "type": "string" }
              }
            }
          }
        }
      }
    },
    "parameters": {
      "SelectParam": {
        "name": "select",
        "in": "query",
        "description": "Comma-separated list of columns to return. Use `*` for all.",
        "schema": { "type": "string", "example": "*" }
      },
      "OrderParam": {
        "name": "order",
        "in": "query",
        "description": "Column and direction, e.g. `created_at.desc`",
        "schema": { "type": "string", "example": "created_at.desc" }
      },
      "LimitParam": {
        "name": "limit",
        "in": "query",
        "schema": { "type": "integer", "default": 20, "maximum": 1000 }
      },
      "OffsetParam": {
        "name": "offset",
        "in": "query",
        "schema": { "type": "integer", "default": 0 }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Missing or invalid token",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "Forbidden": {
        "description": "Insufficient permissions (KYC not approved or wrong role)",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "NotFound": {
        "description": "Resource not found",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "TooManyRequests": {
        "description": "Rate limit exceeded",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      }
    }
  },
  "tags": [
    { "name": "Authentication", "description": "Sign up, sign in, password reset, token management" },
    { "name": "Profiles", "description": "User and business profile management" },
    { "name": "Accounts", "description": "Bank accounts and balances" },
    { "name": "Transactions", "description": "Payment history and transaction details" },
    { "name": "Cards", "description": "Virtual and physical card management" },
    { "name": "Beneficiaries", "description": "Saved payment recipients" },
    { "name": "Payments", "description": "Send money, bulk payments, payment requests" },
    { "name": "Invoices", "description": "Invoice creation and management" },
    { "name": "Scheduled Payments", "description": "Recurring and future-dated payments" },
    { "name": "KYC / Identity", "description": "Identity verification via DIDIT (liveness + document + AML)" },
    { "name": "Documents", "description": "e-Signature for agreements via Documenso" },
    { "name": "Banking — Zenus", "description": "Core banking operations: accounts, payments, cards, compliance" },
    { "name": "Mobile Money — Flutterwave", "description": "Mobile money, airtime, bill payments, and payment links" },
    { "name": "Messaging — Africa's Talking", "description": "SMS, WhatsApp, USSD, and airtime via Africa's Talking" },
    { "name": "Travel — Flights", "description": "Flight search, booking, and management via Duffel" },
    { "name": "Travel — Stays", "description": "Hotel and accommodation booking via Duffel Stays" },
    { "name": "Travel — HotelBeds", "description": "Hotels, activities, and airport transfers via HotelBeds" },
    { "name": "File Storage", "description": "Upload and retrieve files via Cloudflare R2 presigned URLs" },
    { "name": "AI & Tools", "description": "AI insights, receipt scanning, support chat, business auto-fill" },
    { "name": "Notifications", "description": "In-app notification management" },
    { "name": "Webhooks", "description": "Inbound provider webhook callbacks" }
  ],
  "paths": {

    "/auth/v1/token": {
      "post": {
        "tags": ["Authentication"],
        "summary": "Sign in",
        "operationId": "signIn",
        "security": [],
        "parameters": [
          { "name": "grant_type", "in": "query", "required": true, "schema": { "type": "string", "enum": ["password", "refresh_token"] } }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "title": "Password grant",
                    "type": "object",
                    "required": ["email", "password"],
                    "properties": {
                      "email": { "type": "string", "format": "email", "example": "user@olympuspay.co" },
                      "password": { "type": "string", "example": "SecurePass2026!" }
                    }
                  },
                  {
                    "title": "Refresh token grant",
                    "type": "object",
                    "required": ["refresh_token"],
                    "properties": {
                      "refresh_token": { "type": "string" }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Session tokens",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Session" } } }
          },
          "400": { "description": "Invalid credentials" }
        }
      }
    },

    "/auth/v1/signup": {
      "post": {
        "tags": ["Authentication"],
        "summary": "Create account",
        "operationId": "signUp",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["email", "password"],
                "properties": {
                  "email": { "type": "string", "format": "email" },
                  "password": { "type": "string", "minLength": 8 }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Account created", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Session" } } } }
        }
      }
    },

    "/auth/v1/user": {
      "get": {
        "tags": ["Authentication"],
        "summary": "Get current user",
        "operationId": "getUser",
        "responses": {
          "200": { "description": "Current user", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/User" } } } },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },

    "/auth/v1/recover": {
      "post": {
        "tags": ["Authentication"],
        "summary": "Request password reset",
        "operationId": "requestPasswordReset",
        "security": [],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": { "type": "object", "required": ["email"], "properties": { "email": { "type": "string", "format": "email" } } }
            }
          }
        },
        "responses": { "200": { "description": "Reset email sent" } }
      }
    },

    "/auth/v1/logout": {
      "post": {
        "tags": ["Authentication"],
        "summary": "Sign out",
        "operationId": "signOut",
        "responses": { "204": { "description": "Signed out" } }
      }
    },

    "/rest/v1/profiles": {
      "get": {
        "tags": ["Profiles"],
        "summary": "Get profile",
        "operationId": "getProfile",
        "parameters": [
          { "name": "id", "in": "query", "schema": { "type": "string" }, "example": "eq.USER_UUID" },
          { "$ref": "#/components/parameters/SelectParam" }
        ],
        "responses": {
          "200": { "description": "Profile list", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Profile" } } } } }
        }
      },
      "patch": {
        "tags": ["Profiles"],
        "summary": "Update profile",
        "operationId": "updateProfile",
        "parameters": [{ "name": "id", "in": "query", "schema": { "type": "string" }, "example": "eq.USER_UUID" }],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/Profile" }
            }
          }
        },
        "responses": {
          "200": { "description": "Updated profile" }
        }
      }
    },

    "/rest/v1/business_profiles": {
      "get": {
        "tags": ["Profiles"],
        "summary": "Get business profile",
        "operationId": "getBusinessProfile",
        "parameters": [
          { "name": "user_id", "in": "query", "schema": { "type": "string" }, "example": "eq.USER_UUID" },
          { "$ref": "#/components/parameters/SelectParam" }
        ],
        "responses": {
          "200": { "description": "Business profiles", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/BusinessProfile" } } } } }
        }
      },
      "post": {
        "tags": ["Profiles"],
        "summary": "Create / upsert business profile",
        "operationId": "upsertBusinessProfile",
        "parameters": [{ "name": "Prefer", "in": "header", "schema": { "type": "string" }, "example": "resolution=merge-duplicates,return=representation" }],
        "requestBody": {
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BusinessProfile" } } }
        },
        "responses": { "201": { "description": "Created" } }
      }
    },

    "/rest/v1/accounts": {
      "get": {
        "tags": ["Accounts"],
        "summary": "List accounts",
        "operationId": "listAccounts",
        "parameters": [
          { "name": "user_id", "in": "query", "schema": { "type": "string" }, "example": "eq.USER_UUID" },
          { "$ref": "#/components/parameters/SelectParam" }
        ],
        "responses": {
          "200": { "description": "Accounts", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Account" } } } } }
        }
      }
    },

    "/rest/v1/transactions": {
      "get": {
        "tags": ["Transactions"],
        "summary": "List transactions",
        "operationId": "listTransactions",
        "parameters": [
          { "name": "user_id", "in": "query", "schema": { "type": "string" }, "example": "eq.USER_UUID" },
          { "name": "status", "in": "query", "schema": { "type": "string" }, "example": "eq.completed" },
          { "name": "type", "in": "query", "schema": { "type": "string" }, "example": "eq.debit" },
          { "$ref": "#/components/parameters/SelectParam" },
          { "$ref": "#/components/parameters/OrderParam" },
          { "$ref": "#/components/parameters/LimitParam" },
          { "$ref": "#/components/parameters/OffsetParam" }
        ],
        "responses": {
          "200": { "description": "Transactions", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Transaction" } } } } }
        }
      }
    },

    "/rest/v1/cards": {
      "get": {
        "tags": ["Cards"],
        "summary": "List cards",
        "operationId": "listCards",
        "parameters": [
          { "name": "user_id", "in": "query", "schema": { "type": "string" }, "example": "eq.USER_UUID" },
          { "$ref": "#/components/parameters/SelectParam" }
        ],
        "responses": {
          "200": { "description": "Cards", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Card" } } } } }
        }
      }
    },

    "/rest/v1/beneficiaries": {
      "get": {
        "tags": ["Beneficiaries"],
        "summary": "List beneficiaries",
        "operationId": "listBeneficiaries",
        "parameters": [
          { "name": "user_id", "in": "query", "schema": { "type": "string" }, "example": "eq.USER_UUID" },
          { "$ref": "#/components/parameters/OrderParam" }
        ],
        "responses": {
          "200": { "description": "Beneficiaries", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Beneficiary" } } } } }
        }
      },
      "post": {
        "tags": ["Beneficiaries"],
        "summary": "Add beneficiary",
        "operationId": "addBeneficiary",
        "requestBody": {
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Beneficiary" } } }
        },
        "responses": { "201": { "description": "Created" } }
      }
    },

    "/rest/v1/beneficiaries/{id}": {
      "delete": {
        "tags": ["Beneficiaries"],
        "summary": "Delete beneficiary",
        "operationId": "deleteBeneficiary",
        "parameters": [{ "name": "id", "in": "query", "required": true, "schema": { "type": "string" }, "example": "eq.UUID" }],
        "responses": { "204": { "description": "Deleted" } }
      }
    },

    "/rest/v1/invoices": {
      "get": {
        "tags": ["Invoices"],
        "summary": "List invoices",
        "operationId": "listInvoices",
        "parameters": [
          { "name": "user_id", "in": "query", "schema": { "type": "string" }, "example": "eq.USER_UUID" },
          { "name": "status", "in": "query", "schema": { "type": "string" }, "example": "eq.sent" },
          { "$ref": "#/components/parameters/SelectParam" },
          { "$ref": "#/components/parameters/OrderParam" }
        ],
        "responses": {
          "200": { "description": "Invoices", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Invoice" } } } } }
        }
      }
    },

    "/rest/v1/payment_requests": {
      "get": {
        "tags": ["Payments"],
        "summary": "List payment requests",
        "operationId": "listPaymentRequests",
        "parameters": [
          { "name": "from_user_id", "in": "query", "schema": { "type": "string" }, "example": "eq.USER_UUID" },
          { "$ref": "#/components/parameters/OrderParam" }
        ],
        "responses": {
          "200": { "description": "Payment requests", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/PaymentRequest" } } } } }
        }
      }
    },

    "/rest/v1/notifications": {
      "get": {
        "tags": ["Notifications"],
        "summary": "List notifications",
        "operationId": "listNotifications",
        "parameters": [
          { "name": "user_id", "in": "query", "schema": { "type": "string" }, "example": "eq.USER_UUID" },
          { "name": "is_read", "in": "query", "schema": { "type": "string" }, "example": "eq.false" },
          { "$ref": "#/components/parameters/LimitParam" }
        ],
        "responses": {
          "200": { "description": "Notifications" }
        }
      },
      "patch": {
        "tags": ["Notifications"],
        "summary": "Mark notifications as read",
        "operationId": "markNotificationsRead",
        "parameters": [
          { "name": "user_id", "in": "query", "schema": { "type": "string" }, "example": "eq.USER_UUID" },
          { "name": "is_read", "in": "query", "schema": { "type": "string" }, "example": "eq.false" }
        ],
        "requestBody": {
          "content": { "application/json": { "schema": { "type": "object", "properties": { "is_read": { "type": "boolean" } } } } }
        },
        "responses": { "200": { "description": "Updated" } }
      }
    },

    "/rest/v1/rpc/get_my_balance": {
      "post": {
        "tags": ["Accounts"],
        "summary": "Get current balance (RPC)",
        "operationId": "getMyBalance",
        "requestBody": { "content": { "application/json": { "schema": { "type": "object" } } } },
        "responses": {
          "200": {
            "description": "Balance details",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "balance": { "type": "number" },
                    "available_balance": { "type": "number" },
                    "currency": { "type": "string" }
                  }
                }
              }
            }
          }
        }
      }
    },

    "/rest/v1/rpc/get_kyc_status": {
      "post": {
        "tags": ["KYC / Identity"],
        "summary": "Get KYC status (RPC)",
        "operationId": "getKycStatus",
        "requestBody": { "content": { "application/json": { "schema": { "type": "object" } } } },
        "responses": {
          "200": {
            "description": "KYC status",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": { "type": "string", "enum": ["not_started", "pending", "approved", "rejected"] },
                    "entity_type": { "type": "string" },
                    "submitted_at": { "type": "string", "format": "date-time", "nullable": true },
                    "decision_at": { "type": "string", "format": "date-time", "nullable": true }
                  }
                }
              }
            }
          }
        }
      }
    },

    "/rest/v1/rpc/initiate_transfer": {
      "post": {
        "tags": ["Payments"],
        "summary": "Initiate bank transfer (RPC)",
        "operationId": "initiateTransfer",
        "description": "Requires KYC to be approved. Debits the user's account and routes via Zenus based on payment rail.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["p_recipient", "p_account_num", "p_amount", "p_method"],
                "properties": {
                  "p_recipient": { "type": "string", "example": "Jane Smith" },
                  "p_sort_code": { "type": "string", "example": "20-00-00" },
                  "p_account_num": { "type": "string", "example": "12345678" },
                  "p_amount": { "type": "number", "example": 500.00 },
                  "p_method": { "type": "string", "enum": ["eft", "ach", "swift", "internal"] },
                  "p_reference": { "type": "string", "example": "Invoice #001" },
                  "p_category": { "type": "string", "example": "supplier" }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Transfer queued", "content": { "application/json": { "schema": { "type": "object", "properties": { "transaction_id": { "type": "string" }, "status": { "type": "string" } } } } } },
          "403": { "$ref": "#/components/responses/Forbidden" }
        }
      }
    },

    "/rest/v1/rpc/create_payment_request": {
      "post": {
        "tags": ["Payments"],
        "summary": "Create payment request (RPC)",
        "operationId": "createPaymentRequest",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["p_to_email", "p_amount", "p_currency"],
                "properties": {
                  "p_to_email": { "type": "string", "format": "email" },
                  "p_to_name": { "type": "string" },
                  "p_amount": { "type": "number" },
                  "p_currency": { "type": "string", "example": "BWP" },
                  "p_message": { "type": "string" }
                }
              }
            }
          }
        },
        "responses": { "200": { "description": "Payment request created" } }
      }
    },

    "/rest/v1/rpc/create_invoice": {
      "post": {
        "tags": ["Invoices"],
        "summary": "Create invoice with line items (RPC)",
        "operationId": "createInvoice",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["p_client_name", "p_client_email", "p_due_date", "p_currency", "p_items"],
                "properties": {
                  "p_client_name": { "type": "string" },
                  "p_client_email": { "type": "string", "format": "email" },
                  "p_due_date": { "type": "string", "format": "date" },
                  "p_currency": { "type": "string" },
                  "p_items": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "description": { "type": "string" },
                        "quantity": { "type": "number" },
                        "unit_price": { "type": "number" }
                      }
                    }
                  },
                  "p_notes": { "type": "string" }
                }
              }
            }
          }
        },
        "responses": { "200": { "description": "Invoice created", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Invoice" } } } } }
      }
    },

    "/rest/v1/rpc/create_scheduled_payment": {
      "post": {
        "tags": ["Scheduled Payments"],
        "summary": "Schedule a recurring payment (RPC)",
        "operationId": "createScheduledPayment",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["p_recipient_name", "p_amount", "p_currency", "p_frequency", "p_next_run_date"],
                "properties": {
                  "p_recipient_name": { "type": "string" },
                  "p_amount": { "type": "number" },
                  "p_currency": { "type": "string" },
                  "p_frequency": { "type": "string", "enum": ["weekly", "monthly", "quarterly", "annually"] },
                  "p_next_run_date": { "type": "string", "format": "date" },
                  "p_payment_method": { "type": "string", "enum": ["eft", "ach", "swift"] },
                  "p_recipient_acct": { "type": "string" },
                  "p_sort_code": { "type": "string" },
                  "p_memo": { "type": "string" }
                }
              }
            }
          }
        },
        "responses": { "200": { "description": "Scheduled payment created" } }
      }
    },

    "/functions/v1/didit-session": {
      "post": {
        "tags": ["KYC / Identity"],
        "summary": "Create KYC verification session",
        "operationId": "createKycSession",
        "description": "Initiates a DIDIT identity verification session. Returns a `session_url` to redirect the user to complete liveness check and document verification.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["entity_type", "country"],
                "properties": {
                  "entity_type": { "type": "string", "enum": ["individual", "business"] },
                  "country": { "type": "string", "example": "BW", "description": "ISO 3166-1 alpha-2 country code" },
                  "callback_url": { "type": "string", "format": "uri", "example": "olympuspay://didit-callback" },
                  "company_name": { "type": "string", "description": "Required when entity_type is business" },
                  "biz_type": { "type": "string", "enum": ["sole_trader", "llc", "plc", "ngo"] },
                  "industry": { "type": "string" }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "KYC session", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/KycSession" } } } },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },

    "/functions/v1/didit-decision": {
      "post": {
        "tags": ["KYC / Identity"],
        "summary": "Poll KYC decision",
        "operationId": "getKycDecision",
        "description": "Checks the outcome of a DIDIT session. Updates the user's KYC status in the database if a final decision is available.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["session_id"],
                "properties": { "session_id": { "type": "string" } }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Decision",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": { "type": "string", "enum": ["pending", "success", "failed"] },
                    "liveness_score": { "type": "number" },
                    "aml_hits": { "type": "integer" },
                    "kyc_status": { "type": "string" }
                  }
                }
              }
            }
          }
        }
      }
    },

    "/functions/v1/didit-webhook": {
      "post": {
        "tags": ["Webhooks"],
        "summary": "DIDIT webhook callback",
        "operationId": "diditWebhook",
        "security": [],
        "description": "Registered with DIDIT. Verifies `x-didit-signature` HMAC-SHA256 before processing.",
        "parameters": [{ "name": "x-didit-signature", "in": "header", "required": true, "schema": { "type": "string" } }],
        "responses": { "200": { "description": "Processed" }, "403": { "description": "Invalid signature" } }
      }
    },

    "/functions/v1/documenso-send": {
      "post": {
        "tags": ["Documents"],
        "summary": "Send document for e-signature",
        "operationId": "sendDocument",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["email", "full_name", "document_type"],
                "properties": {
                  "email": { "type": "string", "format": "email" },
                  "full_name": { "type": "string" },
                  "document_type": { "type": "string", "enum": ["terms", "agreement", "disclosure"] }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Document sent",
            "content": { "application/json": { "schema": { "type": "object", "properties": { "document_id": { "type": "string" }, "signing_url": { "type": "string" } } } } }
          }
        }
      }
    },

    "/functions/v1/documenso-status": {
      "post": {
        "tags": ["Documents"],
        "summary": "Get document signature status",
        "operationId": "getDocumentStatus",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": { "type": "object", "required": ["document_id"], "properties": { "document_id": { "type": "string" } } }
            }
          }
        },
        "responses": { "200": { "description": "Status" } }
      }
    },

    "/functions/v1/send-payment-request": {
      "post": {
        "tags": ["Payments"],
        "summary": "Send payment request via email",
        "operationId": "sendPaymentRequest",
        "description": "Creates a Flutterwave payment link and sends it to the recipient via Resend email. Falls back to a hosted payment page if Flutterwave is unavailable.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["to_email", "amount", "currency"],
                "properties": {
                  "to_email": { "type": "string", "format": "email" },
                  "to_name": { "type": "string" },
                  "amount": { "type": "number", "example": 1500 },
                  "currency": { "type": "string", "example": "BWP" },
                  "description": { "type": "string" },
                  "from_biz_name": { "type": "string" }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Payment request sent",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean" },
                    "payment_link": { "type": "string", "format": "uri" },
                    "email_sent": { "type": "boolean" },
                    "email_error": { "type": "string", "nullable": true },
                    "fw_link": { "type": "boolean" }
                  }
                }
              }
            }
          }
        }
      }
    },

    "/functions/v1/initiate-deposit": {
      "post": {
        "tags": ["Payments"],
        "summary": "Initiate a deposit",
        "operationId": "initiateDeposit",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["amount", "currency"],
                "properties": {
                  "amount": { "type": "number" },
                  "currency": { "type": "string", "example": "BWP" },
                  "payment_method": { "type": "string", "enum": ["card", "bank_transfer", "mobile_money"] }
                }
              }
            }
          }
        },
        "responses": { "200": { "description": "Deposit initiated" } }
      }
    },

    "/functions/v1/auto-fill-business": {
      "post": {
        "tags": ["AI & Tools"],
        "summary": "Auto-fill business details from Companies House",
        "operationId": "autoFillBusiness",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["country", "registration_number"],
                "properties": {
                  "country": { "type": "string", "example": "GB", "description": "Currently supports GB (Companies House)" },
                  "registration_number": { "type": "string", "example": "12345678" }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Business details", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BusinessProfile" } } } }
        }
      }
    },

    "/functions/v1/ai-insights": {
      "post": {
        "tags": ["AI & Tools"],
        "summary": "Get AI-generated spending insights",
        "operationId": "getAiInsights",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "month": { "type": "string", "example": "2026-06", "description": "YYYY-MM format" },
                  "period": { "type": "string", "enum": ["month", "quarter", "year"] }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Insights",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "summary": { "type": "string" },
                    "top_categories": { "type": "array", "items": { "type": "object" } },
                    "savings_opportunities": { "type": "array", "items": { "type": "string" } }
                  }
                }
              }
            }
          }
        }
      }
    },

    "/functions/v1/scan-receipt": {
      "post": {
        "tags": ["AI & Tools"],
        "summary": "Extract data from receipt image (OCR)",
        "operationId": "scanReceipt",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": ["file"],
                "properties": { "file": { "type": "string", "format": "binary" } }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Extracted receipt data",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "merchant": { "type": "string" },
                    "amount": { "type": "number" },
                    "currency": { "type": "string" },
                    "date": { "type": "string" },
                    "items": { "type": "array", "items": { "type": "object" } }
                  }
                }
              }
            }
          }
        }
      }
    },

    "/functions/v1/support-chat": {
      "post": {
        "tags": ["AI & Tools"],
        "summary": "AI customer support chat",
        "operationId": "supportChat",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["message"],
                "properties": { "message": { "type": "string", "example": "How do I increase my transfer limit?" } }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "AI response",
            "content": { "application/json": { "schema": { "type": "object", "properties": { "reply": { "type": "string" } } } } }
          }
        }
      }
    },

    "/functions/v1/zenus-create-person": {
      "post": {
        "tags": ["Banking — Zenus"],
        "summary": "Create a Zenus person record",
        "operationId": "zenusCreatePerson",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["first_name", "last_name", "email"],
                "properties": {
                  "first_name": { "type": "string" },
                  "last_name": { "type": "string" },
                  "email": { "type": "string", "format": "email" },
                  "phone": { "type": "string" },
                  "date_of_birth": { "type": "string", "format": "date" },
                  "nationality": { "type": "string" }
                }
              }
            }
          }
        },
        "responses": { "200": { "description": "Person created", "content": { "application/json": { "schema": { "type": "object", "properties": { "person_id": { "type": "string" } } } } } }
      }
    },

    "/functions/v1/zenus-open-account": {
      "post": {
        "tags": ["Banking — Zenus"],
        "summary": "Open a Zenus bank account",
        "operationId": "zenusOpenAccount",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": { "account_currency": { "type": "string", "enum": ["USD", "GBP", "EUR", "BWP"], "example": "USD" } }
              }
            }
          }
        },
        "responses": { "200": { "description": "Account opened" } }
      }
    },

    "/functions/v1/zenus-payment": {
      "post": {
        "tags": ["Banking — Zenus"],
        "summary": "Send a payment",
        "operationId": "zenusPayment",
        "description": "Routes payment via LOCAL (EFT), ACH, SWIFT, or BOOK_TRANSFER depending on `payment_rail`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["payment_rail", "amount", "currency", "recipient_name", "account_number"],
                "properties": {
                  "payment_rail": { "type": "string", "enum": ["LOCAL", "ACH", "SWIFT", "BOOK_TRANSFER"] },
                  "amount": { "type": "number" },
                  "currency": { "type": "string" },
                  "recipient_name": { "type": "string" },
                  "account_number": { "type": "string" },
                  "routing_code": { "type": "string", "description": "Sort code (LOCAL), ABA routing (ACH), or BIC/SWIFT (SWIFT)" },
                  "bank_name": { "type": "string" },
                  "bank_country": { "type": "string" },
                  "reference": { "type": "string" },
                  "purpose": { "type": "string", "description": "Required for SWIFT payments" },
                  "save_beneficiary": { "type": "boolean", "default": false }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Payment initiated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "payment_id": { "type": "string" },
                    "transaction_id": { "type": "string" },
                    "status": { "type": "string" }
                  }
                }
              }
            }
          }
        }
      }
    },

    "/functions/v1/zenus-cancel-payment": {
      "post": {
        "tags": ["Banking — Zenus"],
        "summary": "Cancel a pending payment",
        "operationId": "zenusCancelPayment",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": { "type": "object", "required": ["payment_id"], "properties": { "payment_id": { "type": "string" } } }
            }
          }
        },
        "responses": { "200": { "description": "Cancelled" } }
      }
    },

    "/functions/v1/zenus-bulk-payment": {
      "post": {
        "tags": ["Banking — Zenus"],
        "summary": "Send bulk / payroll payments",
        "operationId": "zenusBulkPayment",
        "description": "Processes multiple payments in a single batch. Returns per-recipient results.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["batch_name", "payment_rail", "currency", "recipients"],
                "properties": {
                  "batch_name": { "type": "string" },
                  "payment_rail": { "type": "string", "enum": ["LOCAL", "ACH", "SWIFT"] },
                  "currency": { "type": "string" },
                  "recipients": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "required": ["name", "account_number", "amount"],
                      "properties": {
                        "name": { "type": "string" },
                        "account_number": { "type": "string" },
                        "routing_code": { "type": "string" },
                        "amount": { "type": "number" }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Batch results",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "batch_id": { "type": "string" },
                    "total": { "type": "integer" },
                    "succeeded": { "type": "integer" },
                    "failed": { "type": "integer" },
                    "results": { "type": "array", "items": { "type": "object" } }
                  }
                }
              }
            }
          }
        }
      }
    },

    "/functions/v1/zenus-card-issue": {
      "post": {
        "tags": ["Banking — Zenus"],
        "summary": "Issue a card",
        "operationId": "zenusCardIssue",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["card_type"],
                "properties": {
                  "card_type": { "type": "string", "enum": ["virtual", "physical"] },
                  "display_name": { "type": "string", "example": "Travel Card" },
                  "monthly_limit": { "type": "number", "example": 5000 }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Card issued",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Card" } } }
          }
        }
      }
    },

    "/functions/v1/zenus-card-manage": {
      "post": {
        "tags": ["Banking — Zenus"],
        "summary": "Manage a card (freeze / unfreeze / cancel / update limit)",
        "operationId": "zenusCardManage",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["card_id", "action"],
                "properties": {
                  "card_id": { "type": "string" },
                  "action": { "type": "string", "enum": ["freeze", "unfreeze", "cancel", "update_limit", "request_replacement"] },
                  "monthly_limit": { "type": "number", "description": "Required when action is update_limit" }
                }
              }
            }
          }
        },
        "responses": { "200": { "description": "Card updated" } }
      }
    },

    "/functions/v1/zenus-card-pin": {
      "post": {
        "tags": ["Banking — Zenus"],
        "summary": "Set or change card PIN",
        "operationId": "zenusCardPin",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["card_id", "pin", "action"],
                "properties": {
                  "card_id": { "type": "string" },
                  "pin": { "type": "string", "pattern": "^[0-9]{4}$" },
                  "action": { "type": "string", "enum": ["set", "change"] }
                }
              }
            }
          }
        },
        "responses": { "200": { "description": "PIN updated" } }
      }
    },

    "/functions/v1/zenus-sync-balance": {
      "post": {
        "tags": ["Banking — Zenus"],
        "summary": "Sync account balance from Zenus",
        "operationId": "zenusSyncBalance",
        "requestBody": { "content": { "application/json": { "schema": { "type": "object" } } } },
        "responses": { "200": { "description": "Balance synced" } }
      }
    },

    "/functions/v1/zenus-statement": {
      "post": {
        "tags": ["Banking — Zenus"],
        "summary": "Generate account statement",
        "operationId": "zenusStatement",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["start_date", "end_date"],
                "properties": {
                  "start_date": { "type": "string", "format": "date" },
                  "end_date": { "type": "string", "format": "date" }
                }
              }
            }
          }
        },
        "responses": { "200": { "description": "Statement PDF URL" } }
      }
    },

    "/functions/v1/zenus-compliance-check": {
      "post": {
        "tags": ["Banking — Zenus"],
        "summary": "Run OFAC / PEP compliance check",
        "operationId": "zenusComplianceCheck",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": { "check_type": { "type": "string", "enum": ["OFAC_PEP", "AML", "FULL"] } }
              }
            }
          }
        },
        "responses": { "200": { "description": "Compliance result" } }
      }
    },

    "/functions/v1/zenus-ubo-declare": {
      "post": {
        "tags": ["Banking — Zenus"],
        "summary": "Declare Ultimate Beneficial Owners",
        "operationId": "zenusUboDeclare",
        "description": "Required for business accounts. Declares all beneficial owners with ≥10% ownership.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["beneficial_owners"],
                "properties": {
                  "beneficial_owners": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "required": ["first_name", "last_name", "date_of_birth", "nationality", "ownership_percentage"],
                      "properties": {
                        "first_name": { "type": "string" },
                        "last_name": { "type": "string" },
                        "date_of_birth": { "type": "string", "format": "date" },
                        "nationality": { "type": "string" },
                        "ownership_percentage": { "type": "number", "minimum": 0, "maximum": 100 },
                        "is_director": { "type": "boolean" }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": { "200": { "description": "UBOs registered" } }
      }
    },

    "/functions/v1/zenus-webhook": {
      "post": {
        "tags": ["Webhooks"],
        "summary": "Zenus event webhook",
        "operationId": "zenusWebhook",
        "security": [],
        "description": "Registered with Zenus. Events: `PAYMENT_COMPLETED`, `PAYMENT_FAILED`, `CARD_FROZEN`, `COMPLIANCE_FLAGGED`, `BALANCE_UPDATED`.",
        "parameters": [{ "name": "x-zenus-signature", "in": "header", "required": true, "schema": { "type": "string" } }],
        "responses": { "200": { "description": "Processed" }, "403": { "description": "Invalid signature" } }
      }
    },

    "/functions/v1/fw-transfer": {
      "get": {
        "tags": ["Mobile Money — Flutterwave"],
        "summary": "List available mobile money operators",
        "operationId": "fwListOperators",
        "responses": {
          "200": {
            "description": "Operator list",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "operators": {
                      "type": "array",
                      "items": { "type": "object", "properties": { "code": { "type": "string" }, "name": { "type": "string" }, "country": { "type": "string" } } }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": ["Mobile Money — Flutterwave"],
        "summary": "Send mobile money or bank transfer",
        "operationId": "fwTransfer",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["mode", "amount"],
                "properties": {
                  "mode": { "type": "string", "enum": ["momo", "bank"], "description": "`momo` for mobile money, `bank` for bank transfer" },
                  "recipientPhone": { "type": "string", "description": "Required for momo" },
                  "recipientName": { "type": "string" },
                  "amount": { "type": "number" },
                  "operator": { "type": "string", "description": "Mobile operator code (momo only)" },
                  "currency": { "type": "string" },
                  "narration": { "type": "string" },
                  "accountNumber": { "type": "string", "description": "Required for bank mode" },
                  "bankCode": { "type": "string", "description": "Required for bank mode" },
                  "country": { "type": "string" }
                }
              }
            }
          }
        },
        "responses": { "200": { "description": "Transfer initiated" } }
      }
    },

    "/functions/v1/fw-airtime": {
      "post": {
        "tags": ["Mobile Money — Flutterwave"],
        "summary": "Send airtime",
        "operationId": "fwAirtime",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["phone", "amount", "operator", "country"],
                "properties": {
                  "phone": { "type": "string", "example": "+26771000000" },
                  "amount": { "type": "number" },
                  "operator": { "type": "string" },
                  "country": { "type": "string", "example": "BW" }
                }
              }
            }
          }
        },
        "responses": { "200": { "description": "Airtime sent" } }
      }
    },

    "/functions/v1/fw-bills": {
      "post": {
        "tags": ["Mobile Money — Flutterwave"],
        "summary": "Pay a utility bill",
        "operationId": "fwBills",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["biller_code", "bill_type", "amount"],
                "properties": {
                  "biller_code": { "type": "string" },
                  "bill_type": { "type": "string", "enum": ["electricity", "water", "tv", "internet", "insurance"] },
                  "amount": { "type": "number" },
                  "phone": { "type": "string" }
                }
              }
            }
          }
        },
        "responses": { "200": { "description": "Bill paid" } }
      }
    },

    "/functions/v1/fw-payment-link": {
      "post": {
        "tags": ["Mobile Money — Flutterwave"],
        "summary": "Create a Flutterwave payment link",
        "operationId": "fwPaymentLink",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["amount", "currency"],
                "properties": {
                  "amount": { "type": "number" },
                  "currency": { "type": "string" },
                  "description": { "type": "string" }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Payment link", "content": { "application/json": { "schema": { "type": "object", "properties": { "link": { "type": "string", "format": "uri" } } } } } }
        }
      }
    },

    "/functions/v1/at-sms": {
      "post": {
        "tags": ["Messaging — Africa's Talking"],
        "summary": "Send SMS",
        "operationId": "atSms",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["phone", "message"],
                "properties": {
                  "phone": { "type": "string", "example": "+26771000000" },
                  "message": { "type": "string" }
                }
              }
            }
          }
        },
        "responses": { "200": { "description": "SMS sent" } }
      }
    },

    "/functions/v1/at-whatsapp": {
      "post": {
        "tags": ["Messaging — Africa's Talking"],
        "summary": "Send WhatsApp message",
        "operationId": "atWhatsapp",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["phone", "message"],
                "properties": {
                  "phone": { "type": "string" },
                  "message": { "type": "string" },
                  "template": { "type": "string" }
                }
              }
            }
          }
        },
        "responses": { "200": { "description": "WhatsApp message sent" } }
      }
    },

    "/functions/v1/at-airtime": {
      "post": {
        "tags": ["Messaging — Africa's Talking"],
        "summary": "Send airtime (Africa's Talking)",
        "operationId": "atAirtime",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["phone", "amount", "currency"],
                "properties": {
                  "phone": { "type": "string" },
                  "amount": { "type": "number" },
                  "currency": { "type": "string" }
                }
              }
            }
          }
        },
        "responses": { "200": { "description": "Airtime sent" } }
      }
    },

    "/functions/v1/at-ussd": {
      "post": {
        "tags": ["Messaging — Africa's Talking"],
        "summary": "USSD session handler",
        "operationId": "atUssd",
        "security": [],
        "description": "Called by Africa's Talking for each USSD interaction. Authenticate via `?secret=` query parameter.",
        "parameters": [{ "name": "secret", "in": "query", "required": true, "schema": { "type": "string" } }],
        "requestBody": {
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "sessionId": { "type": "string" },
                  "serviceCode": { "type": "string" },
                  "phoneNumber": { "type": "string" },
                  "text": { "type": "string" }
                }
              }
            }
          }
        },
        "responses": { "200": { "description": "USSD response string (CON or END prefix)" } }
      }
    },

    "/functions/v1/duffel-airports": {
      "get": {
        "tags": ["Travel — Flights"],
        "summary": "Search airports (autocomplete)",
        "operationId": "duffelAirports",
        "parameters": [{ "name": "q", "in": "query", "required": true, "schema": { "type": "string" }, "example": "gaborone" }],
        "responses": {
          "200": {
            "description": "Airport results",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "airports": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "iata_code": { "type": "string" },
                          "name": { "type": "string" },
                          "city": { "type": "string" },
                          "country": { "type": "string" }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },

    "/functions/v1/duffel-search": {
      "post": {
        "tags": ["Travel — Flights"],
        "summary": "Search flights",
        "operationId": "duffelSearch",
        "description": "Returns a list of flight offers. Save `offer_request_id` and the desired `offer_id` for booking.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["origin", "destination", "departure_date", "passengers"],
                "properties": {
                  "origin": { "type": "string", "example": "GBE", "description": "IATA airport code" },
                  "destination": { "type": "string", "example": "JNB" },
                  "departure_date": { "type": "string", "format": "date" },
                  "return_date": { "type": "string", "format": "date", "description": "Omit for one-way" },
                  "passengers": { "type": "integer", "minimum": 1, "maximum": 9 },
                  "cabin_class": { "type": "string", "enum": ["economy", "premium_economy", "business", "first"] }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Flight offers",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "offer_request_id": { "type": "string" },
                    "offers": { "type": "array", "items": { "type": "object" } }
                  }
                }
              }
            }
          }
        }
      }
    },

    "/functions/v1/duffel-offer": {
      "post": {
        "tags": ["Travel — Flights"],
        "summary": "Get offer details",
        "operationId": "duffelOffer",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["offer_id"],
                "properties": {
                  "offer_id": { "type": "string" },
                  "offer_request_id": { "type": "string" }
                }
              }
            }
          }
        },
        "responses": { "200": { "description": "Offer details with fare rules and baggage" } }
      }
    },

    "/functions/v1/duffel-book": {
      "post": {
        "tags": ["Travel — Flights"],
        "summary": "Book a flight",
        "operationId": "duffelBook",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["offer_id", "passengers"],
                "properties": {
                  "offer_id": { "type": "string" },
                  "passengers": { "type": "array", "items": { "$ref": "#/components/schemas/Passenger" } }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Booking confirmed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "order_id": { "type": "string" },
                    "booking_reference": { "type": "string" },
                    "status": { "type": "string" }
                  }
                }
              }
            }
          }
        }
      }
    },

    "/functions/v1/duffel-orders": {
      "get": {
        "tags": ["Travel — Flights"],
        "summary": "List flight orders",
        "operationId": "duffelOrders",
        "responses": {
          "200": { "description": "Orders list", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/TravelBooking" } } } } }
        }
      }
    },

    "/functions/v1/duffel-cancel": {
      "post": {
        "tags": ["Travel — Flights"],
        "summary": "Cancel a flight booking",
        "operationId": "duffelCancel",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": { "type": "object", "required": ["order_id"], "properties": { "order_id": { "type": "string" } } }
            }
          }
        },
        "responses": { "200": { "description": "Cancellation processed" } }
      }
    },

    "/functions/v1/duffel-stays-search": {
      "post": {
        "tags": ["Travel — Stays"],
        "summary": "Search hotel stays (Duffel Stays)",
        "operationId": "duffelStaysSearch",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["check_in_date", "check_out_date", "rooms"],
                "properties": {
                  "check_in_date": { "type": "string", "format": "date" },
                  "check_out_date": { "type": "string", "format": "date" },
                  "rooms": { "type": "integer" },
                  "guests_per_room": { "type": "integer", "default": 2 },
                  "location": { "type": "string", "example": "gaborone" }
                }
              }
            }
          }
        },
        "responses": { "200": { "description": "Available stays" } }
      }
    },

    "/functions/v1/duffel-stays-book": {
      "post": {
        "tags": ["Travel — Stays"],
        "summary": "Book a hotel stay",
        "operationId": "duffelStaysBook",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["accommodation_id", "rate_id", "guests"],
                "properties": {
                  "accommodation_id": { "type": "string" },
                  "rate_id": { "type": "string" },
                  "guests": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "given_name": { "type": "string" },
                        "family_name": { "type": "string" },
                        "email": { "type": "string", "format": "email" }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": { "200": { "description": "Stay booked" } }
      }
    },

    "/functions/v1/hotelbeds-hotels": {
      "post": {
        "tags": ["Travel — HotelBeds"],
        "summary": "Search hotels (HotelBeds)",
        "operationId": "hotelbedsHotels",
        "description": "Search hotels, activities, and airport transfers via HotelBeds.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["check_in_date", "check_out_date", "location"],
                "properties": {
                  "check_in_date": { "type": "string", "format": "date" },
                  "check_out_date": { "type": "string", "format": "date" },
                  "location": { "type": "string" },
                  "num_rooms": { "type": "integer", "default": 1 }
                }
              }
            }
          }
        },
        "responses": { "200": { "description": "Hotels" } }
      }
    },

    "/functions/v1/hotelbeds-activities": {
      "post": {
        "tags": ["Travel — HotelBeds"],
        "summary": "Search activities",
        "operationId": "hotelbedsActivities",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "location": { "type": "string" },
                  "category": { "type": "string", "example": "sightseeing" }
                }
              }
            }
          }
        },
        "responses": { "200": { "description": "Activities" } }
      }
    },

    "/functions/v1/hotelbeds-transfers": {
      "post": {
        "tags": ["Travel — HotelBeds"],
        "summary": "Search airport transfers",
        "operationId": "hotelbedsTransfers",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["pick_up_location", "drop_off_location", "pick_up_date"],
                "properties": {
                  "pick_up_location": { "type": "string" },
                  "drop_off_location": { "type": "string" },
                  "pick_up_date": { "type": "string", "format": "date" }
                }
              }
            }
          }
        },
        "responses": { "200": { "description": "Transfer options" } }
      }
    },

    "/functions/v1/cf-upload": {
      "post": {
        "tags": ["File Storage"],
        "summary": "Get a presigned upload URL",
        "operationId": "cfUpload",
        "description": "Returns a presigned S3 URL. Upload the file directly to that URL with a `PUT` request — no auth header on the PUT.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["file_type", "content_type"],
                "properties": {
                  "file_type": { "type": "string", "enum": ["avatar", "receipt", "doc", "statement", "invoice"] },
                  "content_type": { "type": "string", "example": "image/jpeg" }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Presigned URL",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "presigned_url": { "type": "string", "format": "uri" },
                    "key": { "type": "string", "description": "Store this key to retrieve the file later" }
                  }
                }
              }
            }
          }
        }
      }
    },

    "/functions/v1/cf-file": {
      "get": {
        "tags": ["File Storage"],
        "summary": "Get a signed download URL",
        "operationId": "cfFile",
        "parameters": [{ "name": "key", "in": "query", "required": true, "schema": { "type": "string" }, "example": "avatars/USER_ID.jpg" }],
        "responses": {
          "200": {
            "description": "Signed URL (valid 1 hour)",
            "content": { "application/json": { "schema": { "type": "object", "properties": { "url": { "type": "string", "format": "uri" } } } } }
          }
        }
      }
    },

    "/functions/v1/cf-manage": {
      "delete": {
        "tags": ["File Storage"],
        "summary": "Delete a file from R2",
        "operationId": "cfManage",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": { "type": "object", "required": ["key"], "properties": { "key": { "type": "string" } } }
            }
          }
        },
        "responses": { "200": { "description": "File deleted" } }
      }
    },

    "/functions/v1/duffel-webhook": {
      "post": {
        "tags": ["Webhooks"],
        "summary": "Duffel event webhook",
        "operationId": "duffelWebhook",
        "security": [],
        "description": "Registered with Duffel. Verifies `x-duffel-signature` before processing events like `order.updated` and `order.cancelled`.",
        "parameters": [{ "name": "x-duffel-signature", "in": "header", "required": true, "schema": { "type": "string" } }],
        "responses": { "200": { "description": "Processed" }, "403": { "description": "Invalid signature" } }
      }
    },

    "/functions/v1/documenso-webhook": {
      "post": {
        "tags": ["Webhooks"],
        "summary": "Documenso signature webhook",
        "operationId": "documensoWebhook",
        "security": [],
        "parameters": [{ "name": "x-documenso-signature", "in": "header", "required": true, "schema": { "type": "string" } }],
        "responses": { "200": { "description": "Processed" }, "403": { "description": "Invalid signature" } }
      }
    }
  }
}
}