{
  "openapi": "3.1.0",
  "info": {
    "title": "PamoPay for Business",
    "version": "1",
    "description": "Take payments from anyone in Tanzania with mobile money. One REST API with a secret key; signed webhooks back to your server. Every failure is a code, documented at https://docs.pamopay.co/errors/<CODE>.",
    "contact": {
      "name": "PamoPay",
      "url": "https://docs.pamopay.co"
    }
  },
  "servers": [
    {
      "url": "https://europe-west1-pamopay-fintech.cloudfunctions.net/merchantApi",
      "description": "Live — sk_live_ keys only"
    },
    {
      "url": "https://europe-west1-pamopay-fintech.cloudfunctions.net/merchantSandboxApi",
      "description": "Sandbox — sk_test_ keys only; moves no money"
    }
  ],
  "security": [
    {
      "apiKey": []
    }
  ],
  "x-common-faults": [
    "BAD_KEY",
    "KEY_REVOKED",
    "KEY_EXPIRED",
    "FORBIDDEN_SCOPE",
    "MERCHANT_NOT_LIVE",
    "RATE_LIMITED",
    "USE_SANDBOX_URL",
    "LIVE_KEY_ON_SANDBOX",
    "BAD_REQUEST",
    "NOT_FOUND",
    "METHOD_NOT_ALLOWED",
    "LEDGER_UNAVAILABLE"
  ],
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "`Authorization: Bearer sk_live_…` or `sk_test_…`. Server-side only; never in a browser, never in a query string."
      }
    },
    "parameters": {
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": true,
        "description": "8–128 characters of A–Z a–z 0–9 - _. Use your own order or sale number. A retry with the same key returns the same object; the same key with a different request is IDEMPOTENT_MISMATCH.",
        "schema": {
          "type": "string",
          "pattern": "^[A-Za-z0-9_-]{8,128}$"
        }
      },
      "IdempotencyKeyOptional": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "description": "Optional here. Honoured when sent: a retry returns the same link.",
        "schema": {
          "type": "string",
          "pattern": "^[A-Za-z0-9_-]{8,128}$"
        }
      },
      "Limit": {
        "name": "limit",
        "in": "query",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100,
          "default": 25
        }
      },
      "StartingAfter": {
        "name": "starting_after",
        "in": "query",
        "description": "The last id of the previous page.",
        "schema": {
          "type": "string"
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "doc",
              "request_id"
            ],
            "properties": {
              "code": {
                "type": "string",
                "description": "One of the documented codes."
              },
              "doc": {
                "type": "string",
                "format": "uri"
              },
              "request_id": {
                "type": "string",
                "description": "Quote this when you write to us."
              },
              "scope": {
                "type": "string",
                "description": "FORBIDDEN_SCOPE only: the scope the key lacked."
              }
            }
          }
        }
      },
      "Charge": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "ch_01HZX4Q7V9M2K3N5P6R8S9T0VW"
          },
          "object": {
            "type": "string",
            "const": "charge"
          },
          "state": {
            "type": "string",
            "enum": [
              "requires_payment",
              "processing",
              "succeeded",
              "failed",
              "expired"
            ]
          },
          "amount_minor": {
            "type": "integer",
            "description": "What the payer authorises. 1 TZS = 100."
          },
          "fee_minor": {
            "type": "integer",
            "description": "PamoPay's. On POS charges this field is spelled mdr_minor."
          },
          "net_minor": {
            "type": "integer",
            "description": "What reaches your balance."
          },
          "currency": {
            "type": "string",
            "const": "TZS"
          },
          "rail": {
            "type": "string",
            "enum": [
              "mpesa",
              "airtel",
              "tigo",
              "halopesa",
              "azampesa",
              "wallet"
            ]
          },
          "reference": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "metadata": {
            "type": "object"
          },
          "created": {
            "type": "string",
            "format": "date-time"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "PosCharge": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Charge"
          },
          {
            "type": "object",
            "properties": {
              "till": {
                "type": "string",
                "example": "400291"
              },
              "token": {
                "type": "string"
              },
              "qr": {
                "type": "string",
                "format": "uri",
                "description": "Render this as the QR code, verbatim."
              },
              "mdr_minor": {
                "type": "integer"
              }
            }
          }
        ]
      },
      "PaymentLink": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "pl_01HZX…"
          },
          "object": {
            "type": "string",
            "const": "payment_link"
          },
          "kind": {
            "type": "string",
            "enum": [
              "reusable",
              "single_use"
            ]
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "amount_minor": {
            "type": "integer"
          },
          "currency": {
            "type": "string",
            "const": "TZS"
          },
          "title": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "reference": {
            "type": "string"
          },
          "max_uses": {
            "type": [
              "integer",
              "null"
            ]
          },
          "used": {
            "type": "integer"
          },
          "active": {
            "type": "boolean"
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "success_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "created": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Bill": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "bl_01HZX…"
          },
          "object": {
            "type": "string",
            "const": "bill"
          },
          "state": {
            "type": "string",
            "enum": [
              "open",
              "part_paid",
              "paid",
              "expired",
              "cancelled"
            ]
          },
          "control_number": {
            "type": "string",
            "example": "104827391625",
            "description": "Store this."
          },
          "control_number_printed": {
            "type": "string",
            "example": "1048 2739 1625",
            "description": "Print this."
          },
          "customer_ref": {
            "type": "string"
          },
          "payer_name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "amount_minor": {
            "type": "integer"
          },
          "paid_minor": {
            "type": "integer"
          },
          "outstanding_minor": {
            "type": "integer"
          },
          "currency": {
            "type": "string",
            "const": "TZS"
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "cancelled_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Balance": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "const": "balance"
          },
          "currency": {
            "type": "string",
            "const": "TZS"
          },
          "available_minor": {
            "type": "integer"
          },
          "settling_minor": {
            "type": "integer",
            "description": "Always 0 until settlement is automated."
          },
          "settlement": {
            "type": "object",
            "properties": {
              "enabled": {
                "type": "boolean"
              },
              "note": {
                "type": "string"
              }
            }
          }
        }
      },
      "Event": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "ev_9f3b2c1d4e5f6a7b8c9d0e1f"
          },
          "object": {
            "type": "string",
            "const": "event"
          },
          "type": {
            "type": "string",
            "enum": [
              "charge.succeeded",
              "charge.expired",
              "escrow.funded",
              "escrow.released"
            ]
          },
          "created": {
            "type": "integer",
            "description": "Unix seconds."
          },
          "livemode": {
            "type": "boolean"
          },
          "data": {
            "type": "object"
          },
          "delivery": {
            "type": "object",
            "properties": {
              "state": {
                "type": "string",
                "enum": [
                  "pending",
                  "retrying",
                  "delivered",
                  "exhausted",
                  "unrouted"
                ]
              },
              "attempts": {
                "type": "integer"
              },
              "attempts_remaining": {
                "type": "integer"
              },
              "last_status": {
                "type": [
                  "integer",
                  "null"
                ],
                "description": "0 means we could not reach you at all."
              },
              "last_response": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "last_attempt_at": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "next_attempt_at": {
                "type": [
                  "integer",
                  "null"
                ]
              }
            }
          }
        }
      },
      "List": {
        "type": "object",
        "properties": {
          "object": {
            "type": "string",
            "const": "list"
          },
          "has_more": {
            "type": "boolean"
          },
          "data": {
            "type": "array",
            "items": {}
          }
        }
      },
      "WebhookEndpoint": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "object": {
            "type": "string",
            "const": "webhook_endpoint"
          },
          "mode": {
            "type": "string",
            "enum": [
              "live",
              "test"
            ]
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "active": {
            "type": "boolean"
          },
          "created": {
            "type": "string",
            "format": "date-time"
          },
          "updated": {
            "type": "string",
            "format": "date-time"
          },
          "secret": {
            "type": "string",
            "description": "On create only. whsec_… — derived, so the dashboard can show it again."
          }
        }
      }
    },
    "responses": {
      "Error": {
        "description": "A documented code.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    }
  },
  "paths": {
    "/v1/charges": {
      "post": {
        "tags": [
          "Charges"
        ],
        "summary": "Ask a phone to pay",
        "operationId": "createCharge",
        "x-scope": "charges:write",
        "x-sandbox": true,
        "x-faults": [
          "IDEMPOTENCY_REQUIRED",
          "IDEMPOTENT_MISMATCH",
          "BAD_AMOUNT",
          "BAD_CURRENCY",
          "BAD_RAIL",
          "BAD_MSISDN",
          "AMOUNT_TOO_SMALL"
        ],
        "description": "The payer receives a network prompt on their handset and approves it with their mobile-money PIN. They have 15 minutes. Wait for `succeeded` by webhook or poll; `processing` means not yet known and is never a failure.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "amount_minor",
                  "rail",
                  "msisdn"
                ],
                "properties": {
                  "amount_minor": {
                    "type": "integer",
                    "description": "Gross, whole shillings: a multiple of 100 between 10000 and 2000000000."
                  },
                  "rail": {
                    "type": "string",
                    "enum": [
                      "mpesa",
                      "airtel",
                      "tigo",
                      "halopesa",
                      "azampesa"
                    ]
                  },
                  "msisdn": {
                    "type": "string",
                    "description": "0759280775, 759280775 or +255759280775."
                  },
                  "currency": {
                    "type": "string",
                    "const": "TZS"
                  },
                  "reference": {
                    "type": "string",
                    "maxLength": 200
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 500
                  },
                  "metadata": {
                    "type": "object"
                  }
                }
              },
              "example": {
                "amount_minor": 4650000,
                "rail": "mpesa",
                "msisdn": "0759280775",
                "reference": "ORD-00417",
                "description": "2 × kanga, delivery Kariakoo",
                "metadata": {
                  "cart_id": "c_88a1"
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The charge, usually `processing`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Charge"
                }
              }
            }
          },
          "4XX": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "get": {
        "tags": [
          "Charges"
        ],
        "summary": "List charges",
        "operationId": "listCharges",
        "x-scope": "charges:read",
        "x-sandbox": true,
        "x-faults": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/StartingAfter"
          },
          {
            "name": "state",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "requires_payment",
                "processing",
                "succeeded",
                "failed",
                "expired"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/List"
                }
              }
            }
          }
        }
      }
    },
    "/v1/charges/{id}": {
      "get": {
        "tags": [
          "Charges"
        ],
        "summary": "One charge",
        "operationId": "getCharge",
        "x-scope": "charges:read",
        "x-sandbox": true,
        "x-faults": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The charge.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Charge"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/balance": {
      "get": {
        "tags": [
          "Balance"
        ],
        "summary": "Your balance",
        "operationId": "getBalance",
        "x-scope": "balance:read",
        "x-sandbox": true,
        "x-faults": [],
        "description": "Read from the ledger, not a cache. The sandbox always answers zero.",
        "responses": {
          "200": {
            "description": "The balance.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Balance"
                }
              }
            }
          }
        }
      }
    },
    "/v1/pos/charges": {
      "post": {
        "tags": [
          "Charges"
        ],
        "summary": "A code on the counter",
        "operationId": "createPosCharge",
        "x-scope": "charges:write",
        "x-sandbox": false,
        "x-faults": [
          "IDEMPOTENCY_REQUIRED",
          "IDEMPOTENT_MISMATCH",
          "BAD_TILL",
          "UNKNOWN_TILL",
          "TILL_CLOSED",
          "BAD_AMOUNT",
          "AMOUNT_TOO_SMALL"
        ],
        "description": "For a till system. Nothing is sent to anybody until the customer scans. The code lives five minutes.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "till",
                  "amount_minor"
                ],
                "properties": {
                  "till": {
                    "type": "string",
                    "pattern": "^[0-9]{6}$"
                  },
                  "amount_minor": {
                    "type": "integer"
                  },
                  "reference": {
                    "type": "string"
                  },
                  "description": {
                    "type": "string"
                  },
                  "metadata": {
                    "type": "object"
                  }
                }
              },
              "example": {
                "till": "400291",
                "amount_minor": 4650000,
                "reference": "SALE-3391"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The charge, with its `qr`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PosCharge"
                }
              }
            }
          },
          "4XX": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/bills": {
      "post": {
        "tags": [
          "Bills"
        ],
        "summary": "Issue a bill",
        "operationId": "createBill",
        "x-scope": "bills:write",
        "x-sandbox": false,
        "x-faults": [
          "BAD_CUSTOMER_REF",
          "BAD_AMOUNT",
          "BAD_EXPIRY"
        ],
        "description": "A receivable with a twelve-digit control number a payer types into a mobile-money menu. No Idempotency-Key: a duplicate bill is a second slip you can see and cancel.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "customer_ref",
                  "amount_minor"
                ],
                "properties": {
                  "customer_ref": {
                    "type": "string",
                    "maxLength": 64
                  },
                  "amount_minor": {
                    "type": "integer"
                  },
                  "payer_name": {
                    "type": "string",
                    "maxLength": 120
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 500
                  },
                  "expires_at": {
                    "type": "string",
                    "format": "date-time"
                  }
                }
              },
              "example": {
                "customer_ref": "STU-2026-0412",
                "amount_minor": 25000000,
                "payer_name": "Neema Kileo",
                "description": "Term 3 fees"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The bill, with its control number.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Bill"
                }
              }
            }
          },
          "4XX": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "get": {
        "tags": [
          "Bills"
        ],
        "summary": "List bills",
        "operationId": "listBills",
        "x-scope": "bills:read",
        "x-sandbox": false,
        "x-faults": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/StartingAfter"
          },
          {
            "name": "customer_ref",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "state",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "open",
                "part_paid",
                "paid",
                "expired",
                "cancelled"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/List"
                }
              }
            }
          }
        }
      }
    },
    "/v1/bills/{id}": {
      "get": {
        "tags": [
          "Bills"
        ],
        "summary": "One bill",
        "operationId": "getBill",
        "x-scope": "bills:read",
        "x-sandbox": false,
        "x-faults": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The bill.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Bill"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/bills/{id}/notify": {
      "post": {
        "tags": [
          "Bills"
        ],
        "summary": "Text the payer their control number",
        "operationId": "notifyBill",
        "x-scope": "bills:write",
        "x-sandbox": false,
        "x-faults": [
          "BAD_NUMBER"
        ],
        "description": "One SMS with the outstanding amount, the control number and the bill's link. The number is used once and not stored. A refusal is 200 with `sent: false` and a reason, so a loop over four hundred bills sees which went.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "phone"
                ],
                "properties": {
                  "phone": {
                    "type": "string"
                  }
                }
              },
              "example": {
                "phone": "0759280775"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Whether it went.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": {
                      "const": "bill_notice"
                    },
                    "bill": {
                      "type": "string"
                    },
                    "sent": {
                      "type": "boolean"
                    },
                    "reason": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "to": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "4XX": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/payment_links": {
      "post": {
        "tags": [
          "Payment links"
        ],
        "summary": "A price with a URL on it",
        "operationId": "createPaymentLink",
        "x-scope": "links:write",
        "x-sandbox": false,
        "x-faults": [
          "BAD_AMOUNT",
          "NO_REGISTERED_WEBSITE",
          "BAD_RETURN_URL",
          "IDEMPOTENCY_REQUIRED",
          "IDEMPOTENT_MISMATCH"
        ],
        "description": "No money moves when you create one; every payment against it is an ordinary charge made later by the payer on the hosted page.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKeyOptional"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "amount_minor",
                  "title"
                ],
                "properties": {
                  "amount_minor": {
                    "type": "integer"
                  },
                  "title": {
                    "type": "string",
                    "minLength": 2,
                    "maxLength": 80
                  },
                  "kind": {
                    "type": "string",
                    "enum": [
                      "reusable",
                      "single_use"
                    ],
                    "default": "reusable"
                  },
                  "max_uses": {
                    "type": "integer",
                    "minimum": 1
                  },
                  "expires_at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 500
                  },
                  "reference": {
                    "type": "string",
                    "maxLength": 200
                  },
                  "success_url": {
                    "type": "string",
                    "format": "uri",
                    "description": "https, on the website registered at KYB."
                  }
                }
              },
              "example": {
                "amount_minor": 2500000,
                "title": "Term 3 trip deposit",
                "reference": "TRIP-T3"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The link. Send `url` to the payer.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentLink"
                }
              }
            }
          },
          "4XX": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "get": {
        "tags": [
          "Payment links"
        ],
        "summary": "List payment links",
        "operationId": "listPaymentLinks",
        "x-scope": "links:read",
        "x-sandbox": false,
        "x-faults": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          }
        ],
        "responses": {
          "200": {
            "description": "Newest first. No cursor yet.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/List"
                }
              }
            }
          }
        }
      }
    },
    "/v1/payment_links/{id}": {
      "get": {
        "tags": [
          "Payment links"
        ],
        "summary": "One payment link",
        "operationId": "getPaymentLink",
        "x-scope": "links:read",
        "x-sandbox": false,
        "x-faults": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The link.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentLink"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/events": {
      "get": {
        "tags": [
          "Events"
        ],
        "summary": "Every event, delivered or not",
        "operationId": "listEvents",
        "x-scope": "events:read",
        "x-sandbox": false,
        "x-faults": [],
        "description": "How you catch up after an outage, or before you had an endpoint.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/StartingAfter"
          },
          {
            "name": "type",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/List"
                }
              }
            }
          }
        }
      }
    },
    "/v1/events/{id}": {
      "get": {
        "tags": [
          "Events"
        ],
        "summary": "One event",
        "operationId": "getEvent",
        "x-scope": "events:read",
        "x-sandbox": false,
        "x-faults": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The event and its delivery.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Event"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/events/{id}/retry": {
      "post": {
        "tags": [
          "Events"
        ],
        "summary": "Send an event again",
        "operationId": "retryEvent",
        "x-scope": "events:write",
        "x-sandbox": false,
        "x-faults": [
          "ALREADY_QUEUED"
        ],
        "description": "Allowed when the event is delivered, exhausted or unrouted. The attempt count resets. Nothing is sent by this call; the delivery job picks it up within a minute.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The event, now pending.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Event"
                }
              }
            }
          },
          "409": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/v1/webhook_endpoints": {
      "post": {
        "tags": [
          "Webhook endpoints"
        ],
        "summary": "Set where events go",
        "operationId": "setWebhookEndpoint",
        "x-scope": "webhooks:write",
        "x-sandbox": false,
        "x-faults": [
          "BAD_URL"
        ],
        "description": "One address per mode, and the mode is the key's. Setting a new address rotates the signing secret; saving the same address keeps it. The secret is returned here and can be read again on the dashboard.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "url"
                ],
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri",
                    "description": "https, with a host, at most 500 characters."
                  }
                }
              },
              "example": {
                "url": "https://shop.example.co.tz/pamopay/events"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The endpoint, with its secret.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpoint"
                }
              }
            }
          },
          "4XX": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "get": {
        "tags": [
          "Webhook endpoints"
        ],
        "summary": "Where events go",
        "operationId": "listWebhookEndpoints",
        "x-scope": "webhooks:read",
        "x-sandbox": false,
        "x-faults": [],
        "description": "The endpoint for the key's mode, active or not. Never the secret.",
        "responses": {
          "200": {
            "description": "At most one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/List"
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhook_endpoints/{id}": {
      "delete": {
        "tags": [
          "Webhook endpoints"
        ],
        "summary": "Stop sending events",
        "operationId": "removeWebhookEndpoint",
        "x-scope": "webhooks:write",
        "x-sandbox": false,
        "x-faults": [],
        "description": "Deactivates the endpoint. Events that happen afterwards are recorded as unrouted and readable at GET /v1/events. Never deleted: the delivery history points at it.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Stopped.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpoint"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    }
  }
}
