{
  "openapi": "3.1.0",
  "info": {
    "title": "AutoPayKe API",
    "version": "1.0.0",
    "description": "Pay out to M-Pesa, Airtel Money, Tills, PayBills and Kenyan banks from a USDC balance on Avalanche. Partner endpoints take an API key (Authorization: Bearer apk_test_… or apk_live_…; the prefix picks sandbox or live). Account endpoints take a signed-in user's bearer token; public endpoints need nothing. Every response is { ok: true, data } or { ok: false, error, code, requestId }. Payout amounts are KES cents (integers); USDC amounts are decimal strings with 6 places. Money-moving POSTs need an Idempotency-Key header.",
    "contact": {
      "name": "AutoPayKe developers",
      "email": "developers@autopayke.com",
      "url": "https://www.autopayke.com/developers"
    }
  },
  "servers": [
    {
      "url": "https://api.autopayke.com"
    }
  ],
  "tags": [
    {
      "name": "Account, balance and banks",
      "description": "Who the key belongs to, its limits and today's usage, the balance payouts are paid from, and the bank codes payouts accept."
    },
    {
      "name": "Payouts",
      "description": "Send KES to M-Pesa, Airtel Money, Till (Buy Goods), PayBill and Kenyan bank accounts, paid from your USDC balance. Quote first, then create, then follow the status."
    },
    {
      "name": "Recipients",
      "description": "Check who owns a phone number or bank account before you pay it."
    },
    {
      "name": "Transactions and receipts",
      "description": "Everything that moved money in the workspace (API payouts and anything else on the account), with receipts you can send to recipients or auditors."
    },
    {
      "name": "Events",
      "description": "A record of everything that happened to your objects. The same events your webhooks receive; read them here to catch up after downtime."
    },
    {
      "name": "Webhook endpoints",
      "description": "Manage where we send events, from code. The Console does the same; use whichever suits your deploys."
    },
    {
      "name": "Sandbox",
      "description": "Test-only helpers: top up your test balance and force a test payout into any status. They answer NOT_FOUND to live keys."
    },
    {
      "name": "Capabilities",
      "description": "Where money can come from and go to right now: countries, methods, mobile money operators and per-transaction limits."
    },
    {
      "name": "Fees",
      "description": "The prices AutoPayKe publishes, and an itemised quote for any amount, the same figures the app charges against."
    },
    {
      "name": "Payment links (payer)",
      "description": "The public side of a payment link: what the payer sees, paying it, and following the payment. No account is needed to pay."
    },
    {
      "name": "Top-ups",
      "description": "Money in: add funds to an AutoPayKe balance from mobile money, card or bank transfer, then follow the top-up until it is credited."
    },
    {
      "name": "Balance",
      "description": "What the account holds, what it can send and withdraw, and anything on hold."
    },
    {
      "name": "Payout accounts",
      "description": "Saved places to send money out to: mobile money numbers and bank accounts, with the name on the account checked before you save it."
    },
    {
      "name": "Saved cards",
      "description": "References to cards used for top-ups: brand and last four digits only. Cards are saved by the hosted checkout, never by posting card details."
    },
    {
      "name": "Payment requests",
      "description": "Ask anyone to pay you: create a link, share it, and see when it's paid. The payer side is under Payment links (payer)."
    }
  ],
  "paths": {
    "/api/v1/account": {
      "get": {
        "operationId": "getAccount",
        "summary": "Get the account",
        "description": "The workspace and key behind this request, your limits, and how much of today's and this month's limit you've used.\n\nScope: account:read\n\nLimits here are in whole shillings, unlike payout amounts, which are cents.\n\nDays and months follow Africa/Nairobi time.",
        "tags": [
          "Account, balance and banks"
        ],
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "x-scope": "account:read",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "ok": true,
                  "data": {
                    "object": "account",
                    "livemode": false,
                    "workspace": {
                      "id": "2f6c1a4e-7b3d-4e58-9c21-5a8d0e7f3b64",
                      "name": "Acme Payroll",
                      "kind": "merchant",
                      "tier": "starter",
                      "live_access": true
                    },
                    "key": {
                      "prefix": "apk_test_7Hq2aB",
                      "name": "Production server",
                      "scopes": [
                        "account:read",
                        "payouts:read",
                        "payouts:write"
                      ],
                      "expires_at": "2027-11-03T09:10:00.000Z"
                    },
                    "limits": {
                      "per_payout_kes": 150000,
                      "daily_kes": 1000000,
                      "monthly_kes": 10000000,
                      "requests_per_second": 10,
                      "methods": [
                        "mobile_money",
                        "till",
                        "paybill",
                        "bank"
                      ],
                      "countries": [
                        "KE"
                      ]
                    },
                    "usage": {
                      "today_kes": 45000,
                      "this_month_kes": 820000,
                      "remaining_today_kes": 955000,
                      "remaining_this_month_kes": 9180000
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/balance": {
      "get": {
        "operationId": "getBalance",
        "summary": "Get the balance",
        "description": "Amounts are USDC-equivalent minor units (6 decimals). display repeats them in the account's local currency at the current rate, for showing to people.",
        "tags": [
          "Balance"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "ok": true,
                  "data": {
                    "total": "125400000",
                    "availableToSend": "125400000",
                    "availableToWithdraw": "100400000",
                    "onHold": [
                      {
                        "amount": "25000000",
                        "releaseAt": "2026-11-05T12:00:00.000Z",
                        "reason": "card_new_card"
                      }
                    ],
                    "onHoldTotal": "25000000",
                    "display": {
                      "currency": "KES",
                      "rate": "129.10",
                      "total": "16189.14",
                      "availableToSend": "16189.14",
                      "availableToWithdraw": "12961.64",
                      "onHoldTotal": "3227.50"
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/banks": {
      "get": {
        "operationId": "listBanks",
        "summary": "List banks",
        "description": "Kenyan banks you can pay out to, with the bank_code a bank destination needs. Cache it for a day.\n\nAny valid key can call this; no scope needed.",
        "tags": [
          "Account, balance and banks"
        ],
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "ok": true,
                  "data": {
                    "object": "list",
                    "data": [
                      {
                        "code": "KCBLKENX",
                        "name": "KCB Bank"
                      },
                      {
                        "code": "EQBLKENA",
                        "name": "Equity Bank"
                      },
                      {
                        "code": "KCOOKENA",
                        "name": "Co-operative Bank"
                      }
                    ]
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/payouts/quote": {
      "post": {
        "operationId": "quotePayout",
        "summary": "Quote a payout",
        "description": "Price a payout without sending it: the rate, our fee and the total USDC it will take from your balance. It also checks the destination and your limits, so a quote that succeeds means the payout would be accepted now.\n\nScope: payouts:read\n\nErrors: VALIDATION_ERROR, RECIPIENT_INVALID, METHOD_UNAVAILABLE, LIMIT_PER_TX_EXCEEDED, LIMIT_DAILY_EXCEEDED, LIMIT_MONTHLY_EXCEEDED, CAPABILITY_PAUSED\n\nA quote isn't a reservation: it doesn't hold money or a rate. It's free and doesn't count against limits.",
        "tags": [
          "Payouts"
        ],
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "x-scope": "payouts:read",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "amount",
                  "currency",
                  "destination"
                ],
                "properties": {
                  "amount": {
                    "type": "integer",
                    "examples": [
                      "150000"
                    ],
                    "description": "What the recipient receives, in KES cents, whole shillings only (a multiple of 100). 150000 sends KES 1,500. Fees are added on top and taken from your balance."
                  },
                  "currency": {
                    "type": "string",
                    "enum": [
                      "KES"
                    ],
                    "description": "Always \"KES\"."
                  },
                  "source_token": {
                    "type": "string",
                    "enum": [
                      "USDC"
                    ],
                    "description": "Defaults to \"USDC\"."
                  },
                  "destination": {
                    "type": "object",
                    "description": "Where to send the money. Pick one shape by \"type\". mobile_money: { type, phone, network?, name? } where phone is a Kenyan mobile number in any common format (+254712345678, 0712345678), network is \"safaricom\" (default) or \"airtel\", and name is the name you expect on the account (optional, used for checks). till: { type, till_number } with a 5 to 8 digit Buy Goods number. paybill: { type, paybill_number, account_number } with a 5 to 7 digit PayBill and the account number the business gave (1 to 20 characters). bank: { type, bank_code, account_number, account_name } where bank_code comes from GET /api/v1/banks, account_number is 6 to 34 characters and account_name is the holder's name."
                  }
                }
              },
              "example": {
                "amount": 150000,
                "currency": "KES",
                "destination": {
                  "type": "mobile_money",
                  "phone": "+254700000001"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "ok": true,
                  "data": {
                    "object": "payout_quote",
                    "livemode": false,
                    "amount": 150000,
                    "currency": "KES",
                    "source_token": "USDC",
                    "rate": "128.80",
                    "amount_usdc": "11.646000",
                    "fee_usdc": "0.302310",
                    "total_usdc": "11.948310",
                    "fee_breakdown": {
                      "service_fee_usdc": "0.291150",
                      "network_fee_usdc": "0.011160",
                      "payout_cost_usdc": "0.000000"
                    },
                    "recipient_name": null,
                    "expires_at": "2026-11-03T09:11:00.000Z"
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/payouts": {
      "post": {
        "operationId": "createPayout",
        "summary": "Create a payout",
        "description": "Send money. We check the destination, your limits and your balance, take the total from your balance and start the payout. It returns at once with status pending; the result arrives by webhook, usually within a minute for mobile money.\n\nScope: payouts:write\n\nErrors: IDEMPOTENCY_KEY_REQUIRED, IDEMPOTENCY_KEY_REUSED, IDEMPOTENCY_IN_PROGRESS, VALIDATION_ERROR, RECIPIENT_INVALID, METHOD_UNAVAILABLE, LIMIT_PER_TX_EXCEEDED, LIMIT_DAILY_EXCEEDED, LIMIT_MONTHLY_EXCEEDED, INSUFFICIENT_BALANCE, CAPABILITY_PAUSED\n\nA retry with the same Idempotency-Key and body returns the first response, with the header Idempotent-Replayed: true. The same key with a different body is refused with IDEMPOTENCY_KEY_REUSED.\n\nIf the request fails validation or a limit, nothing is charged and the Idempotency-Key is released, so you can fix the request and retry with the same key.\n\nLive keys that can create payouts must have an IP allowlist; calls from other addresses get IP_NOT_ALLOWED.\n\nSandbox: +254700000001 succeeds, +254700000002 fails, +254700000003 goes to in_review, +254700000004 succeeds then reverses, an amount of 13300 (KES 133) times out after a minute, and account number 0000000000 fails. Anything else succeeds about 5 seconds later.",
        "tags": [
          "Payouts"
        ],
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "x-scope": "payouts:write",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "description": "8 to 128 characters (letters, digits, . _ : -). A retry with the same key and body returns the first response for 24 hours.",
            "schema": {
              "type": "string",
              "minLength": 8,
              "maxLength": 128
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "amount",
                  "currency",
                  "destination"
                ],
                "properties": {
                  "amount": {
                    "type": "integer",
                    "examples": [
                      "150000"
                    ],
                    "description": "What the recipient receives, in KES cents, whole shillings only (a multiple of 100). 150000 sends KES 1,500. Fees are added on top and taken from your balance."
                  },
                  "currency": {
                    "type": "string",
                    "enum": [
                      "KES"
                    ],
                    "description": "Always \"KES\"."
                  },
                  "source_token": {
                    "type": "string",
                    "enum": [
                      "USDC"
                    ],
                    "description": "Defaults to \"USDC\"."
                  },
                  "destination": {
                    "type": "object",
                    "description": "Where to send the money. Pick one shape by \"type\". mobile_money: { type, phone, network?, name? } where phone is a Kenyan mobile number in any common format (+254712345678, 0712345678), network is \"safaricom\" (default) or \"airtel\", and name is the name you expect on the account (optional, used for checks). till: { type, till_number } with a 5 to 8 digit Buy Goods number. paybill: { type, paybill_number, account_number } with a 5 to 7 digit PayBill and the account number the business gave (1 to 20 characters). bank: { type, bank_code, account_number, account_name } where bank_code comes from GET /api/v1/banks, account_number is 6 to 34 characters and account_name is the holder's name."
                  },
                  "reference": {
                    "type": "string",
                    "description": "Your own id for this payout, up to 64 characters (an invoice, order or payroll line). Shown on the payout and searchable. Not unique: use Idempotency-Key for that."
                  },
                  "description": {
                    "type": "string",
                    "description": "Up to 140 characters, for your records."
                  },
                  "metadata": {
                    "type": "object",
                    "description": "Up to 20 string key-value pairs (keys up to 40 characters, values up to 500). Returned on the payout, its events and webhooks. Don't put secrets or card numbers here."
                  }
                }
              },
              "example": {
                "amount": 150000,
                "currency": "KES",
                "destination": {
                  "type": "mobile_money",
                  "phone": "+254700000001",
                  "network": "safaricom",
                  "name": "Jane Wanjiku"
                },
                "reference": "order_1042",
                "description": "Rider payout, week 44",
                "metadata": {
                  "rider_id": "r_5521"
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "ok": true,
                  "data": {
                    "id": "po_8Fq2mZx7Lk3Rt9Vb",
                    "object": "payout",
                    "livemode": false,
                    "status": "pending",
                    "amount": 150000,
                    "currency": "KES",
                    "source_token": "USDC",
                    "debited_usdc": "11.948310",
                    "fee_usdc": "0.302310",
                    "rate": "128.80",
                    "destination": {
                      "type": "mobile_money",
                      "network": "safaricom",
                      "phone": "+254 700 ••• 001"
                    },
                    "recipient_name": "Jane Wanjiku",
                    "reference": "order_1042",
                    "description": "Rider payout, week 44",
                    "metadata": {
                      "rider_id": "r_5521"
                    },
                    "failure_code": null,
                    "failure_message": null,
                    "created": "2026-11-03T09:10:00.000Z",
                    "updated": "2026-11-03T09:10:00.000Z"
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "get": {
        "operationId": "listPayouts",
        "summary": "List payouts",
        "description": "Your payouts, newest first, in the key's environment. Filter by status, your reference or a time window.\n\nScope: payouts:read\n\nTo walk every page: call with limit=100, then repeat with starting_after set to the last id while has_more is true.",
        "tags": [
          "Payouts"
        ],
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "x-scope": "payouts:read",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "examples": [
                "20"
              ],
              "description": "How many to return, 1 to 100. Default 20."
            }
          },
          {
            "name": "starting_after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "description": "Cursor for the next page: the id of the last item you got. Lists are newest first; when has_more is true, pass the last id here."
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "processing",
                "succeeded",
                "failed",
                "reversed",
                "in_review"
              ],
              "description": "Only payouts in this status."
            }
          },
          {
            "name": "reference",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "description": "Only payouts with this reference."
            }
          },
          {
            "name": "created_gte",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "description": "Created at or after this ISO 8601 time."
            }
          },
          {
            "name": "created_lt",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "description": "Created before this ISO 8601 time."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "ok": true,
                  "data": {
                    "object": "list",
                    "data": [
                      {
                        "id": "po_8Fq2mZx7Lk3Rt9Vb",
                        "object": "payout",
                        "livemode": false,
                        "status": "succeeded",
                        "amount": 150000,
                        "currency": "KES",
                        "source_token": "USDC",
                        "debited_usdc": "11.948310",
                        "fee_usdc": "0.302310",
                        "rate": "128.80",
                        "destination": {
                          "type": "mobile_money",
                          "network": "safaricom",
                          "phone": "+254 700 ••• 001"
                        },
                        "recipient_name": "Jane Wanjiku",
                        "reference": "order_1042",
                        "description": "Rider payout, week 44",
                        "metadata": {
                          "rider_id": "r_5521"
                        },
                        "failure_code": null,
                        "failure_message": null,
                        "created": "2026-11-03T09:10:00.000Z",
                        "updated": "2026-11-03T09:10:00.000Z"
                      }
                    ],
                    "has_more": false
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/payouts/{id}": {
      "get": {
        "operationId": "getPayout",
        "summary": "Get a payout",
        "description": "One payout by id, with its current status. Use it to recover after a missed webhook or a timeout.\n\nScope: payouts:read\n\nErrors: NOT_FOUND\n\nA test key can't see live payouts and the other way round: you'll get NOT_FOUND.",
        "tags": [
          "Payouts"
        ],
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "x-scope": "payouts:read",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "The payout id (po_…)."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "ok": true,
                  "data": {
                    "id": "po_8Fq2mZx7Lk3Rt9Vb",
                    "object": "payout",
                    "livemode": false,
                    "status": "succeeded",
                    "amount": 150000,
                    "currency": "KES",
                    "source_token": "USDC",
                    "debited_usdc": "11.948310",
                    "fee_usdc": "0.302310",
                    "rate": "128.80",
                    "destination": {
                      "type": "mobile_money",
                      "network": "safaricom",
                      "phone": "+254 700 ••• 001"
                    },
                    "recipient_name": "Jane Wanjiku",
                    "reference": "order_1042",
                    "description": "Rider payout, week 44",
                    "metadata": {
                      "rider_id": "r_5521"
                    },
                    "failure_code": null,
                    "failure_message": null,
                    "created": "2026-11-03T09:10:00.000Z",
                    "updated": "2026-11-03T09:10:00.000Z"
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/recipients/verify": {
      "post": {
        "operationId": "verifyRecipient",
        "summary": "Verify a recipient",
        "description": "Looks up the name on an M-Pesa, Airtel Money or bank account. Send the name you expect and we tell you whether it matches. Use it when someone gives you their number for the first time.\n\nScope: recipients:read\n\nErrors: VALIDATION_ERROR, RECIPIENT_INVALID, RATE_LIMITED\n\nRate limit: 30 a minute per key\n\nSandbox: every number resolves to JANE WANJIKU DOE except +254700000002, and every account except 0000000000.\n\nChecking many numbers that don't resolve looks like sweeping for valid accounts and alerts our team. Only check numbers people gave you.",
        "tags": [
          "Recipients"
        ],
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "x-scope": "recipients:read",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "type"
                ],
                "properties": {
                  "type": {
                    "type": "string",
                    "enum": [
                      "mobile_money",
                      "bank"
                    ],
                    "description": "What to check."
                  },
                  "phone": {
                    "type": "string",
                    "description": "mobile_money only: the Kenyan mobile number."
                  },
                  "network": {
                    "type": "string",
                    "enum": [
                      "safaricom",
                      "airtel"
                    ],
                    "description": "mobile_money only. Default \"safaricom\"."
                  },
                  "bank_code": {
                    "type": "string",
                    "description": "bank only: from GET /api/v1/banks."
                  },
                  "account_number": {
                    "type": "string",
                    "description": "bank only: 6 to 34 characters."
                  },
                  "name": {
                    "type": "string",
                    "description": "The name you expect. When sent, name_match tells you how close it is."
                  }
                }
              },
              "example": {
                "type": "mobile_money",
                "phone": "+254712345678",
                "name": "Jane Doe"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "ok": true,
                  "data": {
                    "object": "recipient_check",
                    "livemode": false,
                    "resolved": true,
                    "account_name": "JANE WANJIKU DOE",
                    "name_match": "matched"
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/transactions": {
      "get": {
        "operationId": "listTransactions",
        "summary": "List transactions",
        "description": "The workspace's money movements, newest first. In test mode these are your sandbox payouts.\n\nScope: transactions:read",
        "tags": [
          "Transactions and receipts"
        ],
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "x-scope": "transactions:read",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "examples": [
                "20"
              ],
              "description": "How many to return, 1 to 100. Default 20."
            }
          },
          {
            "name": "starting_after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "description": "Cursor for the next page: the id of the last item you got. Lists are newest first; when has_more is true, pass the last id here."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "ok": true,
                  "data": {
                    "object": "list",
                    "data": [
                      {
                        "id": "TXN-8KQ2-ZP7L",
                        "object": "transaction",
                        "livemode": true,
                        "type": "payout",
                        "direction": "debit",
                        "status": "succeeded",
                        "amount_usdc": "11.646000",
                        "fee_usdc": "0.302310",
                        "amount_local": 150000,
                        "currency": "KES",
                        "payout": "po_8Fq2mZx7Lk3Rt9Vb",
                        "counterparty": {
                          "name": "JANE WANJIKU DOE",
                          "phone": "+254 712 ••• 678",
                          "till_number": null
                        },
                        "created": "2026-11-03T09:10:00.000Z",
                        "settled": "2026-11-03T09:10:41.000Z"
                      }
                    ],
                    "has_more": false
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/transactions/{id}": {
      "get": {
        "operationId": "getTransaction",
        "summary": "Get a transaction",
        "description": "One transaction by its reference (or, in test mode, the payout id).\n\nScope: transactions:read\n\nErrors: NOT_FOUND",
        "tags": [
          "Transactions and receipts"
        ],
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "x-scope": "transactions:read",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "Transaction reference."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "ok": true,
                  "data": {
                    "id": "TXN-8KQ2-ZP7L",
                    "object": "transaction",
                    "livemode": true,
                    "type": "payout",
                    "direction": "debit",
                    "status": "succeeded",
                    "amount_local": 150000,
                    "currency": "KES",
                    "payout": "po_8Fq2mZx7Lk3Rt9Vb"
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/transactions/{id}/receipt": {
      "get": {
        "operationId": "getReceipt",
        "summary": "Get a receipt",
        "description": "A receipt for one transaction: who paid, who received, amounts and times, issued by AutoPayKe. Render it in your own template or store it with your books.\n\nScope: transactions:read\n\nErrors: NOT_FOUND",
        "tags": [
          "Transactions and receipts"
        ],
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "x-scope": "transactions:read",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "Transaction reference."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "ok": true,
                  "data": {
                    "object": "receipt",
                    "livemode": true,
                    "transaction": "TXN-8KQ2-ZP7L",
                    "issued": "2026-11-03T12:00:00.000Z",
                    "from": {
                      "name": "Acme Payroll",
                      "workspace": "2f6c1a4e-7b3d-4e58-9c21-5a8d0e7f3b64"
                    },
                    "to": {
                      "name": "JANE WANJIKU DOE",
                      "phone": "+254 712 ••• 678",
                      "till_number": null
                    },
                    "amount_local": 150000,
                    "currency": "KES",
                    "amount_usdc": "11.646000",
                    "fee_usdc": "0.302310",
                    "status": "succeeded",
                    "created": "2026-11-03T09:10:00.000Z",
                    "settled": "2026-11-03T09:10:41.000Z",
                    "issuer": "AutoPayKe"
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/events": {
      "get": {
        "operationId": "listEvents",
        "summary": "List events",
        "description": "Events in the key's environment, newest first. Filter by type or by the object they're about.\n\nScope: events:read",
        "tags": [
          "Events"
        ],
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "x-scope": "events:read",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "examples": [
                "20"
              ],
              "description": "How many to return, 1 to 100. Default 20."
            }
          },
          {
            "name": "starting_after",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "description": "Cursor for the next page: the id of the last item you got. Lists are newest first; when has_more is true, pass the last id here."
            }
          },
          {
            "name": "type",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "description": "Only this event type, e.g. payout.succeeded."
            }
          },
          {
            "name": "object_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "description": "Only events about this object, e.g. a payout id. Gives a payout's full history."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "ok": true,
                  "data": {
                    "object": "list",
                    "data": [
                      {
                        "id": "evt_3Jk8Qw2Zp7Lm4Nx6Rt1Vb9Yc",
                        "object": "event",
                        "type": "payout.succeeded",
                        "created": "2026-11-03T09:10:06.000Z",
                        "livemode": false,
                        "data": {
                          "object": {
                            "id": "po_8Fq2mZx7Lk3Rt9Vb",
                            "object": "payout",
                            "livemode": false,
                            "status": "succeeded",
                            "amount": 150000,
                            "currency": "KES",
                            "source_token": "USDC",
                            "debited_usdc": "11.948310",
                            "fee_usdc": "0.302310",
                            "rate": "128.80",
                            "destination": {
                              "type": "mobile_money",
                              "network": "safaricom",
                              "phone": "+254 700 ••• 001"
                            },
                            "recipient_name": "Jane Wanjiku",
                            "reference": "order_1042",
                            "description": "Rider payout, week 44",
                            "metadata": {
                              "rider_id": "r_5521"
                            },
                            "failure_code": null,
                            "failure_message": null,
                            "created": "2026-11-03T09:10:00.000Z",
                            "updated": "2026-11-03T09:10:06.000Z"
                          }
                        }
                      }
                    ],
                    "has_more": false
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/events/{id}": {
      "get": {
        "operationId": "getEvent",
        "summary": "Get an event",
        "description": "One event by id. Use it to check a webhook you received really came from us: fetch it by the id in the payload.\n\nScope: events:read\n\nErrors: NOT_FOUND",
        "tags": [
          "Events"
        ],
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "x-scope": "events:read",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "evt_…"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "ok": true,
                  "data": {
                    "id": "evt_3Jk8Qw2Zp7Lm4Nx6Rt1Vb9Yc",
                    "object": "event",
                    "type": "payout.succeeded",
                    "created": "2026-11-03T09:10:06.000Z",
                    "livemode": false,
                    "data": {
                      "object": {
                        "id": "po_8Fq2mZx7Lk3Rt9Vb",
                        "object": "payout",
                        "livemode": false,
                        "status": "succeeded",
                        "amount": 150000,
                        "currency": "KES",
                        "source_token": "USDC",
                        "debited_usdc": "11.948310",
                        "fee_usdc": "0.302310",
                        "rate": "128.80",
                        "destination": {
                          "type": "mobile_money",
                          "network": "safaricom",
                          "phone": "+254 700 ••• 001"
                        },
                        "recipient_name": "Jane Wanjiku",
                        "reference": "order_1042",
                        "description": "Rider payout, week 44",
                        "metadata": {
                          "rider_id": "r_5521"
                        },
                        "failure_code": null,
                        "failure_message": null,
                        "created": "2026-11-03T09:10:00.000Z",
                        "updated": "2026-11-03T09:10:06.000Z"
                      }
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/webhook-endpoints": {
      "get": {
        "operationId": "listWebhookEndpoints",
        "summary": "List endpoints",
        "description": "Your endpoints in this environment, with their success rate over the last 7 days.\n\nScope: webhooks:write",
        "tags": [
          "Webhook endpoints"
        ],
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "x-scope": "webhooks:write",
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "ok": true,
                  "data": {
                    "object": "list",
                    "data": [
                      {
                        "id": "1d7c3b5e-2a49-4f86-9a0e-6c2b8d4f1e73",
                        "url": "https://api.example.com/webhooks/autopayke",
                        "description": "Production listener",
                        "environment": "test",
                        "events": [
                          "payout.succeeded",
                          "payout.failed",
                          "payout.reversed"
                        ],
                        "status": "enabled",
                        "disabledReason": null,
                        "failingSince": null,
                        "secretPrefix": "whsec_9xKq2LmZ",
                        "createdAt": "2026-11-03T09:10:00.000Z",
                        "successRate7d": 0.99,
                        "deliveries7d": 412
                      }
                    ]
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "post": {
        "operationId": "createWebhookEndpoint",
        "summary": "Create an endpoint",
        "description": "Start sending events to a URL. The response includes the signing secret, once.\n\nScope: webhooks:write\n\nErrors: VALIDATION_ERROR\n\nStore secret in your secret manager. You can roll it, but we won't show this one again through the API (the Console can reveal it after a PIN check).",
        "tags": [
          "Webhook endpoints"
        ],
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "x-scope": "webhooks:write",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "url",
                  "events"
                ],
                "properties": {
                  "url": {
                    "type": "string",
                    "description": "An https URL on the public internet, up to 2048 characters. Private, local and link-local addresses are refused."
                  },
                  "events": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "The event types to send, or [\"*\"] for all, including types we add later."
                  },
                  "description": {
                    "type": "string",
                    "description": "Up to 200 characters."
                  }
                }
              },
              "example": {
                "url": "https://api.example.com/webhooks/autopayke",
                "events": [
                  "payout.succeeded",
                  "payout.failed",
                  "payout.reversed"
                ],
                "description": "Production listener"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "ok": true,
                  "data": {
                    "id": "1d7c3b5e-2a49-4f86-9a0e-6c2b8d4f1e73",
                    "url": "https://api.example.com/webhooks/autopayke",
                    "description": "Production listener",
                    "environment": "test",
                    "events": [
                      "payout.succeeded",
                      "payout.failed",
                      "payout.reversed"
                    ],
                    "status": "enabled",
                    "disabledReason": null,
                    "failingSince": null,
                    "secretPrefix": "whsec_9xKq2LmZ",
                    "createdAt": "2026-11-03T09:10:00.000Z",
                    "secret": "whsec_9xKq2LmZp4Rt7Vb1Nc8Yw3Jd6Hf0Gs5A"
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/webhook-endpoints/{id}": {
      "get": {
        "operationId": "getWebhookEndpoint",
        "summary": "Get an endpoint",
        "description": "One endpoint. failingSince is set while deliveries keep failing; after 3 days we disable it and set disabledReason.\n\nScope: webhooks:write\n\nErrors: NOT_FOUND",
        "tags": [
          "Webhook endpoints"
        ],
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "x-scope": "webhooks:write",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "Endpoint id."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "ok": true,
                  "data": {
                    "id": "1d7c3b5e-2a49-4f86-9a0e-6c2b8d4f1e73",
                    "url": "https://api.example.com/webhooks/autopayke",
                    "description": "Production listener",
                    "environment": "test",
                    "events": [
                      "payout.succeeded",
                      "payout.failed",
                      "payout.reversed"
                    ],
                    "status": "enabled",
                    "disabledReason": null,
                    "failingSince": null,
                    "secretPrefix": "whsec_9xKq2LmZ",
                    "createdAt": "2026-11-03T09:10:00.000Z"
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "patch": {
        "operationId": "updateWebhookEndpoint",
        "summary": "Update an endpoint",
        "description": "Change the URL, the events or the description, or turn it off and on. Turning a disabled endpoint back on clears its failure state.\n\nScope: webhooks:write\n\nErrors: VALIDATION_ERROR, NOT_FOUND",
        "tags": [
          "Webhook endpoints"
        ],
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "x-scope": "webhooks:write",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "Endpoint id."
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [],
                "properties": {
                  "url": {
                    "type": "string",
                    "description": "New https URL."
                  },
                  "events": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Replaces the list."
                  },
                  "description": {
                    "type": "string",
                    "description": "Up to 200 characters."
                  },
                  "enabled": {
                    "type": "boolean",
                    "description": "false stops deliveries; true starts them again."
                  }
                }
              },
              "example": {
                "enabled": true
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "ok": true,
                  "data": {
                    "id": "1d7c3b5e-2a49-4f86-9a0e-6c2b8d4f1e73",
                    "url": "https://api.example.com/webhooks/autopayke",
                    "description": "Production listener",
                    "environment": "test",
                    "events": [
                      "payout.succeeded",
                      "payout.failed",
                      "payout.reversed"
                    ],
                    "status": "enabled",
                    "disabledReason": null,
                    "failingSince": null,
                    "secretPrefix": "whsec_9xKq2LmZ",
                    "createdAt": "2026-11-03T09:10:00.000Z"
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "delete": {
        "operationId": "deleteWebhookEndpoint",
        "summary": "Delete an endpoint",
        "description": "Stop sending to this endpoint for good. Deliveries waiting for a retry are dropped; the events stay readable at GET /api/v1/events.\n\nScope: webhooks:write\n\nErrors: NOT_FOUND",
        "tags": [
          "Webhook endpoints"
        ],
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "x-scope": "webhooks:write",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "Endpoint id."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "ok": true,
                  "data": {
                    "id": "1d7c3b5e-2a49-4f86-9a0e-6c2b8d4f1e73",
                    "deleted": true
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/webhook-endpoints/{id}/roll-secret": {
      "post": {
        "operationId": "rollWebhookSecret",
        "summary": "Roll the signing secret",
        "description": "Issue a new signing secret. The old one stops at once, so deploy code that accepts both before you roll, then remove the old one.\n\nScope: webhooks:write\n\nErrors: NOT_FOUND",
        "tags": [
          "Webhook endpoints"
        ],
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "x-scope": "webhooks:write",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "Endpoint id."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "ok": true,
                  "data": {
                    "secret": "whsec_Lm4Nx6Rt1Vb9Yc3Jk8Qw2Zp7Fq2mZx7L"
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/webhook-endpoints/{id}/deliveries": {
      "get": {
        "operationId": "listWebhookDeliveries",
        "summary": "List deliveries",
        "description": "Recent delivery attempts to this endpoint: what we sent, what you answered and when we'll retry.\n\nScope: webhooks:write\n\nErrors: NOT_FOUND",
        "tags": [
          "Webhook endpoints"
        ],
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "x-scope": "webhooks:write",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "Endpoint id."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "ok": true,
                  "data": {
                    "object": "list",
                    "data": [
                      {
                        "id": "8c1e5a2f-9b3d-4f7e-a6c0-2d8b4e1f7a93",
                        "eventId": "evt_3Jk8Qw2Zp7Lm4Nx6Rt1Vb9Yc",
                        "eventType": "payout.succeeded",
                        "attempt": 2,
                        "status": "failed",
                        "responseCode": 500,
                        "responseBody": "Internal Server Error",
                        "error": null,
                        "durationMs": 312,
                        "nextRetryAt": "2026-11-03T09:16:06.000Z",
                        "deliveredAt": null,
                        "createdAt": "2026-11-03T09:10:06.000Z"
                      }
                    ]
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/sandbox/faucet": {
      "post": {
        "operationId": "sandboxFaucet",
        "summary": "Add test funds",
        "description": "Adds free test USDC to your sandbox balance, which test payouts are paid from. Not real money.\n\nErrors: NOT_FOUND, VALIDATION_ERROR",
        "tags": [
          "Sandbox"
        ],
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [],
                "properties": {
                  "amount_usdc": {
                    "type": "number",
                    "description": "How much to add, up to 10,000. Default 1,000."
                  }
                }
              },
              "example": {
                "amount_usdc": 1000
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "ok": true,
                  "data": {
                    "object": "balance",
                    "livemode": false,
                    "usdc": "2000.000000"
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/sandbox/payouts/{id}/simulate": {
      "post": {
        "operationId": "sandboxSimulate",
        "summary": "Simulate a payout outcome",
        "description": "Move a test payout to the status you want, now, and fire its webhooks. Use it to test how your code handles each outcome without waiting.\n\nErrors: NOT_FOUND, VALIDATION_ERROR",
        "tags": [
          "Sandbox"
        ],
        "security": [
          {
            "apiKeyAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "A test payout id."
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "outcome"
                ],
                "properties": {
                  "outcome": {
                    "type": "string",
                    "enum": [
                      "processing",
                      "succeeded",
                      "failed",
                      "reversed",
                      "in_review"
                    ],
                    "description": "The status to move to. failed and reversed return the money to your sandbox balance."
                  }
                }
              },
              "example": {
                "outcome": "failed"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "ok": true,
                  "data": {
                    "id": "po_8Fq2mZx7Lk3Rt9Vb",
                    "object": "payout",
                    "livemode": false,
                    "status": "failed",
                    "amount": 150000,
                    "currency": "KES",
                    "source_token": "USDC",
                    "debited_usdc": "11.948310",
                    "fee_usdc": "0.302310",
                    "rate": "128.80",
                    "destination": {
                      "type": "mobile_money",
                      "network": "safaricom",
                      "phone": "+254 700 ••• 001"
                    },
                    "recipient_name": "Jane Wanjiku",
                    "reference": "order_1042",
                    "description": "Rider payout, week 44",
                    "metadata": {
                      "rider_id": "r_5521"
                    },
                    "failure_code": "RECIPIENT_INVALID",
                    "failure_message": "The recipient's account can't receive this payout.",
                    "created": "2026-11-03T09:10:00.000Z",
                    "updated": "2026-11-03T09:10:00.000Z"
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/capabilities": {
      "get": {
        "operationId": "getCapabilities",
        "summary": "Get coverage",
        "description": "Lists the collection (money in) and payout (money out) methods that work right now, per country. Methods appear and disappear as partners go live or pause, so read this rather than hard-coding a list.\n\nCached for 60 seconds (Cache-Control: public, max-age=60).\n\nminAmount and maxAmount are minor units of the method's currency (KES cents for Kenya). null means no limit is published.",
        "tags": [
          "Capabilities"
        ],
        "security": [],
        "parameters": [
          {
            "name": "country",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "examples": [
                "KE"
              ],
              "description": "ISO 3166-1 alpha-2 country code. Omit to get every country."
            }
          },
          {
            "name": "direction",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "collection",
                "payout"
              ],
              "description": "Only collection (money in) or only payout (money out) methods."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "ok": true,
                  "data": {
                    "countries": [
                      {
                        "code": "KE",
                        "name": "Kenya",
                        "dial": "+254",
                        "flag": "🇰🇪",
                        "currency": "KES",
                        "phoneLength": 9,
                        "collection": [],
                        "payout": [
                          {
                            "method": "mobile_money",
                            "currency": "KES",
                            "operators": [
                              {
                                "code": "Safaricom",
                                "label": "M-Pesa"
                              },
                              {
                                "code": "Airtel",
                                "label": "Airtel Money"
                              }
                            ],
                            "minAmount": "1000",
                            "maxAmount": "25000000"
                          },
                          {
                            "method": "paybill",
                            "currency": "KES",
                            "operators": [],
                            "minAmount": "2000",
                            "maxAmount": "25000000"
                          },
                          {
                            "method": "till",
                            "currency": "KES",
                            "operators": [],
                            "minAmount": "2000",
                            "maxAmount": "25000000"
                          }
                        ]
                      }
                    ],
                    "generatedAt": "2026-11-03T07:04:18.972Z"
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/fees/published": {
      "get": {
        "operationId": "getPublishedFees",
        "summary": "Get published prices",
        "description": "Every price we publish: the FX spread, the cash-out percentage and minimum, the wallet-to-wallet fee and top-up fees. A top-up price is null until it is set; nothing is promised before then.\n\nCached for 60 seconds.\n\nRead from the same fee settings that quotes and charges use.",
        "tags": [
          "Fees"
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "ok": true,
                  "data": {
                    "fx": {
                      "spreadPercent": 2.3
                    },
                    "cashout": {
                      "percent": 3,
                      "minimumUsd": 0.1,
                      "providerFeeAddedOnTop": false
                    },
                    "walletToWallet": {
                      "percent": 0,
                      "flatFeeUsd": 0.02
                    },
                    "cardTopup": null,
                    "bankTopup": null,
                    "mobileMoneyTopup": null
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/fees/quote": {
      "get": {
        "operationId": "quoteFee",
        "summary": "Quote a fee",
        "description": "Returns the itemised fee for an amount of one product. Pass the amount in major units (40 means 40 USDC); every amount in the response is in minor units.\n\nplaceholder: true means a line depends on a price that has not been set yet, don't show it to customers as final.\n\nsettingsVersion changes whenever the fee settings change.",
        "tags": [
          "Fees"
        ],
        "security": [],
        "parameters": [
          {
            "name": "product",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "cashout",
                "wallet_to_wallet",
                "card_topup",
                "bank_topup",
                "mobile_money_topup",
                "payment_link_card"
              ],
              "description": "What the money is doing."
            }
          },
          {
            "name": "amount",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "examples": [
                "40"
              ],
              "description": "A positive decimal in major units, e.g. 40 or 12.50."
            }
          },
          {
            "name": "currency",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "examples": [
                "USDC"
              ],
              "description": "Three or four letters. cashout is priced in USDC; top-ups in the currency charged."
            }
          },
          {
            "name": "country",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "examples": [
                "KE"
              ],
              "description": "ISO 3166-1 alpha-2 code of the payer or payee."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "ok": true,
                  "data": {
                    "product": "cashout",
                    "currency": "USDC",
                    "amount": "40000000",
                    "fees": [
                      {
                        "code": "service_fee",
                        "label": "Service fee",
                        "amount": "1200000",
                        "currency": "USDC"
                      }
                    ],
                    "totalFee": "1200000",
                    "total": "41200000",
                    "placeholder": false,
                    "settingsVersion": "2026-11-03T07:03:31.541Z"
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/p/{code}": {
      "get": {
        "operationId": "getPaymentLink",
        "summary": "Get a payment link",
        "description": "What the payer needs to see: who is asking, how much, for what, and which methods are live for this link right now.",
        "tags": [
          "Payment links (payer)"
        ],
        "security": [],
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "examples": [
                "Kq7mZp2xA"
              ],
              "description": "The link code: 6–32 letters, digits, - or _."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "ok": true,
                  "data": {
                    "code": "Kq7mZp2xA",
                    "requesterName": "Wanjiku Farm Supplies",
                    "verified": true,
                    "amount": "250000",
                    "currency": "KES",
                    "description": "Invoice 1042: fertiliser",
                    "status": "active",
                    "expiresAt": "2026-11-10T09:00:00.000Z",
                    "methods": [
                      "mobile_money"
                    ],
                    "cardFeePayer": "customer"
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/p/{code}/pay": {
      "post": {
        "operationId": "payPaymentLink",
        "summary": "Pay a payment link",
        "description": "Starts the payment. For mobile money the payer gets a prompt on their phone; for card the response carries a hosted checkout URL. Card details are never accepted by this API.\n\nRate limit: 10 requests per 5 minutes per IP address.",
        "tags": [
          "Payment links (payer)"
        ],
        "security": [],
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "The link code."
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "method",
                  "country"
                ],
                "properties": {
                  "method": {
                    "type": "string",
                    "enum": [
                      "card",
                      "mobile_money",
                      "bank"
                    ],
                    "description": "How the payer pays. Must be one of the link's live methods."
                  },
                  "country": {
                    "type": "string",
                    "examples": [
                      "KE"
                    ],
                    "description": "ISO 3166-1 alpha-2 code of the payer."
                  },
                  "phone": {
                    "type": "string",
                    "examples": [
                      "+254712345678"
                    ],
                    "description": "International format, required for mobile_money."
                  },
                  "operator": {
                    "type": "string",
                    "description": "Mobile money operator code from /capabilities, e.g. Safaricom."
                  },
                  "firstName": {
                    "type": "string",
                    "description": "Up to 60 characters. Prefills the card checkout."
                  },
                  "lastName": {
                    "type": "string",
                    "description": "Up to 60 characters."
                  },
                  "email": {
                    "type": "string",
                    "description": "Payer's email, for the receipt."
                  },
                  "idempotencyKey": {
                    "type": "string",
                    "description": "8–80 characters. Retrying with the same key returns the same payment instead of charging twice."
                  }
                }
              },
              "example": {
                "method": "mobile_money",
                "country": "KE",
                "phone": "+254712345678",
                "operator": "Safaricom",
                "idempotencyKey": "order-1042-attempt-1"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "ok": true,
                  "data": {
                    "id": "7b1f0c7e-1f3c-4a43-9a8f-3f0d6a2b9c11",
                    "status": "processing",
                    "checkoutUrl": null,
                    "virtualAccount": null,
                    "stepRequired": "Approve the M-Pesa prompt on your phone",
                    "chargeAmount": "250000",
                    "chargeCurrency": "KES",
                    "feeAmount": "0",
                    "expiresAt": "2026-11-03T09:15:00.000Z"
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/p/{code}/payments/{id}": {
      "get": {
        "operationId": "getPaymentAttempt",
        "summary": "Get a payment's status",
        "description": "Poll this after paying until status is final (credited, failed, expired, cancelled or reversed).",
        "tags": [
          "Payment links (payer)"
        ],
        "security": [],
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "The link code."
            }
          },
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "The id returned when paying."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "ok": true,
                  "data": {
                    "id": "7b1f0c7e-1f3c-4a43-9a8f-3f0d6a2b9c11",
                    "status": "credited",
                    "failureReason": null
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/funding/options": {
      "get": {
        "operationId": "getFundingOptions",
        "summary": "Get top-up options",
        "description": "Which top-up methods are switched on right now, their limits, and the card hold periods. Read this before showing a top-up screen and hide the methods that are false.",
        "tags": [
          "Top-ups"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "ok": true,
                  "data": {
                    "card": false,
                    "mobileMoney": true,
                    "mobileMoneyLimitsKes": {
                      "min": 10,
                      "max": 150000
                    },
                    "bank": false,
                    "bankCountries": [],
                    "bankLimitsNgn": null,
                    "fromAbroad": false,
                    "paymentLinks": true,
                    "cardHoldHoursNewUser": 72,
                    "cardHoldHoursNewCard": 24,
                    "cardDailyLimitUsd": 500,
                    "firstCardDailyLimitUsd": 100
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/funding/mobile-money": {
      "post": {
        "operationId": "createMobileMoneyTopup",
        "summary": "Top up with mobile money",
        "description": "Sends a payment prompt to the phone. The customer approves it with their mobile money PIN and the balance is credited in USDC at the quoted rate.\n\nAvailability: Live in Kenya for M-Pesa. The per-top-up limits in force are mobileMoneyLimitsKes from Get top-up options.\n\nRate limit: 10 top-ups per minute per IP address.",
        "tags": [
          "Top-ups"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "amount",
                  "phone"
                ],
                "properties": {
                  "amount": {
                    "type": "string",
                    "examples": [
                      "1500"
                    ],
                    "description": "Major units with up to 2 decimals, e.g. \"1500\"."
                  },
                  "currency": {
                    "type": "string",
                    "description": "Defaults to the currency of the account's country (KES in Kenya)."
                  },
                  "country": {
                    "type": "string",
                    "description": "Defaults to the account's country."
                  },
                  "phone": {
                    "type": "string",
                    "examples": [
                      "+254712345678"
                    ],
                    "description": "The paying phone, international format."
                  },
                  "operator": {
                    "type": "string",
                    "description": "Operator code from /capabilities."
                  },
                  "idempotencyKey": {
                    "type": "string",
                    "description": "8–80 characters. You can send it as the Idempotency-Key header instead."
                  }
                }
              },
              "example": {
                "amount": "1500",
                "phone": "+254712345678",
                "operator": "Safaricom",
                "idempotencyKey": "topup-2026-11-03-0001"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "ok": true,
                  "data": {
                    "id": "3e5b2a90-8c1d-4f6e-b0a2-1c9d7e4f5a33",
                    "method": "mobile_money",
                    "status": "processing",
                    "localAmount": "150000",
                    "localCurrency": "KES",
                    "chargeAmount": "150000",
                    "chargeCurrency": "KES",
                    "feeAmount": "0",
                    "feeCurrency": "KES",
                    "creditAmountUsdc": "11360000",
                    "fxRate": "132.04",
                    "checkoutUrl": null,
                    "stepRequired": "Approve the M-Pesa prompt on your phone",
                    "virtualAccount": null,
                    "failureReason": null,
                    "holdUntil": null,
                    "card": null,
                    "reference": "APK-TP-8F3K2Q",
                    "expiresAt": "2026-11-03T09:15:00.000Z",
                    "createdAt": "2026-11-03T09:10:00.000Z",
                    "creditedAt": null,
                    "progress": {
                      "stages": [
                        {
                          "key": "requested",
                          "title": "Payment requested",
                          "detail": "We sent a prompt to +254 712 ••• 678",
                          "state": "done",
                          "at": "2026-11-03T09:10:00.000Z"
                        },
                        {
                          "key": "payer",
                          "title": "Waiting for approval",
                          "detail": "Approve the prompt with your M-Pesa PIN",
                          "state": "active",
                          "at": null
                        },
                        {
                          "key": "credited",
                          "title": "In your balance",
                          "detail": "",
                          "state": "pending",
                          "at": null
                        }
                      ],
                      "terminal": false,
                      "hint": "This usually takes under a minute."
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/funding/card": {
      "post": {
        "operationId": "createCardTopup",
        "summary": "Top up with a card",
        "description": "Creates a hosted checkout and returns its checkoutUrl. Send the customer there; card details never touch your servers or ours.\n\nAvailability: Switched off today: /funding/options returns card: false. New cards are held before they can be withdrawn.\n\nRate limit: 10 top-ups per minute per IP address.",
        "tags": [
          "Top-ups"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "amount"
                ],
                "properties": {
                  "amount": {
                    "type": "string",
                    "examples": [
                      "50"
                    ],
                    "description": "Major units with up to 2 decimals."
                  },
                  "currency": {
                    "type": "string",
                    "description": "Defaults to the account's currency."
                  },
                  "country": {
                    "type": "string",
                    "description": "Defaults to the account's country."
                  },
                  "firstName": {
                    "type": "string",
                    "description": "Prefills checkout."
                  },
                  "lastName": {
                    "type": "string",
                    "description": "Prefills checkout."
                  },
                  "email": {
                    "type": "string",
                    "description": "Prefills checkout."
                  },
                  "idempotencyKey": {
                    "type": "string",
                    "description": "8–80 characters."
                  }
                }
              },
              "example": {
                "amount": "50",
                "currency": "USD",
                "email": "amina@example.com"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "ok": true,
                  "data": {
                    "id": "c0a4e1d2-5b6f-4c3a-9e8d-7f1a2b3c4d55",
                    "method": "card",
                    "status": "pending",
                    "localAmount": "5000",
                    "localCurrency": "USD",
                    "chargeAmount": "5000",
                    "chargeCurrency": "USD",
                    "feeAmount": "0",
                    "feeCurrency": "USD",
                    "creditAmountUsdc": "50000000",
                    "fxRate": null,
                    "checkoutUrl": "https://checkout.example.com/s/…",
                    "stepRequired": null,
                    "virtualAccount": null,
                    "failureReason": null,
                    "holdUntil": "2026-11-06T09:10:00.000Z",
                    "card": null,
                    "reference": "APK-TP-2M9W4D",
                    "expiresAt": "2026-11-03T09:15:00.000Z",
                    "createdAt": "2026-11-03T09:10:00.000Z",
                    "creditedAt": null,
                    "progress": {
                      "stages": [
                        {
                          "key": "requested",
                          "title": "Payment requested",
                          "detail": "We sent a prompt to +254 712 ••• 678",
                          "state": "done",
                          "at": "2026-11-03T09:10:00.000Z"
                        },
                        {
                          "key": "payer",
                          "title": "Waiting for approval",
                          "detail": "Approve the prompt with your M-Pesa PIN",
                          "state": "active",
                          "at": null
                        },
                        {
                          "key": "credited",
                          "title": "In your balance",
                          "detail": "",
                          "state": "pending",
                          "at": null
                        }
                      ],
                      "terminal": false,
                      "hint": "This usually takes under a minute."
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/funding/bank": {
      "post": {
        "operationId": "createBankTopup",
        "summary": "Top up by bank transfer",
        "description": "Returns a bank account to transfer into, the exact amount and when it expires. Bank top-ups are in Nigerian naira today.\n\nAvailability: Switched off today: /funding/options returns bank: false.\n\nRate limit: 10 top-ups per minute per IP address.",
        "tags": [
          "Top-ups"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "amount"
                ],
                "properties": {
                  "amount": {
                    "type": "string",
                    "examples": [
                      "20000"
                    ],
                    "description": "Major units."
                  },
                  "currency": {
                    "type": "string",
                    "description": "Defaults to NGN."
                  },
                  "country": {
                    "type": "string",
                    "description": "Defaults to NG."
                  },
                  "refundAccount": {
                    "type": "object",
                    "description": "The payer's own account { bankCode, accountNumber (10 digits), accountName }: where money goes back if the top-up can't finish."
                  },
                  "idempotencyKey": {
                    "type": "string",
                    "description": "8–80 characters."
                  }
                }
              },
              "example": {
                "amount": "20000",
                "refundAccount": {
                  "bankCode": "058",
                  "accountNumber": "0123456789"
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "ok": true,
                  "data": {
                    "id": "9d2c7b1a-0e4f-4a5b-8c6d-2e3f4a5b6c77",
                    "method": "bank",
                    "status": "pending",
                    "localAmount": "2000000",
                    "localCurrency": "NGN",
                    "chargeAmount": "2000000",
                    "chargeCurrency": "NGN",
                    "feeAmount": "0",
                    "feeCurrency": "NGN",
                    "creditAmountUsdc": "13020000",
                    "fxRate": "1536.10",
                    "checkoutUrl": null,
                    "stepRequired": null,
                    "virtualAccount": {
                      "bankName": "Example Bank",
                      "accountName": "AutoPayKe Top-up",
                      "accountNumber": "9900112233",
                      "amount": "2000000",
                      "amountText": "₦20,000.00",
                      "currency": "NGN",
                      "expiresAt": "2026-11-03T10:10:00.000Z",
                      "note": "Send the exact amount in one transfer."
                    },
                    "failureReason": null,
                    "holdUntil": null,
                    "card": null,
                    "reference": "APK-TP-7Q1L8N",
                    "expiresAt": "2026-11-03T10:10:00.000Z",
                    "createdAt": "2026-11-03T09:10:00.000Z",
                    "creditedAt": null,
                    "progress": {
                      "stages": [
                        {
                          "key": "requested",
                          "title": "Payment requested",
                          "detail": "We sent a prompt to +254 712 ••• 678",
                          "state": "done",
                          "at": "2026-11-03T09:10:00.000Z"
                        },
                        {
                          "key": "payer",
                          "title": "Waiting for approval",
                          "detail": "Approve the prompt with your M-Pesa PIN",
                          "state": "active",
                          "at": null
                        },
                        {
                          "key": "credited",
                          "title": "In your balance",
                          "detail": "",
                          "state": "pending",
                          "at": null
                        }
                      ],
                      "terminal": false,
                      "hint": "This usually takes under a minute."
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/funding/bank/institutions": {
      "get": {
        "operationId": "listBankInstitutions",
        "summary": "List banks for bank top-ups",
        "description": "The banks a payer can transfer from. Empty while bank top-ups are switched off.",
        "tags": [
          "Top-ups"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "ok": true,
                  "data": {
                    "banks": []
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/funding/{id}": {
      "get": {
        "operationId": "getFunding",
        "summary": "Get a top-up",
        "description": "The top-up now. Poll every 2 seconds while it's on screen, or use the live stream below. Final statuses: credited, failed, expired, cancelled, reversed.",
        "tags": [
          "Top-ups"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "The top-up id (UUID)."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "ok": true,
                  "data": {
                    "id": "3e5b2a90-8c1d-4f6e-b0a2-1c9d7e4f5a33",
                    "method": "mobile_money",
                    "status": "credited",
                    "localAmount": "150000",
                    "localCurrency": "KES",
                    "chargeAmount": "150000",
                    "chargeCurrency": "KES",
                    "feeAmount": "0",
                    "feeCurrency": "KES",
                    "creditAmountUsdc": "11360000",
                    "fxRate": "132.04",
                    "checkoutUrl": null,
                    "stepRequired": null,
                    "virtualAccount": null,
                    "failureReason": null,
                    "holdUntil": null,
                    "card": null,
                    "reference": "APK-TP-8F3K2Q",
                    "expiresAt": "2026-11-03T09:15:00.000Z",
                    "createdAt": "2026-11-03T09:10:00.000Z",
                    "creditedAt": "2026-11-03T09:10:41.000Z",
                    "progress": {
                      "stages": [
                        {
                          "key": "requested",
                          "title": "Payment requested",
                          "detail": "We sent a prompt to +254 712 ••• 678",
                          "state": "done",
                          "at": "2026-11-03T09:10:00.000Z"
                        },
                        {
                          "key": "payer",
                          "title": "Approved",
                          "detail": "",
                          "state": "done",
                          "at": "2026-11-03T09:10:31.000Z"
                        },
                        {
                          "key": "credited",
                          "title": "In your balance",
                          "detail": "",
                          "state": "done",
                          "at": "2026-11-03T09:10:41.000Z"
                        }
                      ],
                      "terminal": true,
                      "hint": null
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/funding/{id}/live": {
      "get": {
        "operationId": "streamFunding",
        "summary": "Stream a top-up",
        "description": "Server-sent events: one event with the top-up now, then one on every change, until it is credited or fails. Each event's data is the same object Get a top-up returns.",
        "tags": [
          "Top-ups"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "The top-up id (UUID)."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Server-sent events stream",
            "content": {
              "text/event-stream": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/wallet/destinations": {
      "get": {
        "operationId": "listDestinations",
        "summary": "List payout accounts",
        "description": "Every saved account, the default first. Only the last four digits of a number are ever returned.",
        "tags": [
          "Payout accounts"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "ok": true,
                  "data": [
                    {
                      "id": "f1e2d3c4-b5a6-4789-8abc-def012345678",
                      "type": "mobile_money",
                      "country": "KE",
                      "currency": "KES",
                      "operator": "Safaricom",
                      "bankCode": null,
                      "bankName": null,
                      "accountNumberLast4": "5678",
                      "accountName": "AMINA WANJIKU",
                      "nameMatchStatus": "matched",
                      "isDefault": true,
                      "usableForWithdrawals": true,
                      "nickname": "My M-Pesa",
                      "holderName": "Amina Wanjiku",
                      "owner": "self",
                      "lastUsedAt": "2026-11-01T16:20:00.000Z",
                      "useCount": 12
                    }
                  ]
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "post": {
        "operationId": "createDestination",
        "summary": "Save a payout account",
        "description": "Saves the account. Same body as checking the name, plus makeDefault.\n\nAvailability: Bank payouts (PesaLink) are built but not live yet: bank accounts can be saved, and usableForWithdrawals tells you whether one can be paid today.\n\nRate limit: 20 requests per 5 minutes per IP address.\n\nmakeDefault is only honoured when the name on the account matches the account holder; defaultRefused tells you it wasn't.",
        "tags": [
          "Payout accounts"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "type",
                  "country",
                  "currency",
                  "accountNumber"
                ],
                "properties": {
                  "type": {
                    "type": "string",
                    "enum": [
                      "mobile_money",
                      "bank"
                    ],
                    "description": "Kind of account."
                  },
                  "country": {
                    "type": "string",
                    "description": "ISO 3166-1 alpha-2."
                  },
                  "currency": {
                    "type": "string",
                    "description": "ISO 4217."
                  },
                  "accountNumber": {
                    "type": "string",
                    "description": "5–34 characters."
                  },
                  "operator": {
                    "type": "string",
                    "description": "Operator code, for mobile money."
                  },
                  "bankCode": {
                    "type": "string",
                    "description": "Bank code, for bank accounts."
                  },
                  "bankName": {
                    "type": "string",
                    "description": "Bank name, for bank accounts."
                  },
                  "makeDefault": {
                    "type": "boolean",
                    "description": "Make this the default payout account."
                  }
                }
              },
              "example": {
                "type": "mobile_money",
                "country": "KE",
                "currency": "KES",
                "accountNumber": "0712345678",
                "operator": "Safaricom",
                "makeDefault": true
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "ok": true,
                  "data": {
                    "destination": {
                      "id": "f1e2d3c4-b5a6-4789-8abc-def012345678",
                      "type": "mobile_money",
                      "country": "KE",
                      "currency": "KES",
                      "operator": "Safaricom",
                      "bankCode": null,
                      "bankName": null,
                      "accountNumberLast4": "5678",
                      "accountName": "AMINA WANJIKU",
                      "nameMatchStatus": "matched",
                      "isDefault": true,
                      "usableForWithdrawals": true,
                      "nickname": "My M-Pesa",
                      "holderName": "Amina Wanjiku",
                      "owner": "self",
                      "lastUsedAt": "2026-11-01T16:20:00.000Z",
                      "useCount": 12
                    },
                    "defaultRefused": false
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/wallet/destinations/resolve-name": {
      "post": {
        "operationId": "resolveDestinationName",
        "summary": "Check the name on an account",
        "description": "Looks up the registered name for a number or bank account before you save it, so people can confirm they have the right one.\n\nRate limit: 20 requests per 5 minutes per IP address (shared with saving).",
        "tags": [
          "Payout accounts"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "type",
                  "country",
                  "currency",
                  "accountNumber"
                ],
                "properties": {
                  "type": {
                    "type": "string",
                    "enum": [
                      "mobile_money",
                      "bank"
                    ],
                    "description": "Kind of account."
                  },
                  "country": {
                    "type": "string",
                    "examples": [
                      "KE"
                    ],
                    "description": "ISO 3166-1 alpha-2."
                  },
                  "currency": {
                    "type": "string",
                    "examples": [
                      "KES"
                    ],
                    "description": "ISO 4217."
                  },
                  "accountNumber": {
                    "type": "string",
                    "examples": [
                      "0712345678"
                    ],
                    "description": "5–34 characters: the phone number for mobile money."
                  },
                  "operator": {
                    "type": "string",
                    "description": "Operator code, for mobile money."
                  },
                  "bankCode": {
                    "type": "string",
                    "description": "Bank code, for bank accounts."
                  },
                  "bankName": {
                    "type": "string",
                    "description": "Bank name, for bank accounts."
                  }
                }
              },
              "example": {
                "type": "mobile_money",
                "country": "KE",
                "currency": "KES",
                "accountNumber": "0712345678",
                "operator": "Safaricom"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "ok": true,
                  "data": {
                    "accountName": "AMINA WANJIKU",
                    "nameMatchStatus": "matched",
                    "legalName": "Amina Wanjiku"
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/wallet/destinations/{id}": {
      "patch": {
        "operationId": "updateDestination",
        "summary": "Rename a payout account",
        "description": "Change the nickname, or correct the name on the account. Send at least one field.",
        "tags": [
          "Payout accounts"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "The account id."
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [],
                "properties": {
                  "nickname": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "Up to 40 characters; null clears it."
                  },
                  "holderName": {
                    "type": "string",
                    "description": "2–100 characters."
                  }
                }
              },
              "example": {
                "nickname": "Shop M-Pesa"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "ok": true,
                  "data": {
                    "id": "f1e2d3c4-b5a6-4789-8abc-def012345678",
                    "type": "mobile_money",
                    "country": "KE",
                    "currency": "KES",
                    "operator": "Safaricom",
                    "bankCode": null,
                    "bankName": null,
                    "accountNumberLast4": "5678",
                    "accountName": "AMINA WANJIKU",
                    "nameMatchStatus": "matched",
                    "isDefault": true,
                    "usableForWithdrawals": true,
                    "nickname": "Shop M-Pesa",
                    "holderName": "Amina Wanjiku",
                    "owner": "self",
                    "lastUsedAt": "2026-11-01T16:20:00.000Z",
                    "useCount": 12
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "delete": {
        "operationId": "deleteDestination",
        "summary": "Delete a payout account",
        "description": "Removes a saved account. Payments already made to it are not affected.",
        "tags": [
          "Payout accounts"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "The account id."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "ok": true,
                  "data": {
                    "deleted": true
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/wallet/destinations/{id}/default": {
      "post": {
        "operationId": "setDefaultDestination",
        "summary": "Make an account the default",
        "description": "The default account is preselected for withdrawals.",
        "tags": [
          "Payout accounts"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "The account id."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "ok": true,
                  "data": {
                    "id": "f1e2d3c4-b5a6-4789-8abc-def012345678",
                    "type": "mobile_money",
                    "country": "KE",
                    "currency": "KES",
                    "operator": "Safaricom",
                    "bankCode": null,
                    "bankName": null,
                    "accountNumberLast4": "5678",
                    "accountName": "AMINA WANJIKU",
                    "nameMatchStatus": "matched",
                    "isDefault": true,
                    "usableForWithdrawals": true,
                    "nickname": "My M-Pesa",
                    "holderName": "Amina Wanjiku",
                    "owner": "self",
                    "lastUsedAt": "2026-11-01T16:20:00.000Z",
                    "useCount": 12
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/wallet/cards": {
      "get": {
        "operationId": "listCards",
        "summary": "List saved cards",
        "description": "Cards saved from earlier card top-ups.",
        "tags": [
          "Saved cards"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "ok": true,
                  "data": [
                    {
                      "id": "0c1d2e3f-4a5b-4c6d-8e7f-90a1b2c3d4e5",
                      "brand": "visa",
                      "last4": "4242",
                      "holderName": "Amina Wanjiku",
                      "expiryMonth": 8,
                      "expiryYear": 2028,
                      "status": "active",
                      "addedAt": "2026-10-12T08:30:00.000Z",
                      "topUps": 3
                    }
                  ]
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/wallet/cards/{id}": {
      "get": {
        "operationId": "getCard",
        "summary": "Get a saved card",
        "description": "One card, with the daily card top-up limit that applies to it.",
        "tags": [
          "Saved cards"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "The card id."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "ok": true,
                  "data": {
                    "id": "0c1d2e3f-4a5b-4c6d-8e7f-90a1b2c3d4e5",
                    "brand": "visa",
                    "last4": "4242",
                    "holderName": "Amina Wanjiku",
                    "expiryMonth": 8,
                    "expiryYear": 2028,
                    "status": "active",
                    "addedAt": "2026-10-12T08:30:00.000Z",
                    "topUps": 3,
                    "dailyLimitUsd": 500
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "delete": {
        "operationId": "deleteCard",
        "summary": "Remove a saved card",
        "description": "Forgets the card reference.\n\nPOST /api/v1/wallet/cards always answers 405 NOT_SUPPORTED: cards are saved only after a hosted-checkout payment.",
        "tags": [
          "Saved cards"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "The card id."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "ok": true,
                  "data": {
                    "removed": true
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/payment-requests": {
      "post": {
        "operationId": "createPaymentRequest",
        "summary": "Create a payment request",
        "description": "Creates a shareable link at /p/{code}. When it's paid the money is credited to this account's balance.",
        "tags": [
          "Payment requests"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "amount",
                  "allowedMethods"
                ],
                "properties": {
                  "amount": {
                    "type": "string",
                    "examples": [
                      "2500"
                    ],
                    "description": "Major units with up to 2 decimals."
                  },
                  "currency": {
                    "type": "string",
                    "description": "Defaults to the account's currency."
                  },
                  "description": {
                    "type": "string",
                    "description": "Up to 140 characters, shown to the payer."
                  },
                  "allowedMethods": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "card",
                        "mobile_money",
                        "bank"
                      ]
                    },
                    "description": "At least one. Only methods live at pay time are offered."
                  },
                  "cardFeePayer": {
                    "type": "string",
                    "enum": [
                      "customer",
                      "owner"
                    ],
                    "description": "Who pays the card fee. Default customer."
                  },
                  "expiresInDays": {
                    "type": "integer",
                    "description": "1–30, default 7."
                  }
                }
              },
              "example": {
                "amount": "2500",
                "description": "Invoice 1042: fertiliser",
                "allowedMethods": [
                  "mobile_money"
                ],
                "expiresInDays": 7
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "ok": true,
                  "data": {
                    "id": "5a6b7c8d-9e0f-4a1b-8c2d-3e4f5a6b7c8d",
                    "code": "Kq7mZp2xA",
                    "amount": "250000",
                    "currency": "KES",
                    "description": "Invoice 1042: fertiliser",
                    "allowedMethods": [
                      "mobile_money"
                    ],
                    "cardFeePayer": "customer",
                    "status": "active",
                    "expiresAt": "2026-11-10T09:00:00.000Z",
                    "paidAt": null,
                    "createdAt": "2026-11-03T09:00:00.000Z",
                    "url": "https://www.autopayke.com/p/Kq7mZp2xA"
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "get": {
        "operationId": "listPaymentRequests",
        "summary": "List payment requests",
        "description": "This account's requests, newest first. status is active, paid, expired or cancelled.",
        "tags": [
          "Payment requests"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "ok": true,
                  "data": [
                    {
                      "id": "5a6b7c8d-9e0f-4a1b-8c2d-3e4f5a6b7c8d",
                      "code": "Kq7mZp2xA",
                      "amount": "250000",
                      "currency": "KES",
                      "description": "Invoice 1042: fertiliser",
                      "allowedMethods": [
                        "mobile_money"
                      ],
                      "cardFeePayer": "customer",
                      "status": "paid",
                      "expiresAt": "2026-11-10T09:00:00.000Z",
                      "paidAt": "2026-11-03T11:42:00.000Z",
                      "createdAt": "2026-11-03T09:00:00.000Z",
                      "url": "https://www.autopayke.com/p/Kq7mZp2xA"
                    }
                  ]
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/payment-requests/{id}/cancel": {
      "post": {
        "operationId": "cancelPaymentRequest",
        "summary": "Cancel a payment request",
        "description": "The link stops accepting payments straight away.",
        "tags": [
          "Payment requests"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "The request id."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "example": {
                  "ok": true,
                  "data": {
                    "id": "5a6b7c8d-9e0f-4a1b-8c2d-3e4f5a6b7c8d",
                    "code": "Kq7mZp2xA",
                    "amount": "250000",
                    "currency": "KES",
                    "description": "Invoice 1042: fertiliser",
                    "allowedMethods": [
                      "mobile_money"
                    ],
                    "cardFeePayer": "customer",
                    "status": "cancelled",
                    "expiresAt": "2026-11-10T09:00:00.000Z",
                    "paidAt": null,
                    "createdAt": "2026-11-03T09:00:00.000Z",
                    "url": "https://www.autopayke.com/p/Kq7mZp2xA"
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT"
      },
      "apiKeyAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "apk_test_… / apk_live_… API key",
        "description": "A partner API key from the Console (console.autopayke.com). apk_test_ keys use the sandbox; apk_live_ keys move real money and work once AutoPayKe approves them."
      }
    },
    "responses": {
      "Error": {
        "description": "Error",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "required": [
                "ok",
                "error",
                "code"
              ],
              "properties": {
                "ok": {
                  "const": false
                },
                "error": {
                  "type": "string",
                  "description": "A sentence you can show to a person."
                },
                "code": {
                  "type": "string",
                  "description": "Stable machine-readable code, e.g. VALIDATION_ERROR. Branch on this, not on error."
                },
                "requestId": {
                  "type": "string",
                  "description": "Also in the X-Request-ID header. Quote it to support."
                },
                "details": {
                  "type": "object",
                  "description": "Extra facts for some errors, e.g. validation issues or the balance a payout needed."
                }
              }
            },
            "example": {
              "ok": false,
              "error": "amount must be a positive decimal in major units",
              "code": "VALIDATION_ERROR"
            }
          }
        }
      }
    }
  }
}
