{
    "openapi": "3.0.3",
    "info": {
        "title": "SYSNAV M-Pesa Payment Gateway API",
        "version": "1.0.0",
        "description": "A multi-tenant payment gateway that unifies **Safaricom Daraja**, **KCB Buni** and **Equity Jenga** behind one REST API.\n\n## Authentication\nProtected endpoints accept either:\n- an **API key** via the `X-API-Key` header (or `?apiKey=` query param), or\n- a **JWT** via `Authorization: Bearer <token>` (issued after OTP login).\n\n## Providers\nThe same request body works for all three rails — the gateway routes to whichever provider is stored on the client's active credential record.",
        "contact": {
            "name": "SYSNAV Support",
            "email": "support@navipos.co.ke"
        }
    },
    "servers": [
        {
            "url": "https://payments.navipos.co.ke",
            "description": "Current environment"
        }
    ],
    "tags": [
        {
            "name": "System",
            "description": "Health and diagnostics"
        },
        {
            "name": "Authentication",
            "description": "Registration, login and OTP verification"
        },
        {
            "name": "Clients",
            "description": "Client account and provider credentials"
        },
        {
            "name": "Payments",
            "description": "STK push, C2B and transactions"
        },
        {
            "name": "QR Payments",
            "description": "Public scan-to-pay endpoints"
        },
        {
            "name": "Webhooks",
            "description": "Inbound provider callbacks"
        },
        {
            "name": "SMS",
            "description": "SMS credentials, templates and campaigns"
        },
        {
            "name": "Email",
            "description": "SMTP configuration and delivery"
        },
        {
            "name": "Dashboard",
            "description": "Aggregate statistics"
        },
        {
            "name": "Reconciliation",
            "description": "Settlement reconciliation"
        }
    ],
    "components": {
        "securitySchemes": {
            "ApiKeyAuth": {
                "type": "apiKey",
                "in": "header",
                "name": "X-API-Key"
            },
            "BearerAuth": {
                "type": "http",
                "scheme": "bearer",
                "bearerFormat": "JWT"
            }
        },
        "schemas": {
            "Error": {
                "type": "object",
                "properties": {
                    "error": {
                        "type": "string",
                        "example": "Unauthorized"
                    },
                    "message": {
                        "type": "string",
                        "example": "API key or JWT token required"
                    }
                }
            },
            "RegisterClient": {
                "type": "object",
                "required": [
                    "name",
                    "businessType",
                    "email",
                    "phone",
                    "password"
                ],
                "properties": {
                    "name": {
                        "type": "string",
                        "example": "My Business Ltd"
                    },
                    "businessType": {
                        "type": "string",
                        "example": "Retail"
                    },
                    "email": {
                        "type": "string",
                        "format": "email",
                        "example": "owner@mybusiness.com"
                    },
                    "phone": {
                        "type": "string",
                        "example": "254712345678"
                    },
                    "password": {
                        "type": "string",
                        "format": "password",
                        "example": "S3cure!pass"
                    }
                }
            },
            "DarajaCredentials": {
                "type": "object",
                "required": [
                    "consumerKey",
                    "consumerSecret"
                ],
                "description": "consumerKey and consumerSecret are always required. shortCode, passkey and initiatorPassword are also required unless provider is kcb. For provider=equity, initiatorPassword must contain an RSA private key in PEM format.",
                "properties": {
                    "provider": {
                        "type": "string",
                        "enum": [
                            "daraja",
                            "kcb",
                            "equity"
                        ],
                        "default": "daraja"
                    },
                    "consumerKey": {
                        "type": "string"
                    },
                    "consumerSecret": {
                        "type": "string"
                    },
                    "shortCode": {
                        "type": "string",
                        "example": "174379"
                    },
                    "passkey": {
                        "type": "string"
                    },
                    "initiatorName": {
                        "type": "string",
                        "example": "testapi"
                    },
                    "initiatorPassword": {
                        "type": "string",
                        "description": "Equity/Jenga: RSA private key in PEM format. Daraja/KCB: initiator password."
                    },
                    "environment": {
                        "type": "string",
                        "enum": [
                            "sandbox",
                            "production"
                        ],
                        "default": "sandbox"
                    }
                }
            },
            "StkPush": {
                "type": "object",
                "required": [
                    "phoneNumber",
                    "amount"
                ],
                "properties": {
                    "phoneNumber": {
                        "type": "string",
                        "example": "254712345678"
                    },
                    "amount": {
                        "type": "number",
                        "example": 1000
                    },
                    "reference": {
                        "type": "string",
                        "example": "ORDER-123456",
                        "description": "Optional — a random reference is generated if omitted."
                    },
                    "description": {
                        "type": "string",
                        "example": "Payment for order #123456",
                        "description": "Optional — defaults to \"STK Push Payment\"."
                    },
                    "telco": {
                        "type": "string",
                        "enum": [
                            "Safaricom",
                            "Equitel"
                        ],
                        "description": "Only used when provider is equity."
                    }
                }
            },
            "Transaction": {
                "type": "object",
                "properties": {
                    "id": {
                        "type": "string"
                    },
                    "amount": {
                        "type": "number"
                    },
                    "paidAmount": {
                        "type": "number",
                        "nullable": true
                    },
                    "phone": {
                        "type": "string"
                    },
                    "status": {
                        "type": "string",
                        "enum": [
                            "pending",
                            "initiated",
                            "completed",
                            "failed",
                            "cancelled"
                        ]
                    },
                    "transactionType": {
                        "type": "string"
                    },
                    "provider": {
                        "type": "string",
                        "enum": [
                            "daraja",
                            "kcb",
                            "equity"
                        ]
                    },
                    "accountReference": {
                        "type": "string"
                    },
                    "checkoutRequestId": {
                        "type": "string",
                        "nullable": true
                    },
                    "mpesaReceiptNumber": {
                        "type": "string",
                        "nullable": true
                    },
                    "customerName": {
                        "type": "string"
                    },
                    "responseCode": {
                        "type": "string",
                        "nullable": true
                    },
                    "responseMessage": {
                        "type": "string",
                        "nullable": true
                    },
                    "statusReason": {
                        "type": "string",
                        "nullable": true
                    },
                    "createdAt": {
                        "type": "string",
                        "format": "date-time"
                    },
                    "completedAt": {
                        "type": "string",
                        "format": "date-time",
                        "nullable": true
                    }
                }
            }
        }
    },
    "paths": {
        "/api/v1/health": {
            "get": {
                "tags": [
                    "System"
                ],
                "summary": "Get health",
                "security": [],
                "description": "Auto-generated from the live route table — a hand-written example has not been added for this endpoint yet.",
                "responses": {
                    "200": {
                        "description": "Success",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/clients": {
            "post": {
                "tags": [
                    "Authentication"
                ],
                "summary": "Register a new client account",
                "security": [],
                "description": "Creates an account in \"inactive\" status and sends a 6-digit OTP by SMS and email. Verify it with POST /api/v1/auth/verify-otp before the account can be used — it moves to \"pending\" (awaiting admin approval) after verification.",
                "responses": {
                    "400": {
                        "description": "Error",
                        "content": {
                            "application/json": {
                                "example": {
                                    "error": "Missing required fields: email"
                                }
                            }
                        }
                    },
                    "409": {
                        "description": "Error",
                        "content": {
                            "application/json": {
                                "example": {
                                    "error": "Email already registered"
                                }
                            }
                        }
                    }
                }
            },
            "get": {
                "tags": [
                    "Clients"
                ],
                "summary": "Get your own account details",
                "security": [
                    {
                        "ApiKeyAuth": []
                    },
                    {
                        "BearerAuth": []
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Success",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/auth/login": {
            "post": {
                "tags": [
                    "Authentication"
                ],
                "summary": "Log in with email and password",
                "security": [],
                "description": "Verifies credentials and sends a 6-digit OTP by SMS and email. Complete the login with POST /api/v1/auth/verify-otp.",
                "responses": {
                    "401": {
                        "description": "Error",
                        "content": {
                            "application/json": {
                                "example": {
                                    "error": "Invalid email or password"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/auth/send-otp": {
            "post": {
                "tags": [
                    "Authentication"
                ],
                "summary": "Resend a login/verification OTP",
                "security": [],
                "responses": {
                    "200": {
                        "description": "Success",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/auth/verify-otp": {
            "post": {
                "tags": [
                    "Authentication"
                ],
                "summary": "Verify an OTP and receive a session token",
                "security": [],
                "description": "Confirms the 6-digit code sent by /clients (registration) or /auth/login. On success returns a JWT plus the account's permanent API key.",
                "responses": {
                    "400": {
                        "description": "Error",
                        "content": {
                            "application/json": {
                                "example": {
                                    "error": "Invalid or expired verification code"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/webhooks/mpesa": {
            "post": {
                "tags": [
                    "Webhooks"
                ],
                "summary": "Safaricom Daraja callback",
                "security": [],
                "description": "Called by Safaricom, not by your app. Configure this URL with Safaricom, not the other way around.",
                "responses": {
                    "200": {
                        "description": "Success",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/webhooks/kcb": {
            "post": {
                "tags": [
                    "Webhooks"
                ],
                "summary": "KCB Buni callback",
                "security": [],
                "description": "Called by KCB, not by your app.",
                "responses": {
                    "200": {
                        "description": "Success",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/webhooks/equity": {
            "post": {
                "tags": [
                    "Webhooks"
                ],
                "summary": "Equity Jenga callback",
                "security": [],
                "description": "Called by Equity, not by your app.",
                "responses": {
                    "200": {
                        "description": "Success",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/webhooks/coop": {
            "post": {
                "tags": [
                    "Webhooks"
                ],
                "summary": "Co-operative Bank callback",
                "security": [],
                "description": "Called by Co-op Bank, not by your app.",
                "responses": {
                    "200": {
                        "description": "Success",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/webhooks/kopokopo": {
            "post": {
                "tags": [
                    "Webhooks"
                ],
                "summary": "Kopo Kopo callback",
                "security": [],
                "description": "Called by Kopo Kopo, not by your app — handles both the per-STK-push result and the buygoods_transaction_received webhook subscription event.",
                "responses": {
                    "200": {
                        "description": "Success",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/c2b-validation": {
            "post": {
                "tags": [
                    "Webhooks"
                ],
                "summary": "C2B validation callback",
                "security": [],
                "description": "Called by Safaricom before a C2B payment is confirmed, then relayed to the validationUrl you registered with POST /api/v1/c2b-register.",
                "responses": {
                    "200": {
                        "description": "Success",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/c2b-confirmation": {
            "post": {
                "tags": [
                    "Webhooks"
                ],
                "summary": "C2B confirmation callback",
                "security": [],
                "description": "Called by Safaricom once a C2B payment settles, then relayed to the confirmationUrl you registered with POST /api/v1/c2b-register.",
                "responses": {
                    "200": {
                        "description": "Success",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/clients/{id}/credentials": {
            "post": {
                "tags": [
                    "Clients"
                ],
                "summary": "Store your provider credentials",
                "security": [
                    {
                        "ApiKeyAuth": []
                    },
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Encrypted at rest. Required fields depend on provider: kcb needs only consumerKey/consumerSecret; daraja and equity also need shortCode, passkey and initiatorPassword (for equity, initiatorPassword must be an RSA private key in PEM format); coop needs shortCode, passkey and a callback ipnUsername/ipnPassword pair, but no initiatorPassword.",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "ID"
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "example": {
                                "provider": "daraja",
                                "consumerKey": "your_consumer_key",
                                "consumerSecret": "your_consumer_secret",
                                "shortCode": "174379",
                                "passkey": "your_passkey",
                                "initiatorPassword": "your_initiator_password",
                                "environment": "sandbox"
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Error",
                        "content": {
                            "application/json": {
                                "example": {
                                    "success": true,
                                    "message": "Credentials saved"
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Error",
                        "content": {
                            "application/json": {
                                "example": {
                                    "error": "Missing fields: shortCode, passkey"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            },
            "get": {
                "tags": [
                    "Clients"
                ],
                "summary": "Get your stored provider credentials",
                "security": [
                    {
                        "ApiKeyAuth": []
                    },
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Security note: this endpoint returns your secrets (consumerSecret, passkey, initiatorPassword) in plaintext so you can confirm what's stored — treat the response as sensitive, don't log it, and only call it over HTTPS.",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "ID"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Error",
                        "content": {
                            "application/json": {
                                "example": {
                                    "status": "success",
                                    "data": {
                                        "provider": "daraja",
                                        "environment": "sandbox",
                                        "consumerKey": "your_consumer_key",
                                        "consumerSecret": "your_consumer_secret",
                                        "shortCode": "174379",
                                        "passkey": "your_passkey",
                                        "initiatorName": "",
                                        "initiatorPassword": "your_initiator_password"
                                    }
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Error",
                        "content": {
                            "application/json": {
                                "example": {
                                    "error": "Credentials not configured",
                                    "message": "Credentials not configured"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/clients/{id}/smtp": {
            "post": {
                "tags": [
                    "Email"
                ],
                "summary": "Save your SMTP configuration",
                "security": [
                    {
                        "ApiKeyAuth": []
                    },
                    {
                        "BearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "ID"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Success",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            },
            "get": {
                "tags": [
                    "Email"
                ],
                "summary": "Get your SMTP configuration",
                "security": [
                    {
                        "ApiKeyAuth": []
                    },
                    {
                        "BearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "ID"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Success",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/email/send": {
            "post": {
                "tags": [
                    "Email"
                ],
                "summary": "Send an email via your configured SMTP",
                "security": [
                    {
                        "ApiKeyAuth": []
                    },
                    {
                        "BearerAuth": []
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Success",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/clients/{id}/sms-config": {
            "post": {
                "tags": [
                    "SMS"
                ],
                "summary": "Save your SMS provider configuration",
                "security": [
                    {
                        "ApiKeyAuth": []
                    },
                    {
                        "BearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "ID"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Success",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            },
            "get": {
                "tags": [
                    "SMS"
                ],
                "summary": "Get your SMS provider configuration",
                "security": [
                    {
                        "ApiKeyAuth": []
                    },
                    {
                        "BearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "ID"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Success",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/sms/balance": {
            "get": {
                "tags": [
                    "SMS"
                ],
                "summary": "Get your SMS account balance",
                "security": [
                    {
                        "ApiKeyAuth": []
                    },
                    {
                        "BearerAuth": []
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Success",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/sms/onfon/groups": {
            "get": {
                "tags": [
                    "SMS"
                ],
                "summary": "List OnfonMedia contact groups",
                "security": [
                    {
                        "ApiKeyAuth": []
                    },
                    {
                        "BearerAuth": []
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Success",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            },
            "post": {
                "tags": [
                    "SMS"
                ],
                "summary": "Create an OnfonMedia contact group",
                "security": [
                    {
                        "ApiKeyAuth": []
                    },
                    {
                        "BearerAuth": []
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Success",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/sms/onfon/groups/{id}": {
            "put": {
                "tags": [
                    "SMS"
                ],
                "summary": "Update an OnfonMedia contact group",
                "security": [
                    {
                        "ApiKeyAuth": []
                    },
                    {
                        "BearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "ID"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Success",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            },
            "delete": {
                "tags": [
                    "SMS"
                ],
                "summary": "Delete an OnfonMedia contact group",
                "security": [
                    {
                        "ApiKeyAuth": []
                    },
                    {
                        "BearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "ID"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Success",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/sms/onfon/templates": {
            "get": {
                "tags": [
                    "SMS"
                ],
                "summary": "List OnfonMedia templates",
                "security": [
                    {
                        "ApiKeyAuth": []
                    },
                    {
                        "BearerAuth": []
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Success",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            },
            "post": {
                "tags": [
                    "SMS"
                ],
                "summary": "Create an OnfonMedia template",
                "security": [
                    {
                        "ApiKeyAuth": []
                    },
                    {
                        "BearerAuth": []
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Success",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/sms/onfon/templates/{id}": {
            "put": {
                "tags": [
                    "SMS"
                ],
                "summary": "Update an OnfonMedia template",
                "security": [
                    {
                        "ApiKeyAuth": []
                    },
                    {
                        "BearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "ID"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Success",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            },
            "delete": {
                "tags": [
                    "SMS"
                ],
                "summary": "Delete an OnfonMedia template",
                "security": [
                    {
                        "ApiKeyAuth": []
                    },
                    {
                        "BearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "ID"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Success",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/sms/templates": {
            "get": {
                "tags": [
                    "SMS"
                ],
                "summary": "List your locally-saved templates",
                "security": [
                    {
                        "ApiKeyAuth": []
                    },
                    {
                        "BearerAuth": []
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Success",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            },
            "post": {
                "tags": [
                    "SMS"
                ],
                "summary": "Save a local template",
                "security": [
                    {
                        "ApiKeyAuth": []
                    },
                    {
                        "BearerAuth": []
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Success",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/sms/templates/{id}": {
            "delete": {
                "tags": [
                    "SMS"
                ],
                "summary": "Delete a local template",
                "security": [
                    {
                        "ApiKeyAuth": []
                    },
                    {
                        "BearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "ID"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Success",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/sms/send": {
            "post": {
                "tags": [
                    "SMS"
                ],
                "summary": "Send an SMS campaign",
                "security": [
                    {
                        "ApiKeyAuth": []
                    },
                    {
                        "BearerAuth": []
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "example": {
                                "recipients": [
                                    "254712345678"
                                ],
                                "message": "Your order has shipped!",
                                "scheduleAt": null,
                                "flash": false,
                                "unicode": false
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Success",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/sms/campaigns": {
            "get": {
                "tags": [
                    "SMS"
                ],
                "summary": "List your SMS campaign history",
                "security": [
                    {
                        "ApiKeyAuth": []
                    },
                    {
                        "BearerAuth": []
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Success",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/sms/campaigns/{id}": {
            "get": {
                "tags": [
                    "SMS"
                ],
                "summary": "Get a single SMS campaign's detail",
                "security": [
                    {
                        "ApiKeyAuth": []
                    },
                    {
                        "BearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "ID"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Success",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/pay/{clientId}/info": {
            "get": {
                "tags": [
                    "QR Payments"
                ],
                "summary": "Get merchant display info for a scan-to-pay page",
                "security": [],
                "description": "Public — no auth. Used by pay.php when a customer scans a merchant's QR code.",
                "parameters": [
                    {
                        "name": "clientId",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Client ID"
                    }
                ],
                "responses": {
                    "404": {
                        "description": "Error",
                        "content": {
                            "application/json": {
                                "example": {
                                    "error": "Merchant not found"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/pay/{clientId}/initiate": {
            "post": {
                "tags": [
                    "QR Payments"
                ],
                "summary": "Initiate a scan-to-pay STK push",
                "security": [],
                "description": "Public — no auth. phoneNumber and amount are required; reference defaults to \"NaviPay\".",
                "parameters": [
                    {
                        "name": "clientId",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Client ID"
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "example": {
                                "phoneNumber": "0712345678",
                                "amount": 100,
                                "reference": "NaviPay"
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Success",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/pay/{clientId}/status/{checkoutId}": {
            "get": {
                "tags": [
                    "QR Payments"
                ],
                "summary": "Poll a scan-to-pay payment's status",
                "security": [],
                "description": "Public — no auth.",
                "parameters": [
                    {
                        "name": "clientId",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Client ID"
                    },
                    {
                        "name": "checkoutId",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Checkout ID"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Success",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/stk-push": {
            "post": {
                "tags": [
                    "Payments"
                ],
                "summary": "Trigger an M-Pesa STK push",
                "security": [
                    {
                        "ApiKeyAuth": []
                    },
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Only phoneNumber and amount are required — reference defaults to a generated ID and description defaults to \"STK Push Payment\" if omitted. Routes automatically to whichever provider (Daraja/KCB/Equity) is stored on your credentials.",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "example": {
                                "phoneNumber": "254712345678",
                                "amount": 1000,
                                "reference": "ORDER-1042",
                                "description": "Payment for order #1042"
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Error",
                        "content": {
                            "application/json": {
                                "example": {
                                    "data": {
                                        "transactionId": "a1b2c3...",
                                        "checkoutRequestId": "ws_CO_...",
                                        "merchantRequestId": "29115-...",
                                        "responseCode": "0",
                                        "customerMessage": "Success. Request accepted for processing",
                                        "message": "STK push sent. Check phone for M-Pesa prompt.",
                                        "status": "pending",
                                        "amount": 1000,
                                        "phone": "254712345678"
                                    }
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Error",
                        "content": {
                            "application/json": {
                                "example": {
                                    "error": "Credentials not configured",
                                    "message": "Payment credentials not configured for this client",
                                    "responseCode": "NO_CREDENTIALS"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/c2b-register": {
            "post": {
                "tags": [
                    "Payments"
                ],
                "summary": "Register your C2B (Pay Bill / Till) callback URLs",
                "security": [
                    {
                        "ApiKeyAuth": []
                    },
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Safaricom will call validationUrl then confirmationUrl on your own server for every C2B payment to your shortCode.",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "example": {
                                "shortCode": "174379",
                                "validationUrl": "https://example.com/payments/validation",
                                "confirmationUrl": "https://example.com/payments/confirmation"
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Error",
                        "content": {
                            "application/json": {
                                "example": {
                                    "data": {
                                        "status": "success",
                                        "message": "C2B URLs registered successfully",
                                        "responseCode": "0",
                                        "responseDescription": "Success",
                                        "environment": "sandbox"
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/c2b-simulate": {
            "post": {
                "tags": [
                    "Payments"
                ],
                "summary": "Simulate a C2B payment (sandbox only)",
                "security": [
                    {
                        "ApiKeyAuth": []
                    },
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Lets you trigger your own registered C2B webhook for testing, without a real Safaricom payment.",
                "responses": {
                    "200": {
                        "description": "Success",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/c2b-verify": {
            "get": {
                "tags": [
                    "Payments"
                ],
                "summary": "Verify C2B registration status",
                "security": [
                    {
                        "ApiKeyAuth": []
                    },
                    {
                        "BearerAuth": []
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Success",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/transactions": {
            "get": {
                "tags": [
                    "Payments"
                ],
                "summary": "List your transactions",
                "security": [
                    {
                        "ApiKeyAuth": []
                    },
                    {
                        "BearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "name": "status",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Filter by status: pending, initiated, completed, failed, cancelled"
                    },
                    {
                        "name": "type",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Filter by transaction type"
                    },
                    {
                        "name": "limit",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "type": "integer"
                        },
                        "description": "Max 100, defaults to 50"
                    },
                    {
                        "name": "offset",
                        "in": "query",
                        "required": false,
                        "schema": {
                            "type": "integer"
                        },
                        "description": "Defaults to 0"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Error",
                        "content": {
                            "application/json": {
                                "example": {
                                    "data": [
                                        {
                                            "id": "a1b2c3...",
                                            "amount": 1000,
                                            "paidAmount": 1000,
                                            "phone": "254712345678",
                                            "status": "completed",
                                            "transactionType": "stk_push",
                                            "provider": "daraja",
                                            "accountReference": "ORDER-1042",
                                            "checkoutRequestId": "ws_CO_...",
                                            "mpesaReceiptNumber": "NLJ7RT61SV",
                                            "customerName": "JOHN DOE",
                                            "responseCode": "0",
                                            "responseMessage": "The service request is processed successfully.",
                                            "statusReason": null,
                                            "createdAt": "2026-01-15 10:30:00",
                                            "completedAt": "2026-01-15 10:30:42"
                                        }
                                    ],
                                    "pagination": {
                                        "limit": 50,
                                        "offset": 0,
                                        "count": 1,
                                        "total": 1
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/transactions/{id}": {
            "get": {
                "tags": [
                    "Payments"
                ],
                "summary": "Get a single transaction",
                "security": [
                    {
                        "ApiKeyAuth": []
                    },
                    {
                        "BearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "ID"
                    }
                ],
                "responses": {
                    "404": {
                        "description": "Error",
                        "content": {
                            "application/json": {
                                "example": {
                                    "error": "Transaction not found"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/dashboard/stats": {
            "get": {
                "tags": [
                    "Dashboard"
                ],
                "summary": "Get aggregate statistics for your account",
                "security": [
                    {
                        "ApiKeyAuth": []
                    },
                    {
                        "BearerAuth": []
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Success",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/reconciliation/report": {
            "get": {
                "tags": [
                    "Reconciliation"
                ],
                "summary": "Get the settlement reconciliation report",
                "security": [
                    {
                        "ApiKeyAuth": []
                    },
                    {
                        "BearerAuth": []
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Success",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/reconciliation/mismatches": {
            "get": {
                "tags": [
                    "Reconciliation"
                ],
                "summary": "List reconciliation mismatches",
                "security": [
                    {
                        "ApiKeyAuth": []
                    },
                    {
                        "BearerAuth": []
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Success",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/reconciliation/resolve/{id}": {
            "post": {
                "tags": [
                    "Reconciliation"
                ],
                "summary": "Mark a mismatch as resolved",
                "security": [
                    {
                        "ApiKeyAuth": []
                    },
                    {
                        "BearerAuth": []
                    }
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "ID"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Success",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/api/v1/reconciliation/run": {
            "post": {
                "tags": [
                    "Reconciliation"
                ],
                "summary": "Trigger a reconciliation run",
                "security": [
                    {
                        "ApiKeyAuth": []
                    },
                    {
                        "BearerAuth": []
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Success",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "type": "object"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Error"
                                }
                            }
                        }
                    }
                }
            }
        }
    },
    "x-generated": {
        "source": "public/api.php (live route table + inline docblocks)",
        "endpoints": 49,
        "generatedAt": "2026-09-01T15:57:51-04:00"
    }
}