{
    "openapi": "3.1.0",
    "info": {
        "title": "Cashmora API",
        "version": "1.0.0",
        "description": "Mobile money collections, payouts and hosted checkout for Uganda. Full guide: https://docs.cashmora.co.ug",
        "contact": {
            "email": "support@pearl-host.com"
        }
    },
    "servers": [
        {
            "url": "https://api.cashmora.co.ug"
        }
    ],
    "components": {
        "securitySchemes": {
            "bearerAuth": {
                "type": "http",
                "scheme": "bearer",
                "description": "Secret key: cm_test_sk_… (sandbox) or cm_live_sk_… (live)"
            }
        }
    },
    "security": [
        {
            "bearerAuth": []
        }
    ],
    "paths": {
        "/v1/ping": {
            "get": {
                "operationId": "ping",
                "summary": "Check your key",
                "description": "Connectivity check. Send your key to confirm it works and see its mode.\n\nNo key needed. If you send one, the answer says whether it is `valid` and its `mode`.",
                "tags": [
                    "Getting started"
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "status": "ok",
                                    "data": {
                                        "service": "cashmora-api",
                                        "version": "v1",
                                        "time": "2026-10-28T09:00:00Z",
                                        "key": "valid",
                                        "mode": "sandbox"
                                    }
                                }
                            }
                        }
                    }
                },
                "security": []
            }
        },
        "/v1/collections": {
            "post": {
                "operationId": "createCollection",
                "summary": "Request a payment",
                "description": "Send a mobile money prompt to a customer's phone.\n\nThe customer approves with their PIN; you learn the result by [webhook](#webhooks) or with [GET](#get-collection). The response comes back immediately with status `pending`.\n\n> Important: Only deliver goods when the status is `successful`. Treat `unknown` as \"still waiting\", never as failed.",
                "tags": [
                    "Collections"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "request_id": {
                                        "type": "string",
                                        "description": "Your unique id for **this attempt** (8–100 characters: letters, numbers, `- _ . :`; a UUID works). Retry with the same value; never reuse it for another payment. Also accepted as the `Idempotency-Key` header."
                                    },
                                    "phone": {
                                        "type": "string",
                                        "description": "Customer's MTN or Airtel number."
                                    },
                                    "amount": {
                                        "type": "integer",
                                        "description": "UGX 500 – 5,000,000."
                                    },
                                    "reference": {
                                        "type": "string",
                                        "description": "Your order or invoice number (≤ 100). **Can repeat** across attempts and part payments."
                                    },
                                    "description": {
                                        "type": "string",
                                        "description": "What the payment is for (≤ 255)."
                                    },
                                    "payer_name": {
                                        "type": "string",
                                        "description": "Customer's name, for your records (≤ 120)."
                                    },
                                    "fee_paid_by": {
                                        "type": "string",
                                        "description": "`merchant` (fee taken from what you receive) or `customer` (prompt asks for amount + fee). Defaults to your setting in Products → Mobile Collections."
                                    },
                                    "callback_url": {
                                        "type": "string",
                                        "description": "Public HTTPS URL. This payment's events are POSTed here as well as to your webhooks. See [callback_url](#callback-url)."
                                    },
                                    "country": {
                                        "type": "string",
                                        "description": "ISO code, e.g. `UG`. Must be your account's country."
                                    }
                                },
                                "required": [
                                    "request_id",
                                    "phone",
                                    "amount"
                                ]
                            },
                            "example": {
                                "request_id": "3f8e4c1a-6b2d-4f7e-9a10-5c3b2e1d0f9a",
                                "phone": "0770000001",
                                "amount": 5000,
                                "reference": "ORDER-1042",
                                "description": "Order #1042",
                                "payer_name": "Jane Namuli"
                            }
                        }
                    }
                },
                "responses": {
                    "202": {
                        "description": "Accepted",
                        "content": {
                            "application/json": {
                                "example": {
                                    "status": "success",
                                    "data": {
                                        "id": "CM-COL-8F3K2Q9XAB",
                                        "object": "collection",
                                        "status": "pending",
                                        "amount": 5000,
                                        "fee": 100,
                                        "net_amount": 4900,
                                        "currency": "UGX",
                                        "phone": "+256770000001",
                                        "network": "MTN",
                                        "request_id": "3f8e4c1a-6b2d-4f7e-9a10-5c3b2e1d0f9a",
                                        "callback_url": null,
                                        "reference": "ORDER-1042",
                                        "description": "Order #1042",
                                        "channel": "api",
                                        "fee_paid_by": "merchant",
                                        "customer": {
                                            "name": "Jane Namuli",
                                            "email": null,
                                            "note": null
                                        },
                                        "checkout": null,
                                        "provider_reference": null,
                                        "failure_reason": null,
                                        "mode": "sandbox",
                                        "created_at": "2026-10-23T09:15:00Z",
                                        "completed_at": null
                                    }
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Validation failed",
                        "content": {
                            "application/json": {
                                "example": {
                                    "status": "error",
                                    "error": {
                                        "code": "validation_failed",
                                        "message": "Some details need your attention.",
                                        "fields": {
                                            "phone": "Only MTN and Airtel numbers can pay with mobile money.",
                                            "amount": "Amount must be between UGX 500 and UGX 5,000,000."
                                        }
                                    }
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Bad key",
                        "content": {
                            "application/json": {
                                "example": {
                                    "status": "error",
                                    "error": {
                                        "code": "unauthenticated",
                                        "message": "Send a valid API key: Authorization: Bearer cm_test_sk_..."
                                    }
                                }
                            }
                        }
                    }
                }
            },
            "get": {
                "operationId": "listCollections",
                "summary": "List payments",
                "description": "Newest first, with filters.\n\nFind every payment for an order with `?reference=ORDER-1042&status=successful` and add up `amount` to see how much of the order is paid. Find an attempt your server never heard back about with `?request_id=…`.",
                "tags": [
                    "Collections"
                ],
                "parameters": [
                    {
                        "name": "limit",
                        "in": "query",
                        "required": false,
                        "description": "1–100, default 20.",
                        "schema": {
                            "type": "integer"
                        }
                    },
                    {
                        "name": "status",
                        "in": "query",
                        "required": false,
                        "description": "`pending`, `processing`, `successful`, `failed`, `expired`, `unknown`.",
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "reference",
                        "in": "query",
                        "required": false,
                        "description": "Your order number.",
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "request_id",
                        "in": "query",
                        "required": false,
                        "description": "Your attempt id.",
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "starting_after",
                        "in": "query",
                        "required": false,
                        "description": "An `id` from the previous page, for the next page.",
                        "schema": {
                            "type": "string"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "status": "success",
                                    "data": [
                                        {
                                            "id": "CM-COL-8F3K2Q9XAB",
                                            "status": "successful",
                                            "amount": 140000,
                                            "reference": "OR-0097878",
                                            "...": "..."
                                        }
                                    ],
                                    "meta": {
                                        "has_more": false
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/v1/collections/{id}": {
            "get": {
                "operationId": "getCollection",
                "summary": "Get a payment",
                "description": "One payment by its Cashmora id.\n\nAsking also nudges a waiting payment: if the network has answered, you get the final status straight away.",
                "tags": [
                    "Collections"
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "The `id` from the create response, e.g. `CM-COL-8F3K2Q9XAB`.",
                        "schema": {
                            "type": "string"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "status": "success",
                                    "data": {
                                        "id": "CM-COL-8F3K2Q9XAB",
                                        "object": "collection",
                                        "status": "successful",
                                        "amount": 5000,
                                        "fee": 100,
                                        "net_amount": 4900,
                                        "currency": "UGX",
                                        "phone": "+256770000001",
                                        "network": "MTN",
                                        "request_id": "3f8e4c1a-6b2d-4f7e-9a10-5c3b2e1d0f9a",
                                        "callback_url": null,
                                        "reference": "ORDER-1042",
                                        "description": "Order #1042",
                                        "channel": "api",
                                        "fee_paid_by": "merchant",
                                        "customer": {
                                            "name": "Jane Namuli",
                                            "email": null,
                                            "note": null
                                        },
                                        "checkout": null,
                                        "provider_reference": null,
                                        "failure_reason": null,
                                        "mode": "sandbox",
                                        "created_at": "2026-10-23T09:15:00Z",
                                        "completed_at": "2026-10-23T09:15:07Z"
                                    }
                                }
                            }
                        }
                    },
                    "404": {
                        "description": "Not found",
                        "content": {
                            "application/json": {
                                "example": {
                                    "status": "error",
                                    "error": {
                                        "code": "not_found",
                                        "message": "No collection with that id."
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/v1/checkouts": {
            "post": {
                "operationId": "createCheckout",
                "summary": "Create a checkout",
                "description": "A short, embeddable payment page for an exact amount and your reference.\n\nCreate the checkout on your **server**, then show its `url` inside your page (iframe or modal), in a popup, or redirect to it. The page is compact (it fits a 360–440 px wide box), lets the customer pick how to pay (Mobile Money now, cards soon), and can only be paid once. You are told the result by [webhook](#webhooks), by a [browser message](#embed-checkout) and, optionally, by a redirect.",
                "tags": [
                    "Hosted checkout"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "amount": {
                                        "type": "integer",
                                        "description": "UGX 500 – 5,000,000. The customer can't change it."
                                    },
                                    "reference": {
                                        "type": "string",
                                        "description": "Your order number (≤ 100). Shown to the customer and returned on every event."
                                    },
                                    "description": {
                                        "type": "string",
                                        "description": "What the payment is for (≤ 255)."
                                    },
                                    "customer": {
                                        "type": "object",
                                        "properties": {
                                            "name": {
                                                "type": "string",
                                                "description": "Saved as the payer name."
                                            },
                                            "email": {
                                                "type": "string",
                                                "description": "Receives the receipt."
                                            },
                                            "phone": {
                                                "type": "string",
                                                "description": "Pre-fills the number (the customer can change it)."
                                            }
                                        },
                                        "description": "Optional details about the payer."
                                    },
                                    "redirect_url": {
                                        "type": "string",
                                        "description": "HTTPS. After a successful payment the page goes here with `?checkout=…&reference=…&status=paid&payment=…`."
                                    },
                                    "cancel_url": {
                                        "type": "string",
                                        "description": "HTTPS. Where \"Cancel\" goes, with `status=cancelled`."
                                    },
                                    "callback_url": {
                                        "type": "string",
                                        "description": "HTTPS. This checkout's payment events are POSTed here too."
                                    },
                                    "expires_in_minutes": {
                                        "type": "integer",
                                        "description": "5–1440, default 60."
                                    },
                                    "fee_paid_by": {
                                        "type": "string",
                                        "description": "`merchant` or `customer`; defaults to your setting."
                                    },
                                    "request_id": {
                                        "type": "string",
                                        "description": "Send the same value on a retry to get the same checkout back."
                                    }
                                },
                                "required": [
                                    "amount"
                                ]
                            },
                            "example": {
                                "amount": 45000,
                                "reference": "INV-2041",
                                "description": "Order #2041",
                                "customer": {
                                    "name": "Jane Namuli",
                                    "email": "jane@example.com"
                                },
                                "redirect_url": "https://yourshop.com/orders/2041/thanks"
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "Created",
                        "content": {
                            "application/json": {
                                "example": {
                                    "status": "success",
                                    "data": {
                                        "id": "chkQ3x9sT2mVb8LwP4nR6yA1",
                                        "object": "checkout",
                                        "status": "open",
                                        "amount": 45000,
                                        "currency": "UGX",
                                        "reference": "INV-2041",
                                        "description": "Order #2041",
                                        "url": "https://app.cashmora.co.ug/checkout/chkQ3x9sT2mVb8LwP4nR6yA1",
                                        "redirect_url": "https://yourshop.com/orders/2041/thanks",
                                        "cancel_url": null,
                                        "expires_at": "2026-10-28T10:15:00Z",
                                        "paid_at": null,
                                        "payment": null,
                                        "mode": "sandbox",
                                        "created_at": "2026-10-28T09:15:00Z"
                                    }
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Validation failed",
                        "content": {
                            "application/json": {
                                "example": {
                                    "status": "error",
                                    "error": {
                                        "code": "validation_failed",
                                        "message": "Some details need your attention.",
                                        "fields": {
                                            "phone": "Only MTN and Airtel numbers can pay with mobile money.",
                                            "amount": "Amount must be between UGX 500 and UGX 5,000,000."
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/v1/checkouts/{id}": {
            "get": {
                "operationId": "getCheckout",
                "summary": "Get a checkout",
                "description": "Status of a checkout, with the payment once paid.\n\n`open` means still waiting (failed attempts can be retried on the same page). `paid` includes the successful payment under `payment`.",
                "tags": [
                    "Hosted checkout"
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "The checkout `id`.",
                        "schema": {
                            "type": "string"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "status": "success",
                                    "data": {
                                        "id": "chkQ3x9sT2mVb8LwP4nR6yA1",
                                        "object": "checkout",
                                        "status": "paid",
                                        "amount": 45000,
                                        "currency": "UGX",
                                        "reference": "INV-2041",
                                        "description": "Order #2041",
                                        "url": "https://app.cashmora.co.ug/checkout/chkQ3x9sT2mVb8LwP4nR6yA1",
                                        "redirect_url": "https://yourshop.com/orders/2041/thanks",
                                        "cancel_url": null,
                                        "expires_at": "2026-10-28T10:15:00Z",
                                        "paid_at": "2026-10-28T09:17:41Z",
                                        "payment": {
                                            "id": "CM-COL-2H7P4K9QZD",
                                            "status": "successful",
                                            "amount": 45000,
                                            "reference": "INV-2041",
                                            "checkout": "chkQ3x9sT2mVb8LwP4nR6yA1",
                                            "...": "..."
                                        },
                                        "mode": "sandbox",
                                        "created_at": "2026-10-28T09:15:00Z"
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/v1/payouts": {
            "post": {
                "operationId": "createPayout",
                "summary": "Send a payout",
                "description": "Send money from your balance to an MTN or Airtel number.\n\nNeeds a subscription to **Transfers**, a key created with **Allow payouts**, and a unique `request_id`. The amount plus fee is **reserved** from your balance immediately; a failed payout returns all of it.\n\n> Warning: If a payout is `unknown`, the money stays reserved while we keep checking. Don't send the same payout again with a new `request_id`: wait for the final status.",
                "tags": [
                    "Payouts"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "request_id": {
                                        "type": "string",
                                        "description": "Your unique id for **this payout**, e.g. a payroll line id. Retry with the same value."
                                    },
                                    "phone": {
                                        "type": "string",
                                        "description": "Recipient's MTN or Airtel number."
                                    },
                                    "amount": {
                                        "type": "integer",
                                        "description": "What the recipient receives (UGX 500 – 5,000,000). The fee is added on top."
                                    },
                                    "name": {
                                        "type": "string",
                                        "description": "Recipient's name (≤ 120)."
                                    },
                                    "reference": {
                                        "type": "string",
                                        "description": "Your payroll, invoice or claim number (≤ 100)."
                                    },
                                    "description": {
                                        "type": "string",
                                        "description": "Reason, shown to the recipient (≤ 255)."
                                    },
                                    "callback_url": {
                                        "type": "string",
                                        "description": "Public HTTPS URL for this payout's result."
                                    },
                                    "country": {
                                        "type": "string",
                                        "description": "e.g. `UG`."
                                    }
                                },
                                "required": [
                                    "request_id",
                                    "phone",
                                    "amount"
                                ]
                            },
                            "example": {
                                "request_id": "salary-2026-10-emp-0042",
                                "phone": "0770000001",
                                "amount": 50000,
                                "name": "Jane Namuli",
                                "reference": "PAY-001",
                                "description": "October salary"
                            }
                        }
                    }
                },
                "responses": {
                    "202": {
                        "description": "Accepted",
                        "content": {
                            "application/json": {
                                "example": {
                                    "status": "success",
                                    "data": {
                                        "id": "CM-PAY-7KQ2M9XB4C",
                                        "object": "payout",
                                        "status": "pending",
                                        "amount": 50000,
                                        "fee": 1000,
                                        "total": 51000,
                                        "currency": "UGX",
                                        "phone": "+256770000001",
                                        "network": "MTN",
                                        "name": "Jane Namuli",
                                        "request_id": "salary-2026-10-emp-0042",
                                        "reference": "PAY-001",
                                        "description": "October salary",
                                        "channel": "api",
                                        "batch": null,
                                        "provider_reference": null,
                                        "failure_reason": null,
                                        "mode": "sandbox",
                                        "created_at": "2026-10-23T09:20:00Z",
                                        "completed_at": null
                                    }
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Not enough balance",
                        "content": {
                            "application/json": {
                                "example": {
                                    "status": "error",
                                    "error": {
                                        "code": "insufficient_balance",
                                        "message": "Your available balance is UGX 20,000. This payout needs UGX 51,000."
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/v1/payouts/{id}": {
            "get": {
                "operationId": "getPayout",
                "summary": "Get or list payouts",
                "description": "One payout by id. `GET /v1/payouts` lists them with the same filters as payments.\n\nList with `GET /v1/payouts?reference=PAY-001` or `?request_id=…`.",
                "tags": [
                    "Payouts"
                ],
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "required": true,
                        "description": "e.g. `CM-PAY-7KQ2M9XB4C`.",
                        "schema": {
                            "type": "string"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "status": "success",
                                    "data": {
                                        "id": "CM-PAY-7KQ2M9XB4C",
                                        "object": "payout",
                                        "status": "successful",
                                        "amount": 50000,
                                        "fee": 1000,
                                        "total": 51000,
                                        "currency": "UGX",
                                        "phone": "+256770000001",
                                        "network": "MTN",
                                        "name": "Jane Namuli",
                                        "request_id": "salary-2026-10-emp-0042",
                                        "reference": "PAY-001",
                                        "description": "October salary",
                                        "channel": "api",
                                        "batch": null,
                                        "provider_reference": null,
                                        "failure_reason": null,
                                        "mode": "sandbox",
                                        "created_at": "2026-10-23T09:20:00Z",
                                        "completed_at": null
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/v1/phone/verify": {
            "post": {
                "operationId": "verifyPhone",
                "summary": "Verify a phone number",
                "description": "Check a number before you charge or pay it.\n\nTells you whether it's a valid MTN/Airtel number, whether that network is open for payments right now, and the name it is registered to (when known). Free; 60 checks a minute per key. `GET /v1/phone/verify?phone=…` works too.\n\n`name_lookup` is `found`, `not_found` or `unavailable` (the name service didn't answer; you can still use the number). Invalid numbers return `valid: false` with a `reason`.",
                "tags": [
                    "Account & tools"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "type": "object",
                                "properties": {
                                    "phone": {
                                        "type": "string",
                                        "description": "Any common format."
                                    }
                                },
                                "required": [
                                    "phone"
                                ]
                            },
                            "example": {
                                "phone": "0772123456"
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "status": "success",
                                    "data": {
                                        "phone": "+256772123456",
                                        "valid": true,
                                        "network": "MTN",
                                        "can_collect": true,
                                        "can_pay_out": true,
                                        "notice": null,
                                        "registered_name": "Jane Namuli",
                                        "name_verified": true,
                                        "name_lookup": "found",
                                        "mode": "sandbox"
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/v1/balance": {
            "get": {
                "operationId": "getBalance",
                "summary": "Balance",
                "description": "Your balance in the key's mode.\n\n`available` can be paid out or withdrawn. `pending` is payments waiting for customers to approve. `reserved` is held for payouts in progress.",
                "tags": [
                    "Account & tools"
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "status": "success",
                                    "data": {
                                        "currency": "UGX",
                                        "available": 245000,
                                        "pending": 15000,
                                        "reserved": 51000,
                                        "mode": "sandbox"
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        "/v1/transactions": {
            "get": {
                "operationId": "listTransactions",
                "summary": "List transactions",
                "description": "Payments and payouts together, newest first.\n\nPage through with `starting_after` while `meta.has_more` is true.",
                "tags": [
                    "Account & tools"
                ],
                "parameters": [
                    {
                        "name": "limit",
                        "in": "query",
                        "required": false,
                        "description": "1–100, default 20.",
                        "schema": {
                            "type": "integer"
                        }
                    },
                    {
                        "name": "type",
                        "in": "query",
                        "required": false,
                        "description": "`collection` or `payout`.",
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "status",
                        "in": "query",
                        "required": false,
                        "description": "Any status.",
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "reference",
                        "in": "query",
                        "required": false,
                        "description": "Your order number.",
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "request_id",
                        "in": "query",
                        "required": false,
                        "description": "Your attempt id.",
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "starting_after",
                        "in": "query",
                        "required": false,
                        "description": "Last `id` of the previous page.",
                        "schema": {
                            "type": "string"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "example": {
                                    "status": "success",
                                    "data": [
                                        {
                                            "id": "CM-PAY-7KQ2M9XB4C",
                                            "object": "payout",
                                            "status": "successful",
                                            "...": "..."
                                        }
                                    ],
                                    "meta": {
                                        "has_more": true
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}