{
  "openapi": "3.0.4",
  "info": {
    "title": "Link2Pay API",
    "description": "Create a payment link, send it to a payer, and find out when the money arrived.\r\n\r\nEverything the merchant panel does is available here, over the same application\r\nlayer — the panel and this API are two adapters over one set of services, so an\r\nendpoint cannot fall behind the screen that does the same thing.\r\n\r\n---\r\n\r\n## 1. Getting started in four calls\r\n\r\n```bash\r\nBASE=https://app.example.ir\r\n\r\n# 1. Exchange the client credentials for an access token.\r\nTOKEN=$(curl -s -X POST \"$BASE/api/v1/oauth/token\" \\\r\n  -H 'Content-Type: application/json' \\\r\n  -d '{\"clientId\":\"l2p_live_…\",\"clientSecret\":\"…\",\"grantType\":\"client_credentials\"}' \\\r\n  | jq -r .accessToken)\r\n\r\n# 2. Find the terminal the money should settle into.\r\ncurl -s \"$BASE/api/v1/terminals\" -H \"Authorization: Bearer $TOKEN\"\r\n\r\n# 3. Create a link for 250,000 Toman and text it to the payer.\r\ncurl -s -X POST \"$BASE/api/v1/links\" \\\r\n  -H \"Authorization: Bearer $TOKEN\" -H 'Content-Type: application/json' \\\r\n  -d '{\"terminalUuid\":\"…\",\"amount\":250000,\"currency\":2,\r\n       \"payerName\":\"…\",\"payerMobile\":\"09121234567\",\r\n       \"description\":\"Invoice 1042\",\"sendSms\":true}'\r\n\r\n# 4. Poll until it is paid.\r\ncurl -s \"$BASE/api/v1/links/{uuid}/check\" -H \"Authorization: Bearer $TOKEN\"\r\n```\r\n\r\n`POST /api/v1/links` answers with the link's `uuid`, its `shortId` and the\r\naddress to give the payer. `GET /api/v1/links/{uuid}/check` is the cheap\r\nendpoint to poll: it reports `isPaid` and nothing else expensive.\r\n\r\n---\r\n\r\n## 2. Base address and versioning\r\n\r\nEvery path begins `/api/v1/`. The version is in the path and will stay there:\r\na second version would be served alongside this one, not in place of it.\r\n\r\nWithin `v1`, changes are additive. A new field may appear in a response and a\r\nnew optional field may be accepted in a request, so **ignore fields you do not\r\nrecognise** rather than failing on them. Fields are never removed or retyped\r\nwithout a new version.\r\n\r\nRequests and responses are `application/json` with UTF-8, except where an\r\nendpoint is documented as answering `application/pdf` or a spreadsheet.\r\nJSON property names are `camelCase`.\r\n\r\n---\r\n\r\n## 3. Authenticating\r\n\r\nThere are three doors, and they are not interchangeable — pick by what the\r\ncaller *is*.\r\n\r\n### 3.1 An integration: client credentials (recommended)\r\n\r\nA merchant creates an API client in their panel and receives a `clientId` and a\r\nsecret shown exactly once. Exchange them for a short-lived access token:\r\n\r\n```http\r\nPOST /api/v1/oauth/token\r\nContent-Type: application/json\r\n\r\n{ \"clientId\": \"…\", \"clientSecret\": \"…\", \"grantType\": \"client_credentials\" }\r\n```\r\n\r\n```json\r\n{ \"accessToken\": \"eyJ…\", \"tokenType\": \"Bearer\",\r\n  \"expiresIn\": 3600, \"scope\": \"links:read links:write\" }\r\n```\r\n\r\nSend it as `Authorization: Bearer <accessToken>` and exchange again when\r\n`expiresIn` runs out — read that field rather than assuming an hour, because the\r\nlifetime is set per client. This is the preferred door because the secret then\r\ncrosses the network once a lifetime instead of on every call.\r\n\r\n`GET /api/v1/oauth/scopes` lists every scope the platform understands, so a\r\nconsent screen need not hard-code them.\r\n\r\n### 3.2 An integration that cannot manage the exchange: an API key\r\n\r\nSend the secret directly:\r\n\r\n```http\r\nX-Api-Key: <the client secret>\r\n```\r\n\r\nSame client, same scopes, same limits. It exists for callers that cannot hold a\r\ntoken; prefer 3.1 wherever you can.\r\n\r\n### 3.3 A person: the password grant\r\n\r\n`POST /api/v1/auth/token` takes a mobile number and password and answers an\r\naccess token and a refresh token. This is what a first-party mobile or desktop\r\nclient uses. It is **not** the way to build an integration on a merchant's\r\nbehalf — an integration authenticating as its owner holds everything the owner\r\nholds, which is the arrangement scopes exist to end.\r\n\r\n* `POST /api/v1/auth/refresh` rotates the pair. A refresh token is single-use:\r\n  presenting one twice is treated as theft and revokes the whole family, so\r\n  store the newest pair and never retry a refresh with the old token.\r\n* `POST /api/v1/auth/revoke` ends the session.\r\n* `GET /api/v1/auth/me` says who the caller is.\r\n\r\n### 3.4 What each door cannot do\r\n\r\nSome endpoints refuse an integration outright and require the person: creating\r\nan API client, rotating its secret, widening its scopes, registering a terminal,\r\nopening a support ticket. A client holding `links:write` must not be able to\r\nmint itself a second client holding more. These answer **403** with the code\r\n`client.person_required`, and the operation says so below.\r\n\r\nAnything an administrator does — setting a terminal's wage, depositing to a\r\nwallet — additionally requires the administrator role. A merchant's client\r\ncannot obtain it: `admin:*` may only be granted to a platform client.\r\n\r\n---\r\n\r\n## 4. Scopes\r\n\r\nA scope narrows an integration to less than its owner can do. It never widens\r\nanyone: a person signing in with their own credentials satisfies every scope,\r\nbecause their authority is their role.\r\n\r\n| Scope | What it allows |\r\n|---|---|\r\n| `links:read` | Read links, poll their status, download invoices |\r\n| `links:write` | Create links (single, bulk, scheduled) and delete unpaid ones |\r\n| `transactions:read` | Read transactions, reports and receipts |\r\n| `terminals:read` | Read terminals and their configuration |\r\n| `wallet:read` | Read the SMS wallet balance, ledger and usage |\r\n| `sms:send` | Send messages, which spends the wallet |\r\n| `contacts:read` | Read contacts and groups, export them |\r\n| `contacts:write` | Create, change, delete and import contacts and groups |\r\n| `merchants:read` | Read the registration dossier the acquirer requires |\r\n| `merchants:write` | Fill in the registration dossier |\r\n| `products:read` | Read the catalogue |\r\n| `products:write` | Change the catalogue and its photographs |\r\n| `admin:*` | Everything an administrator can do. Platform clients only |\r\n\r\nA client's authority is the **intersection** of its scopes and its owner's, so\r\ngranting `admin:*` to a merchant's client is refused at creation\r\n(`api_client.scope_not_permitted`) rather than silently ignored at call time.\r\n\r\nEvery operation below states the scope it needs.\r\n\r\n---\r\n\r\n## 5. When a call fails\r\n\r\nFailures answer [RFC 9457 `application/problem+json`](https://www.rfc-editor.org/rfc/rfc9457):\r\n\r\n```json\r\n{\r\n  \"status\": 400,\r\n  \"title\": \"مبلغ از کمینه مجاز پایانه کمتر است.\",\r\n  \"instance\": \"/api/v1/links\",\r\n  \"code\": \"link.amount_below_minimum\",\r\n  \"correlationId\": \"0HN7…\"\r\n}\r\n```\r\n\r\n**Branch on `code`, never on `title`.** `code` is a stable identifier that will\r\nnot change; `title` is Persian prose meant to be shown to a merchant, and it is\r\nfree to improve between releases. `correlationId` is the value returned in the\r\nresponse header and the one to quote in a support ticket — it is what the logs\r\nand the audit trail are indexed by.\r\n\r\n| Status | Means | Do |\r\n|---|---|---|\r\n| `400` | The request was understood and refused | Read `code`; fix and retry |\r\n| `401` | No credential, or one that is not accepted | Re-authenticate once, then stop |\r\n| `403` | Authenticated, but not permitted — a missing scope, a role, or a person-only endpoint | Do not retry; the grant must change |\r\n| `404` | No such object, **or** one the caller may not see | Treat as absent; the two are deliberately indistinguishable |\r\n| `409` | The object is in a state that forbids this — deleting a paid link | Do not retry |\r\n| `429` | Rate limited | Wait `Retry-After` seconds |\r\n| `5xx` | The platform failed | Retry with backoff; the call may still have taken effect |\r\n\r\nSome codes an integration should recognise by name:\r\n\r\n| Code | Where |\r\n|---|---|\r\n| `client.credentials_invalid` | Wrong client id or secret |\r\n| `client.suspended` | The client exists but has been switched off |\r\n| `client.address_not_allowed` | The call came from an address outside the client's allow list |\r\n| `client.person_required` | A person-only endpoint, called by an integration |\r\n| `api_client.scope_unknown` / `api_client.scope_not_permitted` | Creating or widening a client |\r\n| `grant_type.unsupported` | Only `client_credentials` is issued |\r\n\r\nA `404` for an object that belongs to another merchant is not an accident of\r\nimplementation: answering `403` there would confirm the object exists, which is\r\nhow the system this replaces leaked one merchant's transactions to another.\r\n\r\n---\r\n\r\n## 6. Money\r\n\r\n**Amounts are integers in minor units, and every amount carries its currency.**\r\n\r\n```json\r\n{ \"amount\": 250000, \"currency\": 2 }\r\n```\r\n\r\n| `currency` | Meaning | Minor unit |\r\n|---|---|---|\r\n| `1` | Rial (IRR) — what the bank gateways transact in | 1 Rial |\r\n| `2` | Toman (IRT) — worth ten Rials | 1 Toman |\r\n\r\nRial is what an omitted `currency` means; send it explicitly all the same, so\r\nthat reading the request tells you what it charges. Never send a decimal,\r\nnever send\r\na bare number and a separate \"this is Toman\" flag, and never convert on the way\r\nin: state the currency you mean and the platform converts once, where the\r\nconversion can be audited. The system this replaces carried an `isToman`\r\nboolean beside a floating amount and double-converted, which is how a\r\n250,000-Toman invoice was collected as 25,000.\r\n\r\nSplit settlements sum exactly for the same reason: integers, never floats.\r\n\r\n---\r\n\r\n## 7. Iranian and Persian input\r\n\r\n* **Mobile numbers** are accepted in every spelling — `09121234567`,\r\n  `+989121234567`, `989121234567`, with spaces or dashes, and with Persian or\r\n  Arabic-Indic digits. They are normalised before anything is looked up, so one\r\n  number can never become two accounts. Responses always return the normalised\r\n  `09…` form.\r\n* **Persian digits** (`۰۱۲۳۴۵۶۷۸۹`) are translated to ASCII at the boundary in\r\n  any numeric field, so a value pasted out of a Persian document is accepted.\r\n* **National codes** and **IBANs** are validated by check digit, not by length.\r\n  An IBAN is accepted with or without the `IR` prefix and spaces.\r\n* **Dates and times in this API are Gregorian ISO-8601 with an offset**\r\n  (`2026-03-21T08:30:00+03:30`). Jalali dates belong to the screens a person\r\n  reads; do not send one here.\r\n\r\n---\r\n\r\n## 8. Lists and paging\r\n\r\nEndpoints that list use `skip` and `take` query parameters.\r\n\r\n* `skip` defaults to `0`.\r\n* `take` defaults to `50` and is capped at `200`. Asking for more returns the\r\n  cap rather than an error.\r\n* Order is newest first unless the operation says otherwise.\r\n\r\nA page shorter than `take` means the end has been reached.\r\n\r\n---\r\n\r\n## 9. Rate limits\r\n\r\n| Applies to | Limit |\r\n|---|---|\r\n| Endpoints an integration calls | 120 requests per minute per **client**, as a token bucket, so a burst is allowed |\r\n| `/api/v1/auth/*` and `/api/v1/oauth/token` | 10 requests per minute per **source address** |\r\n| The public abuse-report form | 5 requests per 10 minutes per source address |\r\n\r\nThe integration limit is counted per client rather than per address, so several\r\nmerchants' integrations sharing one hosting provider do not spend each other's\r\nallowance. Exceeding it answers `429` with `Retry-After: 60`.\r\n\r\nAsk for a page of 200 rather than 200 pages of one: the limit is on calls.\r\n\r\n---\r\n\r\n## 10. The lifecycle of a payment\r\n\r\n1. **Create** — `POST /api/v1/links`. The link starts `Pending`. With\r\n   `sendSms: true` the payer is texted, which spends the SMS wallet; check the\r\n   balance first with `GET /api/v1/wallet`, or send the address yourself.\r\n2. **Pay** — the payer opens `/s/{shortId}` and is taken to the acquirer's\r\n   gateway. Nothing here is called during this.\r\n3. **Settle** — the gateway returns to the platform, which verifies the payment\r\n   with the acquirer before marking anything paid. A link is `Succeeded` only\r\n   once the acquirer has confirmed it, never on the strength of a redirect.\r\n4. **Observe** — poll `GET /api/v1/links/{uuid}/check` until `isPaid` is true,\r\n   or read `GET /api/v1/reports/transactions`. Poll no faster than every few\r\n   seconds; the status does not change while nobody is paying.\r\n5. **Document** — `GET /api/v1/links/{uuid}/invoice` and\r\n   `GET /api/v1/transactions/{uuid}/receipt` return PDF bytes, not URLs, so an\r\n   integration can attach one to its own email without exposing a document\r\n   behind authentication.\r\n\r\nAn expired or deleted link cannot be paid. A link with a transaction against it\r\ncannot be deleted — that answers `409`.\r\n\r\nRecurring collection is `POST /api/v1/schedules`: the platform mints the link\r\nand sends it on the cadence given, and the schedule can be paused and resumed.\r\nTo bill a whole group at once, `POST /api/v1/links/bulk/{groupUuid}` creates one\r\nlink per contact and reports which contacts it passed over.\r\n\r\n---\r\n\r\n## 11. Correlation\r\n\r\nEvery response carries a correlation identifier, echoed in the `correlationId`\r\nmember of a problem response. Send your own on a request and it is used instead\r\nof a generated one, which is what makes a failure traceable from your logs into\r\nthe platform's. Include it in any support request.\r\n\r\n---\r\n\r\n## 12. Trying an endpoint from this page\r\n\r\nPress **Authorize**, then either paste an access token under `bearer` or a\r\nclient secret under `apiKey`, and the browser attaches it to every call you make\r\nfrom here. Calls made from this page are real calls against whichever deployment\r\nserved the page — creating a link here creates a link.",
    "version": "v1"
  },
  "paths": {
    "/api/v1/abuse-reports": {
      "post": {
        "tags": [
          "AbuseReports"
        ],
        "summary": "Files a complaint.",
        "description": "**Authentication** — none. Open to anyone.\n\n**Role** — restricted to `admin`.\n\n**Rate limit** — 5 requests per 10 minutes per source address.",
        "operationId": "AbuseReportsSubmit",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/SubmitAbuseReportBody"
                  }
                ],
                "description": "A complaint being filed."
              }
            },
            "text/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/SubmitAbuseReportBody"
                  }
                ],
                "description": "A complaint being filed."
              }
            },
            "application/*+json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/SubmitAbuseReportBody"
                  }
                ],
                "description": "A complaint being filed."
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The complaint's reference.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AbuseReportSummary"
                }
              }
            }
          },
          "400": {
            "description": "A field is missing or the mobile number is not one.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too many complaints from this address."
          }
        }
      },
      "get": {
        "tags": [
          "AbuseReports"
        ],
        "summary": "Lists complaints.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — none in particular; any authenticated caller with the rights below may call it.\n\n**Role** — restricted to `admin`.",
        "operationId": "AbuseReportsList",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "description": "Values:\n\n* `0` — `Received`\n* `1` — `Reviewing`\n* `2` — `Closed`",
            "schema": {
              "allOf": [
                {
                  "$ref": "#/components/schemas/AbuseReportStatus"
                }
              ],
              "description": "Values:\n\n* `0` — `Received`\n* `1` — `Reviewing`\n* `2` — `Closed`"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Complaints, unanswered and oldest first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/AbuseReportSummary"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/abuse-reports/{uuid}": {
      "get": {
        "tags": [
          "AbuseReports"
        ],
        "summary": "Reads one complaint.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — none in particular; any authenticated caller with the rights below may call it.\n\n**Role** — restricted to `admin`.",
        "operationId": "AbuseReportsGet",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The complaint and what has been done about it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AbuseReportDetail"
                }
              }
            }
          },
          "404": {
            "description": "No such complaint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/abuse-reports/{uuid}/status": {
      "post": {
        "tags": [
          "AbuseReports"
        ],
        "summary": "Moves a complaint along.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — none in particular; any authenticated caller with the rights below may call it.\n\n**Role** — restricted to `admin`.",
        "operationId": "AbuseReportsSetStatus",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/SetAbuseReportStatusBody"
                  }
                ],
                "description": "Where a complaint now stands."
              }
            },
            "text/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/SetAbuseReportStatusBody"
                  }
                ],
                "description": "Where a complaint now stands."
              }
            },
            "application/*+json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/SetAbuseReportStatusBody"
                  }
                ],
                "description": "Where a complaint now stands."
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Recorded."
          },
          "404": {
            "description": "No such complaint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/api-clients": {
      "get": {
        "tags": [
          "ApiClients"
        ],
        "summary": "Lists the caller's integrations.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — none in particular; any authenticated caller with the rights below may call it.\n\n**Person only** — this grants or withdraws authority, so an integration calling it is refused with `403` and the code `client.person_required`. It must be done by the account's owner.",
        "operationId": "ApiClientsList",
        "responses": {
          "200": {
            "description": "The integrations, without their secrets.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ApiClientSummary"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          },
          {
            "apiKey": [ ]
          }
        ]
      },
      "post": {
        "tags": [
          "ApiClients"
        ],
        "summary": "Creates an integration and issues its first secret.",
        "description": "The secret in the response is the only copy. It is not stored in a form\nanyone can read back, so it is shown once and rotated if lost.\n\n**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — none in particular; any authenticated caller with the rights below may call it.\n\n**Person only** — this grants or withdraws authority, so an integration calling it is refused with `403` and the code `client.person_required`. It must be done by the account's owner.",
        "operationId": "ApiClientsCreate",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/CreateApiClientBody"
                  }
                ],
                "description": "What creating an integration needs."
              }
            },
            "text/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/CreateApiClientBody"
                  }
                ],
                "description": "What creating an integration needs."
              }
            },
            "application/*+json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/CreateApiClientBody"
                  }
                ],
                "description": "What creating an integration needs."
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The integration, with its secret.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IssuedSecret"
                }
              }
            }
          },
          "400": {
            "description": "A scope was unknown or not one this owner may grant.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/api-clients/{uuid}": {
      "get": {
        "tags": [
          "ApiClients"
        ],
        "summary": "Describes one integration.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — none in particular; any authenticated caller with the rights below may call it.\n\n**Person only** — this grants or withdraws authority, so an integration calling it is refused with `403` and the code `client.person_required`. It must be done by the account's owner.",
        "operationId": "ApiClientsGet",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The integration.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiClientSummary"
                }
              }
            }
          },
          "404": {
            "description": "No such integration, or not one the caller may see.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/api-clients/{uuid}/secrets": {
      "post": {
        "tags": [
          "ApiClients"
        ],
        "summary": "Issues an additional secret, for rotation with a grace period.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — none in particular; any authenticated caller with the rights below may call it.\n\n**Person only** — this grants or withdraws authority, so an integration calling it is refused with `403` and the code `client.person_required`. It must be done by the account's owner.",
        "operationId": "ApiClientsIssueSecret",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/IssueSecretBody"
                  }
                ],
                "description": "What issuing a secret needs."
              }
            },
            "text/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/IssueSecretBody"
                  }
                ],
                "description": "What issuing a secret needs."
              }
            },
            "application/*+json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/IssueSecretBody"
                  }
                ],
                "description": "What issuing a secret needs."
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The new secret. Shown once.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IssuedSecret"
                }
              }
            }
          },
          "404": {
            "description": "No such integration.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/api-clients/{uuid}/secrets/{secretUuid}": {
      "delete": {
        "tags": [
          "ApiClients"
        ],
        "summary": "Revokes one secret, leaving the integration's others working.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — none in particular; any authenticated caller with the rights below may call it.\n\n**Person only** — this grants or withdraws authority, so an integration calling it is refused with `403` and the code `client.person_required`. It must be done by the account's owner.",
        "operationId": "ApiClientsRevokeSecret",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "secretUuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "The secret no longer authenticates."
          },
          "404": {
            "description": "No such integration or secret.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/api-clients/{uuid}/scopes": {
      "put": {
        "tags": [
          "ApiClients"
        ],
        "summary": "Replaces the scopes an integration holds.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — none in particular; any authenticated caller with the rights below may call it.\n\n**Person only** — this grants or withdraws authority, so an integration calling it is refused with `403` and the code `client.person_required`. It must be done by the account's owner.",
        "operationId": "ApiClientsSetScopes",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/SetScopesBody"
                  }
                ],
                "description": "A replacement set of scopes."
              }
            },
            "text/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/SetScopesBody"
                  }
                ],
                "description": "A replacement set of scopes."
              }
            },
            "application/*+json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/SetScopesBody"
                  }
                ],
                "description": "A replacement set of scopes."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The integration, with its new scopes.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiClientSummary"
                }
              }
            }
          },
          "400": {
            "description": "A scope was unknown or not one this owner may grant.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/api-clients/{uuid}/suspend": {
      "post": {
        "tags": [
          "ApiClients"
        ],
        "summary": "Stops an integration authenticating, keeping its history.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — none in particular; any authenticated caller with the rights below may call it.\n\n**Person only** — this grants or withdraws authority, so an integration calling it is refused with `403` and the code `client.person_required`. It must be done by the account's owner.",
        "operationId": "ApiClientsSuspend",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/SuspendBody"
                  }
                ],
                "description": "Why an integration is being suspended."
              }
            },
            "text/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/SuspendBody"
                  }
                ],
                "description": "Why an integration is being suspended."
              }
            },
            "application/*+json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/SuspendBody"
                  }
                ],
                "description": "Why an integration is being suspended."
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "It can no longer authenticate."
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/api-clients/{uuid}/resume": {
      "post": {
        "tags": [
          "ApiClients"
        ],
        "summary": "Lets a suspended integration authenticate again.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — none in particular; any authenticated caller with the rights below may call it.\n\n**Person only** — this grants or withdraws authority, so an integration calling it is refused with `403` and the code `client.person_required`. It must be done by the account's owner.",
        "operationId": "ApiClientsResume",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "It can authenticate again."
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/api-clients/usage": {
      "get": {
        "tags": [
          "ApiClients"
        ],
        "summary": "Reports what the caller's integrations have been doing.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — none in particular; any authenticated caller with the rights below may call it.\n\n**Person only** — this grants or withdraws authority, so an integration calling it is refused with `403` and the code `client.person_required`. It must be done by the account's owner.",
        "operationId": "ApiClientsUsage",
        "parameters": [
          {
            "name": "uuid",
            "in": "query",
            "description": "One integration, or omitted for all of them.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "from",
            "in": "query",
            "description": "The first Jalali or ISO day included. Defaults to 30 days ago.",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "to",
            "in": "query",
            "description": "The last day included. Defaults to today.",
            "schema": {
              "type": "string",
              "format": "date"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Calls per day and per endpoint, with error and latency figures.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiUsageReport"
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/auth/token": {
      "post": {
        "tags": [
          "Auth"
        ],
        "summary": "Exchanges a mobile number and password for a token pair.",
        "description": "**Authentication** — none. Open to anyone.\n\n**Rate limit** — 10 requests per minute per source address. Credentials are what this guards, so the address is the partition — the account being tried is what an attacker varies.",
        "operationId": "AuthToken",
        "requestBody": {
          "description": "The credentials.",
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/TokenRequest"
                  }
                ],
                "description": "Credentials for a token request."
              }
            },
            "text/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/TokenRequest"
                  }
                ],
                "description": "Credentials for a token request."
              }
            },
            "application/*+json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/TokenRequest"
                  }
                ],
                "description": "Credentials for a token request."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The credentials were accepted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TokenPair"
                }
              }
            }
          },
          "401": {
            "description": "They were not, or the account cannot sign in.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait the number of seconds in the `Retry-After` header. The body is a plain-text Persian sentence, not a problem document: this is refused before any endpoint runs.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/refresh": {
      "post": {
        "tags": [
          "Auth"
        ],
        "summary": "Exchanges a refresh token for a new pair.",
        "description": "**Authentication** — none. Open to anyone.\n\n**Rate limit** — 10 requests per minute per source address. Credentials are what this guards, so the address is the partition — the account being tried is what an attacker varies.",
        "operationId": "AuthRefresh",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/RefreshRequest"
                  }
                ],
                "description": "A refresh token to exchange or revoke."
              }
            },
            "text/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/RefreshRequest"
                  }
                ],
                "description": "A refresh token to exchange or revoke."
              }
            },
            "application/*+json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/RefreshRequest"
                  }
                ],
                "description": "A refresh token to exchange or revoke."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "A new pair was issued and the old token retired.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TokenPair"
                }
              }
            }
          },
          "401": {
            "description": "The token was unknown, expired, revoked or replayed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait the number of seconds in the `Retry-After` header. The body is a plain-text Persian sentence, not a problem document: this is refused before any endpoint runs.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/revoke": {
      "post": {
        "tags": [
          "Auth"
        ],
        "summary": "Ends a session.",
        "description": "Revokes the whole family the token belongs to, not only the token\npresented — signing out is meant to end the session, not one link of it.\nA token we have never seen still answers 204: the caller wanted it gone.\n\n**Authentication** — none. Open to anyone.\n\n**Rate limit** — 10 requests per minute per source address. Credentials are what this guards, so the address is the partition — the account being tried is what an attacker varies.",
        "operationId": "AuthRevoke",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/RefreshRequest"
                  }
                ],
                "description": "A refresh token to exchange or revoke."
              }
            },
            "text/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/RefreshRequest"
                  }
                ],
                "description": "A refresh token to exchange or revoke."
              }
            },
            "application/*+json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/RefreshRequest"
                  }
                ],
                "description": "A refresh token to exchange or revoke."
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "The session is over."
          },
          "429": {
            "description": "Too many requests. Wait the number of seconds in the `Retry-After` header. The body is a plain-text Persian sentence, not a problem document: this is refused before any endpoint runs.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/register": {
      "post": {
        "tags": [
          "Auth"
        ],
        "summary": "Opens an account.",
        "description": "**Authentication** — none. Open to anyone.\n\n**Rate limit** — 10 requests per minute per source address. Credentials are what this guards, so the address is the partition — the account being tried is what an attacker varies.",
        "operationId": "AuthRegister",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/RegisterRequest"
                  }
                ],
                "description": "What is needed to open an account."
              }
            },
            "text/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/RegisterRequest"
                  }
                ],
                "description": "What is needed to open an account."
              }
            },
            "application/*+json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/RegisterRequest"
                  }
                ],
                "description": "What is needed to open an account."
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The account exists.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserSummary"
                }
              }
            }
          },
          "400": {
            "description": "The request was refused; see the code.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait the number of seconds in the `Retry-After` header. The body is a plain-text Persian sentence, not a problem document: this is refused before any endpoint runs.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/me": {
      "get": {
        "tags": [
          "Auth"
        ],
        "summary": "Describes the caller.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — none in particular; any authenticated caller with the rights below may call it.\n\n**Rate limit** — 10 requests per minute per source address. Credentials are what this guards, so the address is the partition — the account being tried is what an attacker varies.",
        "operationId": "AuthMe",
        "responses": {
          "200": {
            "description": "Who the token belongs to.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserSummary"
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait the number of seconds in the `Retry-After` header. The body is a plain-text Persian sentence, not a problem document: this is refused before any endpoint runs.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/auth/change-password": {
      "post": {
        "tags": [
          "Auth"
        ],
        "summary": "Changes the caller's password.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — none in particular; any authenticated caller with the rights below may call it.\n\n**Rate limit** — 10 requests per minute per source address. Credentials are what this guards, so the address is the partition — the account being tried is what an attacker varies.",
        "operationId": "AuthChangePassword",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/ChangePasswordRequest"
                  }
                ],
                "description": "A password change by someone who knows the current one."
              }
            },
            "text/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/ChangePasswordRequest"
                  }
                ],
                "description": "A password change by someone who knows the current one."
              }
            },
            "application/*+json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/ChangePasswordRequest"
                  }
                ],
                "description": "A password change by someone who knows the current one."
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Changed."
          },
          "400": {
            "description": "The current password was wrong, or the new one too weak.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait the number of seconds in the `Retry-After` header. The body is a plain-text Persian sentence, not a problem document: this is refused before any endpoint runs.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/auth/forgot-password": {
      "post": {
        "tags": [
          "Auth"
        ],
        "summary": "Starts a password reset.",
        "description": "Always 202, whether or not the number belongs to an account. Answering\ndifferently would turn this into a way to ask which numbers are\nregistered.\n\n**Authentication** — none. Open to anyone.\n\n**Rate limit** — 10 requests per minute per source address. Credentials are what this guards, so the address is the partition — the account being tried is what an attacker varies.",
        "operationId": "AuthForgotPassword",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/ForgotPasswordRequest"
                  }
                ],
                "description": "The start of a password reset."
              }
            },
            "text/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/ForgotPasswordRequest"
                  }
                ],
                "description": "The start of a password reset."
              }
            },
            "application/*+json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/ForgotPasswordRequest"
                  }
                ],
                "description": "The start of a password reset."
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "If the number is known, a code is on its way."
          },
          "429": {
            "description": "Too many requests. Wait the number of seconds in the `Retry-After` header. The body is a plain-text Persian sentence, not a problem document: this is refused before any endpoint runs.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/reset-password": {
      "post": {
        "tags": [
          "Auth"
        ],
        "summary": "Completes a password reset.",
        "description": "**Authentication** — none. Open to anyone.\n\n**Rate limit** — 10 requests per minute per source address. Credentials are what this guards, so the address is the partition — the account being tried is what an attacker varies.",
        "operationId": "AuthResetPassword",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/ResetPasswordRequest"
                  }
                ],
                "description": "The completion of a password reset."
              }
            },
            "text/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/ResetPasswordRequest"
                  }
                ],
                "description": "The completion of a password reset."
              }
            },
            "application/*+json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/ResetPasswordRequest"
                  }
                ],
                "description": "The completion of a password reset."
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "The password is changed."
          },
          "400": {
            "description": "The token was wrong, used or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait the number of seconds in the `Retry-After` header. The body is a plain-text Persian sentence, not a problem document: this is refused before any endpoint runs.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/contacts": {
      "get": {
        "tags": [
          "Contacts"
        ],
        "summary": "Lists the caller's contacts.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — an integration needs `contacts:read`. A person signing in for themselves needs none.\n\n**Rate limit** — 120 requests per minute per integration, replenished continuously, so a burst of that size is allowed. Counted per client rather than per address, so integrations sharing a host do not spend each other's allowance. A call made by a person is not counted.",
        "operationId": "ContactsList",
        "parameters": [
          {
            "name": "group",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "search",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The contacts.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ContactSummary"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait the number of seconds in the `Retry-After` header. The body is a plain-text Persian sentence, not a problem document: this is refused before any endpoint runs.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [
              "contacts:read"
            ]
          },
          {
            "apiKey": [ ]
          }
        ]
      },
      "post": {
        "tags": [
          "Contacts"
        ],
        "summary": "Adds a contact.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — an integration needs `contacts:write`. A person signing in for themselves needs none.\n\n**Rate limit** — 120 requests per minute per integration, replenished continuously, so a burst of that size is allowed. Counted per client rather than per address, so integrations sharing a host do not spend each other's allowance. A call made by a person is not counted.",
        "operationId": "ContactsCreate",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/ContactRequest"
                  }
                ],
                "description": "What is needed to save a contact."
              }
            },
            "text/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/ContactRequest"
                  }
                ],
                "description": "What is needed to save a contact."
              }
            },
            "application/*+json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/ContactRequest"
                  }
                ],
                "description": "What is needed to save a contact."
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The contact.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ContactSummary"
                }
              }
            }
          },
          "400": {
            "description": "The number was invalid or already in the address book.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait the number of seconds in the `Retry-After` header. The body is a plain-text Persian sentence, not a problem document: this is refused before any endpoint runs.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [
              "contacts:write"
            ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/contacts/{uuid}": {
      "put": {
        "tags": [
          "Contacts"
        ],
        "summary": "Changes a contact.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — an integration needs `contacts:write`. A person signing in for themselves needs none.\n\n**Rate limit** — 120 requests per minute per integration, replenished continuously, so a burst of that size is allowed. Counted per client rather than per address, so integrations sharing a host do not spend each other's allowance. A call made by a person is not counted.",
        "operationId": "ContactsUpdate",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/ContactRequest"
                  }
                ],
                "description": "What is needed to save a contact."
              }
            },
            "text/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/ContactRequest"
                  }
                ],
                "description": "What is needed to save a contact."
              }
            },
            "application/*+json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/ContactRequest"
                  }
                ],
                "description": "What is needed to save a contact."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The contact as it now stands.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ContactSummary"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait the number of seconds in the `Retry-After` header. The body is a plain-text Persian sentence, not a problem document: this is refused before any endpoint runs.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [
              "contacts:write"
            ]
          },
          {
            "apiKey": [ ]
          }
        ]
      },
      "delete": {
        "tags": [
          "Contacts"
        ],
        "summary": "Removes a contact.",
        "description": "The payment links already sent to them are untouched — those are\nfinancial records, and they carry their payer details rather than joining\nto this table.\n\n**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — an integration needs `contacts:write`. A person signing in for themselves needs none.\n\n**Rate limit** — 120 requests per minute per integration, replenished continuously, so a burst of that size is allowed. Counted per client rather than per address, so integrations sharing a host do not spend each other's allowance. A call made by a person is not counted.",
        "operationId": "ContactsDelete",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "It is gone."
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait the number of seconds in the `Retry-After` header. The body is a plain-text Persian sentence, not a problem document: this is refused before any endpoint runs.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [
              "contacts:write"
            ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/contacts/groups": {
      "get": {
        "tags": [
          "Contacts"
        ],
        "summary": "Lists the caller's groups, with how many are in each.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — an integration needs `contacts:read`. A person signing in for themselves needs none.\n\n**Rate limit** — 120 requests per minute per integration, replenished continuously, so a burst of that size is allowed. Counted per client rather than per address, so integrations sharing a host do not spend each other's allowance. A call made by a person is not counted.",
        "operationId": "ContactsGroups",
        "responses": {
          "200": {
            "description": "The groups.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/GroupSummary"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait the number of seconds in the `Retry-After` header. The body is a plain-text Persian sentence, not a problem document: this is refused before any endpoint runs.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [
              "contacts:read"
            ]
          },
          {
            "apiKey": [ ]
          }
        ]
      },
      "post": {
        "tags": [
          "Contacts"
        ],
        "summary": "Creates a group.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — an integration needs `contacts:write`. A person signing in for themselves needs none.\n\n**Rate limit** — 120 requests per minute per integration, replenished continuously, so a burst of that size is allowed. Counted per client rather than per address, so integrations sharing a host do not spend each other's allowance. A call made by a person is not counted.",
        "operationId": "ContactsCreateGroup",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/CreateGroupBody"
                  }
                ],
                "description": "A new group."
              }
            },
            "text/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/CreateGroupBody"
                  }
                ],
                "description": "A new group."
              }
            },
            "application/*+json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/CreateGroupBody"
                  }
                ],
                "description": "A new group."
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The group.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GroupSummary"
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait the number of seconds in the `Retry-After` header. The body is a plain-text Persian sentence, not a problem document: this is refused before any endpoint runs.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [
              "contacts:write"
            ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/contacts/import": {
      "post": {
        "tags": [
          "Contacts"
        ],
        "summary": "Imports contacts from a CSV file.",
        "description": "Answers with what it took and what it skipped, line by line. A caller\ntold only \"done\" when forty rows were dropped finds out one missing\npayment at a time.\n\n**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — an integration needs `contacts:write`. A person signing in for themselves needs none.\n\n**Rate limit** — 120 requests per minute per integration, replenished continuously, so a burst of that size is allowed. Counted per client rather than per address, so integrations sharing a host do not spend each other's allowance. A call made by a person is not counted.",
        "operationId": "ContactsImport",
        "parameters": [
          {
            "name": "group",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary"
                  }
                }
              },
              "encoding": {
                "file": {
                  "style": "form"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "What was imported, updated and skipped.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ImportReport"
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait the number of seconds in the `Retry-After` header. The body is a plain-text Persian sentence, not a problem document: this is refused before any endpoint runs.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [
              "contacts:write"
            ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/contacts/export": {
      "get": {
        "tags": [
          "Contacts"
        ],
        "summary": "Exports the caller's contacts as CSV.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — an integration needs `contacts:read`. A person signing in for themselves needs none.\n\n**Rate limit** — 120 requests per minute per integration, replenished continuously, so a burst of that size is allowed. Counted per client rather than per address, so integrations sharing a host do not spend each other's allowance. A call made by a person is not counted.",
        "operationId": "ContactsExport",
        "parameters": [
          {
            "name": "group",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The file."
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait the number of seconds in the `Retry-After` header. The body is a plain-text Persian sentence, not a problem document: this is refused before any endpoint runs.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [
              "contacts:read"
            ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/documents": {
      "get": {
        "tags": [
          "Documents"
        ],
        "summary": "Lists the caller's documents.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — none in particular; any authenticated caller with the rights below may call it.",
        "operationId": "DocumentsList",
        "responses": {
          "200": {
            "description": "The documents, without their contents.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/AttachmentSummary"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          },
          {
            "apiKey": [ ]
          }
        ]
      },
      "post": {
        "tags": [
          "Documents"
        ],
        "summary": "Uploads a document.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — none in particular; any authenticated caller with the rights below may call it.\n\n**Person only** — this grants or withdraws authority, so an integration calling it is refused with `403` and the code `client.person_required`. It must be done by the account's owner.",
        "operationId": "DocumentsUpload",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "file": {
                    "type": "string",
                    "description": "The file, as multipart form data.",
                    "format": "binary"
                  },
                  "type": {
                    "allOf": [
                      {
                        "$ref": "#/components/schemas/AttachmentType"
                      }
                    ],
                    "description": "What a stored document is.\n\nValues:\n\n* `0` — `Other`\n* `1` — `Avatar`\n* `2` — `NationalCard`\n* `3` — `BirthCertificate`\n* `4` — `IncorporationNotice`\n* `5` — `ChangesNotice`\n* `6` — `ArticlesOfAssociation`\n* `7` — `TradeLicence`\n* `8` — `Ticket`\n* `9` — `Product`\n* `10` — `BusinessLogo`"
                  },
                  "description": {
                    "type": "string",
                    "description": "What it is for."
                  },
                  "terminalUuid": {
                    "type": "string",
                    "description": "Which terminal it belongs to, for the kinds that describe one business —\na trade licence, a logo. Left out for identity documents, which belong to\nthe account holder.",
                    "format": "uuid"
                  }
                }
              },
              "encoding": {
                "file": {
                  "style": "form"
                },
                "type": {
                  "style": "form"
                },
                "description": {
                  "style": "form"
                },
                "terminalUuid": {
                  "style": "form"
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The stored document.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AttachmentSummary"
                }
              }
            }
          },
          "400": {
            "description": "The file was too large or of a type not accepted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/documents/{uuid}": {
      "get": {
        "tags": [
          "Documents"
        ],
        "summary": "Downloads a document.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — none in particular; any authenticated caller with the rights below may call it.",
        "operationId": "DocumentsDownload",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The file."
          },
          "404": {
            "description": "No such document, or not one the caller may read.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          },
          {
            "apiKey": [ ]
          }
        ]
      },
      "delete": {
        "tags": [
          "Documents"
        ],
        "summary": "Deletes one of the caller's documents.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — none in particular; any authenticated caller with the rights below may call it.\n\n**Person only** — this grants or withdraws authority, so an integration calling it is refused with `403` and the code `client.person_required`. It must be done by the account's owner.",
        "operationId": "DocumentsDelete",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "It is gone."
          },
          "404": {
            "description": "No such document, or not one the caller owns.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/links": {
      "get": {
        "tags": [
          "Links"
        ],
        "summary": "Lists the caller's links, newest first.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — an integration needs `links:read`. A person signing in for themselves needs none.\n\n**Rate limit** — 120 requests per minute per integration, replenished continuously, so a burst of that size is allowed. Counted per client rather than per address, so integrations sharing a host do not spend each other's allowance. A call made by a person is not counted.",
        "operationId": "LinksList",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "description": "Only links in this state.",
            "schema": {
              "allOf": [
                {
                  "$ref": "#/components/schemas/LinkStatus"
                }
              ],
              "description": "Values:\n\n* `0` — `Pending`\n* `1` — `Viewed`\n* `2` — `InProgress`\n* `3` — `Succeeded`\n* `4` — `Failed`\n* `5` — `Expired`"
            }
          },
          {
            "name": "search",
            "in": "query",
            "description": "Matches a payer's name or number, or a short id.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "skip",
            "in": "query",
            "description": "How many to pass over.",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "take",
            "in": "query",
            "description": "How many to return. Capped at 200.",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The links.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/LinkSummary"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait the number of seconds in the `Retry-After` header. The body is a plain-text Persian sentence, not a problem document: this is refused before any endpoint runs.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [
              "links:read"
            ]
          },
          {
            "apiKey": [ ]
          }
        ]
      },
      "post": {
        "tags": [
          "Links"
        ],
        "summary": "Creates a payment link.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — an integration needs `links:write`. A person signing in for themselves needs none.\n\n**Rate limit** — 120 requests per minute per integration, replenished continuously, so a burst of that size is allowed. Counted per client rather than per address, so integrations sharing a host do not spend each other's allowance. A call made by a person is not counted.",
        "operationId": "LinksCreate",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/CreateLinkBody"
                  }
                ],
                "description": "What creating a link needs."
              }
            },
            "text/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/CreateLinkBody"
                  }
                ],
                "description": "What creating a link needs."
              }
            },
            "application/*+json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/CreateLinkBody"
                  }
                ],
                "description": "What creating a link needs."
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The link, with the address to send.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LinkSummary"
                }
              }
            }
          },
          "400": {
            "description": "The terminal, the amount or the expiry was refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait the number of seconds in the `Retry-After` header. The body is a plain-text Persian sentence, not a problem document: this is refused before any endpoint runs.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [
              "links:write"
            ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/links/{uuid}": {
      "get": {
        "tags": [
          "Links"
        ],
        "summary": "Describes one link.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — an integration needs `links:read`. A person signing in for themselves needs none.\n\n**Rate limit** — 120 requests per minute per integration, replenished continuously, so a burst of that size is allowed. Counted per client rather than per address, so integrations sharing a host do not spend each other's allowance. A call made by a person is not counted.",
        "operationId": "LinksGet",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The link.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LinkSummary"
                }
              }
            }
          },
          "404": {
            "description": "No such link, or not one the caller may see.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait the number of seconds in the `Retry-After` header. The body is a plain-text Persian sentence, not a problem document: this is refused before any endpoint runs.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [
              "links:read"
            ]
          },
          {
            "apiKey": [ ]
          }
        ]
      },
      "delete": {
        "tags": [
          "Links"
        ],
        "summary": "Deletes a link that has never been paid.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — an integration needs `links:write`. A person signing in for themselves needs none.\n\n**Rate limit** — 120 requests per minute per integration, replenished continuously, so a burst of that size is allowed. Counted per client rather than per address, so integrations sharing a host do not spend each other's allowance. A call made by a person is not counted.",
        "operationId": "LinksDelete",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "It is gone."
          },
          "409": {
            "description": "It has a transaction against it, so it stays.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait the number of seconds in the `Retry-After` header. The body is a plain-text Persian sentence, not a problem document: this is refused before any endpoint runs.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [
              "links:write"
            ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/links/bulk/{groupUuid}": {
      "post": {
        "tags": [
          "Links"
        ],
        "summary": "Creates a link for every contact in a group.",
        "description": "Answers when the links exist, not when the work has been scheduled, and\nreports the contacts it passed over — a missing mobile number, usually —\nso the merchant knows who will not receive one.\n\n**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — an integration needs `links:write`. A person signing in for themselves needs none.\n\n**Rate limit** — 120 requests per minute per integration, replenished continuously, so a burst of that size is allowed. Counted per client rather than per address, so integrations sharing a host do not spend each other's allowance. A call made by a person is not counted.",
        "operationId": "LinksCreateForGroup",
        "parameters": [
          {
            "name": "groupUuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/CreateLinkBody"
                  }
                ],
                "description": "What creating a link needs."
              }
            },
            "text/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/CreateLinkBody"
                  }
                ],
                "description": "What creating a link needs."
              }
            },
            "application/*+json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/CreateLinkBody"
                  }
                ],
                "description": "What creating a link needs."
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "What was created, and what was skipped.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BulkLinkResult"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait the number of seconds in the `Retry-After` header. The body is a plain-text Persian sentence, not a problem document: this is refused before any endpoint runs.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [
              "links:write"
            ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/links/{uuid}/check": {
      "get": {
        "tags": [
          "Links"
        ],
        "summary": "Reports whether a link has been paid.",
        "description": "What an integration polls while it waits for a payer. Kept separate from\nthe full description so that it can stay cheap.\n\n**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — an integration needs `links:read`. A person signing in for themselves needs none.\n\n**Rate limit** — 120 requests per minute per integration, replenished continuously, so a burst of that size is allowed. Counted per client rather than per address, so integrations sharing a host do not spend each other's allowance. A call made by a person is not counted.",
        "operationId": "LinksCheck",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The link's current state.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LinkStatusResponse"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait the number of seconds in the `Retry-After` header. The body is a plain-text Persian sentence, not a problem document: this is refused before any endpoint runs.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [
              "links:read"
            ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/links/{uuid}/invoice": {
      "get": {
        "tags": [
          "Links"
        ],
        "summary": "The invoice for a link, as a PDF.",
        "description": "Bytes rather than a URL: an integration that emails its own customers\nattaches this, and a link to a document behind authentication is a link\nits customer cannot open.\n\n**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — an integration needs `links:read`. A person signing in for themselves needs none.\n\n**Rate limit** — 120 requests per minute per integration, replenished continuously, so a burst of that size is allowed. Counted per client rather than per address, so integrations sharing a host do not spend each other's allowance. A call made by a person is not counted.",
        "operationId": "LinksInvoice",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The invoice."
          },
          "404": {
            "description": "No such link, or not one the caller may read.",
            "content": {
              "application/pdf": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait the number of seconds in the `Retry-After` header. The body is a plain-text Persian sentence, not a problem document: this is refused before any endpoint runs.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [
              "links:read"
            ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/merchants/{userUuid}/registration": {
      "get": {
        "tags": [
          "Merchants"
        ],
        "summary": "Reads a merchant's registration dossier.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — an integration needs `merchants:read`. A person signing in for themselves needs none.",
        "operationId": "MerchantsGet",
        "parameters": [
          {
            "name": "userUuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The dossier, empty of signatories where none are entered.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MerchantDossier"
                }
              }
            }
          },
          "404": {
            "description": "No such merchant, or not one the caller may see.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [
              "merchants:read"
            ]
          },
          {
            "apiKey": [ ]
          }
        ]
      },
      "put": {
        "tags": [
          "Merchants"
        ],
        "summary": "Saves a merchant's registration dossier.",
        "description": "The signatories given replace those held. A national code that fails its\ncheck digit is refused here rather than passed to the acquirer, which\nrejects the whole dossier days later and says only that it was rejected.\n\n**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — an integration needs `merchants:write`. A person signing in for themselves needs none.",
        "operationId": "MerchantsSave",
        "parameters": [
          {
            "name": "userUuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/MerchantDossierRequest"
                  }
                ],
                "description": "What is needed to save a dossier."
              }
            },
            "text/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/MerchantDossierRequest"
                  }
                ],
                "description": "What is needed to save a dossier."
              }
            },
            "application/*+json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/MerchantDossierRequest"
                  }
                ],
                "description": "What is needed to save a dossier."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The dossier as it now stands.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MerchantDossier"
                }
              }
            }
          },
          "400": {
            "description": "A name was missing, or a national code or mobile number was invalid.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "No such merchant, or not one the caller may see.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [
              "merchants:write"
            ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/oauth/token": {
      "post": {
        "tags": [
          "OAuth"
        ],
        "summary": "Exchanges client credentials for an access token.",
        "description": "**Authentication** — none. Open to anyone.\n\n**Rate limit** — 10 requests per minute per source address. Credentials are what this guards, so the address is the partition — the account being tried is what an attacker varies.",
        "operationId": "OAuthToken",
        "requestBody": {
          "description": "The client identifier and secret.",
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/ClientCredentialsRequest"
                  }
                ],
                "description": "An OAuth2 client-credentials request."
              }
            },
            "text/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/ClientCredentialsRequest"
                  }
                ],
                "description": "An OAuth2 client-credentials request."
              }
            },
            "application/*+json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/ClientCredentialsRequest"
                  }
                ],
                "description": "An OAuth2 client-credentials request."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The credentials were accepted.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ClientTokenResponse"
                }
              }
            }
          },
          "401": {
            "description": "They were not, or the client may not call from here.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait the number of seconds in the `Retry-After` header. The body is a plain-text Persian sentence, not a problem document: this is refused before any endpoint runs.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/oauth/scopes": {
      "get": {
        "tags": [
          "OAuth"
        ],
        "summary": "Lists the scopes this system understands.",
        "description": "So that an integrator building a consent screen does not have to hard-code\nthem, and a merchant reading one sees the same list the platform enforces.\n\n**Authentication** — none. Open to anyone.\n\n**Rate limit** — 10 requests per minute per source address. Credentials are what this guards, so the address is the partition — the account being tried is what an attacker varies.",
        "operationId": "OAuthScopes",
        "responses": {
          "200": {
            "description": "Every grantable scope.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait the number of seconds in the `Retry-After` header. The body is a plain-text Persian sentence, not a problem document: this is refused before any endpoint runs.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/pricing": {
      "get": {
        "tags": [
          "Pricing"
        ],
        "summary": "The default bracket table.",
        "description": "**Authentication** — none. Open to anyone.",
        "operationId": "PricingTable",
        "responses": {
          "200": {
            "description": "The bands, ascending. Amounts in rials.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PriceBand"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/products": {
      "get": {
        "tags": [
          "Products"
        ],
        "summary": "Lists the caller's products.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — an integration needs `products:read`. A person signing in for themselves needs none.",
        "operationId": "ProductsList",
        "parameters": [
          {
            "name": "search",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "activeOnly",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The catalogue.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ProductSummary"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [
              "products:read"
            ]
          },
          {
            "apiKey": [ ]
          }
        ]
      },
      "post": {
        "tags": [
          "Products"
        ],
        "summary": "Adds a product.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — an integration needs `products:write`. A person signing in for themselves needs none.",
        "operationId": "ProductsCreate",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/ProductRequest"
                  }
                ],
                "description": "What is needed to save a product."
              }
            },
            "text/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/ProductRequest"
                  }
                ],
                "description": "What is needed to save a product."
              }
            },
            "application/*+json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/ProductRequest"
                  }
                ],
                "description": "What is needed to save a product."
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The product.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProductSummary"
                }
              }
            }
          },
          "400": {
            "description": "It had no name, or a price that cannot be charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [
              "products:write"
            ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/products/{uuid}": {
      "get": {
        "tags": [
          "Products"
        ],
        "summary": "Describes one product.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — an integration needs `products:read`. A person signing in for themselves needs none.",
        "operationId": "ProductsGet",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The product, with its photographs.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProductSummary"
                }
              }
            }
          },
          "404": {
            "description": "No such product, or not one the caller owns.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [
              "products:read"
            ]
          },
          {
            "apiKey": [ ]
          }
        ]
      },
      "put": {
        "tags": [
          "Products"
        ],
        "summary": "Changes a product.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — an integration needs `products:write`. A person signing in for themselves needs none.",
        "operationId": "ProductsUpdate",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/ProductRequest"
                  }
                ],
                "description": "What is needed to save a product."
              }
            },
            "text/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/ProductRequest"
                  }
                ],
                "description": "What is needed to save a product."
              }
            },
            "application/*+json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/ProductRequest"
                  }
                ],
                "description": "What is needed to save a product."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The product as it now stands.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProductSummary"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [
              "products:write"
            ]
          },
          {
            "apiKey": [ ]
          }
        ]
      },
      "delete": {
        "tags": [
          "Products"
        ],
        "summary": "Removes a product.",
        "description": "The links and payments made for it stay. A payment is a financial record\nand does not disappear because a catalogue entry did.\n\n**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — an integration needs `products:write`. A person signing in for themselves needs none.",
        "operationId": "ProductsDelete",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "It is gone."
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [
              "products:write"
            ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/products/{uuid}/photos": {
      "post": {
        "tags": [
          "Products"
        ],
        "summary": "Attaches a photograph.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — an integration needs `products:write`. A person signing in for themselves needs none.",
        "operationId": "ProductsAddPhoto",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary"
                  }
                }
              },
              "encoding": {
                "file": {
                  "style": "form"
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The photograph. Serve it from `/p/photo/{uuid}`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProductPhoto"
                }
              }
            }
          },
          "400": {
            "description": "Not an image, too large, or the product is full.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [
              "products:write"
            ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/products/photos/{photoUuid}": {
      "delete": {
        "tags": [
          "Products"
        ],
        "summary": "Removes a photograph.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — an integration needs `products:write`. A person signing in for themselves needs none.",
        "operationId": "ProductsRemovePhoto",
        "parameters": [
          {
            "name": "photoUuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "It is gone."
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [
              "products:write"
            ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/reports/summary": {
      "get": {
        "tags": [
          "Reports"
        ],
        "summary": "The headline figures for a period.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — an integration needs `transactions:read`. A person signing in for themselves needs none.\n\n**Rate limit** — 120 requests per minute per integration, replenished continuously, so a burst of that size is allowed. Counted per client rather than per address, so integrations sharing a host do not spend each other's allowance. A call made by a person is not counted.",
        "operationId": "ReportsSummary",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Takings, counts, commission and success rate.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReportSummary"
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait the number of seconds in the `Retry-After` header. The body is a plain-text Persian sentence, not a problem document: this is refused before any endpoint runs.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [
              "transactions:read"
            ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/reports/daily": {
      "get": {
        "tags": [
          "Reports"
        ],
        "summary": "Takings per day, with empty days included.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — an integration needs `transactions:read`. A person signing in for themselves needs none.\n\n**Rate limit** — 120 requests per minute per integration, replenished continuously, so a burst of that size is allowed. Counted per client rather than per address, so integrations sharing a host do not spend each other's allowance. A call made by a person is not counted.",
        "operationId": "ReportsDaily",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One row per day in the period.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/DailyTotal"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait the number of seconds in the `Retry-After` header. The body is a plain-text Persian sentence, not a problem document: this is refused before any endpoint runs.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [
              "transactions:read"
            ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/reports/by-terminal": {
      "get": {
        "tags": [
          "Reports"
        ],
        "summary": "Takings per terminal.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — an integration needs `transactions:read`. A person signing in for themselves needs none.\n\n**Rate limit** — 120 requests per minute per integration, replenished continuously, so a burst of that size is allowed. Counted per client rather than per address, so integrations sharing a host do not spend each other's allowance. A call made by a person is not counted.",
        "operationId": "ReportsByTerminal",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One row per terminal, busiest first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/GroupedTotal"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait the number of seconds in the `Retry-After` header. The body is a plain-text Persian sentence, not a problem document: this is refused before any endpoint runs.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [
              "transactions:read"
            ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/reports/by-gateway": {
      "get": {
        "tags": [
          "Reports"
        ],
        "summary": "Takings per bank, with each one's success rate.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — an integration needs `transactions:read`. A person signing in for themselves needs none.\n\n**Rate limit** — 120 requests per minute per integration, replenished continuously, so a burst of that size is allowed. Counted per client rather than per address, so integrations sharing a host do not spend each other's allowance. A call made by a person is not counted.",
        "operationId": "ReportsByGateway",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One row per acquirer.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/GatewayPerformance"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait the number of seconds in the `Retry-After` header. The body is a plain-text Persian sentence, not a problem document: this is refused before any endpoint runs.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [
              "transactions:read"
            ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/reports/funnel": {
      "get": {
        "tags": [
          "Reports"
        ],
        "summary": "How many links were created, opened and paid.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — an integration needs `links:read`. A person signing in for themselves needs none.\n\n**Rate limit** — 120 requests per minute per integration, replenished continuously, so a burst of that size is allowed. Counted per client rather than per address, so integrations sharing a host do not spend each other's allowance. A call made by a person is not counted.",
        "operationId": "ReportsFunnel",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The funnel.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LinkFunnel"
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait the number of seconds in the `Retry-After` header. The body is a plain-text Persian sentence, not a problem document: this is refused before any endpoint runs.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [
              "links:read"
            ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/reports/sms-usage": {
      "get": {
        "tags": [
          "Reports"
        ],
        "summary": "What messages cost over the period.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — an integration needs `wallet:read`. A person signing in for themselves needs none.\n\n**Rate limit** — 120 requests per minute per integration, replenished continuously, so a burst of that size is allowed. Counted per client rather than per address, so integrations sharing a host do not spend each other's allowance. A call made by a person is not counted.",
        "operationId": "ReportsSmsUsage",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Counts, segments and cost.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SmsUsage"
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait the number of seconds in the `Retry-After` header. The body is a plain-text Persian sentence, not a problem document: this is refused before any endpoint runs.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [
              "wallet:read"
            ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/reports/transactions": {
      "get": {
        "tags": [
          "Reports"
        ],
        "summary": "The transactions themselves, paged and filtered.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — an integration needs `transactions:read`. A person signing in for themselves needs none.\n\n**Rate limit** — 120 requests per minute per integration, replenished continuously, so a burst of that size is allowed. Counted per client rather than per address, so integrations sharing a host do not spend each other's allowance. A call made by a person is not counted.",
        "operationId": "ReportsTransactions",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "search",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "succeeded",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "minimum",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "maximum",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "skip",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "take",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          },
          {
            "name": "sort",
            "in": "query",
            "description": "What a transaction grid can be ordered by.\n\nValues:\n\n* `0` — `Date`\n* `1` — `Amount`\n* `2` — `Status`",
            "schema": {
              "allOf": [
                {
                  "$ref": "#/components/schemas/TransactionSort"
                }
              ],
              "description": "What a transaction grid can be ordered by.\n\nValues:\n\n* `0` — `Date`\n* `1` — `Amount`\n* `2` — `Status`"
            }
          },
          {
            "name": "descending",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "One page, with the total count and sum behind it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TransactionPage"
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait the number of seconds in the `Retry-After` header. The body is a plain-text Persian sentence, not a problem document: this is refused before any endpoint runs.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [
              "transactions:read"
            ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/schedules": {
      "get": {
        "tags": [
          "Schedules"
        ],
        "summary": "Lists the caller's schedules, soonest due first.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — an integration needs `links:read`. A person signing in for themselves needs none.\n\n**Rate limit** — 120 requests per minute per integration, replenished continuously, so a burst of that size is allowed. Counted per client rather than per address, so integrations sharing a host do not spend each other's allowance. A call made by a person is not counted.",
        "operationId": "SchedulesList",
        "responses": {
          "200": {
            "description": "The schedules.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ScheduleSummary"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait the number of seconds in the `Retry-After` header. The body is a plain-text Persian sentence, not a problem document: this is refused before any endpoint runs.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [
              "links:read"
            ]
          },
          {
            "apiKey": [ ]
          }
        ]
      },
      "post": {
        "tags": [
          "Schedules"
        ],
        "summary": "Sets up a recurring bill.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — an integration needs `links:write`. A person signing in for themselves needs none.\n\n**Rate limit** — 120 requests per minute per integration, replenished continuously, so a burst of that size is allowed. Counted per client rather than per address, so integrations sharing a host do not spend each other's allowance. A call made by a person is not counted.",
        "operationId": "SchedulesCreate",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/CreateScheduleBody"
                  }
                ],
                "description": "What setting up a recurring bill needs."
              }
            },
            "text/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/CreateScheduleBody"
                  }
                ],
                "description": "What setting up a recurring bill needs."
              }
            },
            "application/*+json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/CreateScheduleBody"
                  }
                ],
                "description": "What setting up a recurring bill needs."
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The schedule, with the date it first runs.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ScheduleSummary"
                }
              }
            }
          },
          "400": {
            "description": "The terminal, the recipient or the dates were refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait the number of seconds in the `Retry-After` header. The body is a plain-text Persian sentence, not a problem document: this is refused before any endpoint runs.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [
              "links:write"
            ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/schedules/{uuid}/pause": {
      "post": {
        "tags": [
          "Schedules"
        ],
        "summary": "Pauses a schedule.",
        "description": "Resuming later picks up from the next date due, not from the ones missed\nwhile it was paused — a merchant who pauses for three months does not\nwant three months of bills at once.\n\n**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — an integration needs `links:write`. A person signing in for themselves needs none.\n\n**Rate limit** — 120 requests per minute per integration, replenished continuously, so a burst of that size is allowed. Counted per client rather than per address, so integrations sharing a host do not spend each other's allowance. A call made by a person is not counted.",
        "operationId": "SchedulesPause",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "It is paused."
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait the number of seconds in the `Retry-After` header. The body is a plain-text Persian sentence, not a problem document: this is refused before any endpoint runs.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [
              "links:write"
            ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/schedules/{uuid}/resume": {
      "post": {
        "tags": [
          "Schedules"
        ],
        "summary": "Resumes a schedule.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — an integration needs `links:write`. A person signing in for themselves needs none.\n\n**Rate limit** — 120 requests per minute per integration, replenished continuously, so a burst of that size is allowed. Counted per client rather than per address, so integrations sharing a host do not spend each other's allowance. A call made by a person is not counted.",
        "operationId": "SchedulesResume",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "It is running again."
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait the number of seconds in the `Retry-After` header. The body is a plain-text Persian sentence, not a problem document: this is refused before any endpoint runs.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [
              "links:write"
            ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/schedules/{uuid}": {
      "delete": {
        "tags": [
          "Schedules"
        ],
        "summary": "Removes a schedule. The links it already made stay.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — an integration needs `links:write`. A person signing in for themselves needs none.\n\n**Rate limit** — 120 requests per minute per integration, replenished continuously, so a burst of that size is allowed. Counted per client rather than per address, so integrations sharing a host do not spend each other's allowance. A call made by a person is not counted.",
        "operationId": "SchedulesDelete",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "It is gone."
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait the number of seconds in the `Retry-After` header. The body is a plain-text Persian sentence, not a problem document: this is refused before any endpoint runs.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [
              "links:write"
            ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/terminals": {
      "get": {
        "tags": [
          "Terminals"
        ],
        "summary": "Lists the caller's terminals.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — an integration needs `terminals:read`. A person signing in for themselves needs none.",
        "operationId": "TerminalsList",
        "responses": {
          "200": {
            "description": "The terminals.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/TerminalSummary"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [
              "terminals:read"
            ]
          },
          {
            "apiKey": [ ]
          }
        ]
      },
      "post": {
        "tags": [
          "Terminals"
        ],
        "summary": "Registers a terminal, which starts awaiting approval.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — none in particular; any authenticated caller with the rights below may call it.\n\n**Person only** — this grants or withdraws authority, so an integration calling it is refused with `403` and the code `client.person_required`. It must be done by the account's owner.",
        "operationId": "TerminalsCreate",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/TerminalRequest"
                  }
                ],
                "description": "What is needed to register or reconfigure a terminal."
              }
            },
            "text/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/TerminalRequest"
                  }
                ],
                "description": "What is needed to register or reconfigure a terminal."
              }
            },
            "application/*+json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/TerminalRequest"
                  }
                ],
                "description": "What is needed to register or reconfigure a terminal."
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The terminal, pending review.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TerminalDetail"
                }
              }
            }
          },
          "400": {
            "description": "The IBAN, gateway or pay-page path was refused.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/terminals/{uuid}": {
      "get": {
        "tags": [
          "Terminals"
        ],
        "summary": "Describes one terminal, with its split accounts.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — an integration needs `terminals:read`. A person signing in for themselves needs none.",
        "operationId": "TerminalsGet",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The terminal.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TerminalDetail"
                }
              }
            }
          },
          "404": {
            "description": "No such terminal, or not one the caller may see.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [
              "terminals:read"
            ]
          },
          {
            "apiKey": [ ]
          }
        ]
      },
      "put": {
        "tags": [
          "Terminals"
        ],
        "summary": "Changes a terminal's configuration.",
        "description": "Moving the settlement IBAN on an approved terminal returns it for review:\nthe account the acquirer holds on file is no longer the account we would\nsettle to.\n\n**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — none in particular; any authenticated caller with the rights below may call it.\n\n**Person only** — this grants or withdraws authority, so an integration calling it is refused with `403` and the code `client.person_required`. It must be done by the account's owner.",
        "operationId": "TerminalsUpdate",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/TerminalRequest"
                  }
                ],
                "description": "What is needed to register or reconfigure a terminal."
              }
            },
            "text/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/TerminalRequest"
                  }
                ],
                "description": "What is needed to register or reconfigure a terminal."
              }
            },
            "application/*+json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/TerminalRequest"
                  }
                ],
                "description": "What is needed to register or reconfigure a terminal."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The terminal as it now stands.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TerminalDetail"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/terminals/{uuid}/history": {
      "get": {
        "tags": [
          "Terminals"
        ],
        "summary": "The terminal's history: every status change, with who and why.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — an integration needs `terminals:read`. A person signing in for themselves needs none.",
        "operationId": "TerminalsHistory",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The history, most recent first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/TerminalEvent"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [
              "terminals:read"
            ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/terminals/{uuid}/split-accounts": {
      "put": {
        "tags": [
          "Terminals"
        ],
        "summary": "Replaces the IBANs settlement is split across.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — none in particular; any authenticated caller with the rights below may call it.\n\n**Person only** — this grants or withdraws authority, so an integration calling it is refused with `403` and the code `client.person_required`. It must be done by the account's owner.",
        "operationId": "TerminalsSetSplitAccounts",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/SplitAccountRequest"
                }
              }
            },
            "text/json": {
              "schema": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/SplitAccountRequest"
                }
              }
            },
            "application/*+json": {
              "schema": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/SplitAccountRequest"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The terminal, with its new split.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TerminalDetail"
                }
              }
            }
          },
          "400": {
            "description": "An IBAN was invalid, repeated, or the shares did not total a whole.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/terminals/{uuid}/wage": {
      "put": {
        "tags": [
          "Terminals"
        ],
        "summary": "Sets what this terminal is charged, and who pays it.",
        "description": "Administrators only. Commission is what the platform charges, so a\nmerchant editing it would be setting their own price — which is why it is\nnot part of the terminal update above.\n\n**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — none in particular; any authenticated caller with the rights below may call it.\n\n**Role** — restricted to `admin`.\n\n**Person only** — this grants or withdraws authority, so an integration calling it is refused with `403` and the code `client.person_required`. It must be done by the account's owner.",
        "operationId": "TerminalsSetWage",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/WageConfigurationRequest"
                  }
                ],
                "description": "What is needed to price a terminal."
              }
            },
            "text/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/WageConfigurationRequest"
                  }
                ],
                "description": "What is needed to price a terminal."
              }
            },
            "application/*+json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/WageConfigurationRequest"
                  }
                ],
                "description": "What is needed to price a terminal."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The terminal, with its new pricing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TerminalDetail"
                }
              }
            }
          },
          "400": {
            "description": "The commission described cannot be charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/terminals/{uuid}/status": {
      "post": {
        "tags": [
          "Terminals"
        ],
        "summary": "Approves, suspends or otherwise moves a terminal's status.",
        "description": "Administrators only. A merchant asks for a terminal; whether it may take\nmoney is somebody else's decision, and it is recorded with a reason.\n\n**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — none in particular; any authenticated caller with the rights below may call it.\n\n**Role** — restricted to `admin`.\n\n**Person only** — this grants or withdraws authority, so an integration calling it is refused with `403` and the code `client.person_required`. It must be done by the account's owner.",
        "operationId": "TerminalsChangeStatus",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/ChangeTerminalStatusBody"
                  }
                ],
                "description": "A decision about a terminal's status."
              }
            },
            "text/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/ChangeTerminalStatusBody"
                  }
                ],
                "description": "A decision about a terminal's status."
              }
            },
            "application/*+json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/ChangeTerminalStatusBody"
                  }
                ],
                "description": "A decision about a terminal's status."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The terminal as it now stands.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TerminalDetail"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/tickets": {
      "get": {
        "tags": [
          "Tickets"
        ],
        "summary": "Lists the caller's conversations.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — none in particular; any authenticated caller with the rights below may call it.\n\n**Person only** — this grants or withdraws authority, so an integration calling it is refused with `403` and the code `client.person_required`. It must be done by the account's owner.\n\n**Rate limit** — 120 requests per minute per integration, replenished continuously, so a burst of that size is allowed. Counted per client rather than per address, so integrations sharing a host do not spend each other's allowance. A call made by a person is not counted.",
        "operationId": "TicketsList",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "description": "Values:\n\n* `0` — `Open`\n* `1` — `Answered`\n* `2` — `Closed`",
            "schema": {
              "allOf": [
                {
                  "$ref": "#/components/schemas/TicketStatus"
                }
              ],
              "description": "Values:\n\n* `0` — `Open`\n* `1` — `Answered`\n* `2` — `Closed`"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The conversations, most recently active first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/TicketSummary"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait the number of seconds in the `Retry-After` header. The body is a plain-text Persian sentence, not a problem document: this is refused before any endpoint runs.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          },
          {
            "apiKey": [ ]
          }
        ]
      },
      "post": {
        "tags": [
          "Tickets"
        ],
        "summary": "Opens a conversation.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — none in particular; any authenticated caller with the rights below may call it.\n\n**Person only** — this grants or withdraws authority, so an integration calling it is refused with `403` and the code `client.person_required`. It must be done by the account's owner.\n\n**Rate limit** — 120 requests per minute per integration, replenished continuously, so a burst of that size is allowed. Counted per client rather than per address, so integrations sharing a host do not spend each other's allowance. A call made by a person is not counted.",
        "operationId": "TicketsOpen",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/OpenTicketBody"
                  }
                ],
                "description": "A new conversation."
              }
            },
            "text/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/OpenTicketBody"
                  }
                ],
                "description": "A new conversation."
              }
            },
            "application/*+json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/OpenTicketBody"
                  }
                ],
                "description": "A new conversation."
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The conversation.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TicketSummary"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait the number of seconds in the `Retry-After` header. The body is a plain-text Persian sentence, not a problem document: this is refused before any endpoint runs.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/tickets/{uuid}": {
      "get": {
        "tags": [
          "Tickets"
        ],
        "summary": "Reads one conversation.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — none in particular; any authenticated caller with the rights below may call it.\n\n**Person only** — this grants or withdraws authority, so an integration calling it is refused with `403` and the code `client.person_required`. It must be done by the account's owner.\n\n**Rate limit** — 120 requests per minute per integration, replenished continuously, so a burst of that size is allowed. Counted per client rather than per address, so integrations sharing a host do not spend each other's allowance. A call made by a person is not counted.",
        "operationId": "TicketsGet",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The conversation and its messages.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TicketThread"
                }
              }
            }
          },
          "404": {
            "description": "No such conversation, or not one the caller may read.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait the number of seconds in the `Retry-After` header. The body is a plain-text Persian sentence, not a problem document: this is refused before any endpoint runs.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/tickets/{uuid}/messages": {
      "post": {
        "tags": [
          "Tickets"
        ],
        "summary": "Adds a message.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — none in particular; any authenticated caller with the rights below may call it.\n\n**Person only** — this grants or withdraws authority, so an integration calling it is refused with `403` and the code `client.person_required`. It must be done by the account's owner.\n\n**Rate limit** — 120 requests per minute per integration, replenished continuously, so a burst of that size is allowed. Counted per client rather than per address, so integrations sharing a host do not spend each other's allowance. A call made by a person is not counted.",
        "operationId": "TicketsReply",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/ReplyBody"
                  }
                ],
                "description": "A message added to a conversation."
              }
            },
            "text/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/ReplyBody"
                  }
                ],
                "description": "A message added to a conversation."
              }
            },
            "application/*+json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/ReplyBody"
                  }
                ],
                "description": "A message added to a conversation."
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "The message.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TicketMessageSummary"
                }
              }
            }
          },
          "409": {
            "description": "The conversation is closed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait the number of seconds in the `Retry-After` header. The body is a plain-text Persian sentence, not a problem document: this is refused before any endpoint runs.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/tickets/{uuid}/close": {
      "post": {
        "tags": [
          "Tickets"
        ],
        "summary": "Closes a conversation.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — none in particular; any authenticated caller with the rights below may call it.\n\n**Person only** — this grants or withdraws authority, so an integration calling it is refused with `403` and the code `client.person_required`. It must be done by the account's owner.\n\n**Rate limit** — 120 requests per minute per integration, replenished continuously, so a burst of that size is allowed. Counted per client rather than per address, so integrations sharing a host do not spend each other's allowance. A call made by a person is not counted.",
        "operationId": "TicketsClose",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "It is closed."
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait the number of seconds in the `Retry-After` header. The body is a plain-text Persian sentence, not a problem document: this is refused before any endpoint runs.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/transactions/{uuid}/receipt": {
      "get": {
        "tags": [
          "Transactions"
        ],
        "summary": "The receipt for a payment, as a PDF.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — an integration needs `transactions:read`. A person signing in for themselves needs none.\n\n**Rate limit** — 120 requests per minute per integration, replenished continuously, so a burst of that size is allowed. Counted per client rather than per address, so integrations sharing a host do not spend each other's allowance. A call made by a person is not counted.",
        "operationId": "TransactionsReceipt",
        "parameters": [
          {
            "name": "uuid",
            "in": "path",
            "description": "Which payment.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The receipt."
          },
          "404": {
            "description": "No such payment, or not one the caller may read.",
            "content": {
              "application/pdf": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait the number of seconds in the `Retry-After` header. The body is a plain-text Persian sentence, not a problem document: this is refused before any endpoint runs.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [
              "transactions:read"
            ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/wallet": {
      "get": {
        "tags": [
          "Wallet"
        ],
        "summary": "Reads the caller's balance.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — an integration needs `wallet:read`. A person signing in for themselves needs none.\n\n**Rate limit** — 120 requests per minute per integration, replenished continuously, so a burst of that size is allowed. Counted per client rather than per address, so integrations sharing a host do not spend each other's allowance. A call made by a person is not counted.",
        "operationId": "WalletGet",
        "responses": {
          "200": {
            "description": "The balance, in rials.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WalletSummary"
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait the number of seconds in the `Retry-After` header. The body is a plain-text Persian sentence, not a problem document: this is refused before any endpoint runs.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [
              "wallet:read"
            ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/wallet/entries": {
      "get": {
        "tags": [
          "Wallet"
        ],
        "summary": "The movements on the caller's wallet, most recent first.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — an integration needs `wallet:read`. A person signing in for themselves needs none.\n\n**Rate limit** — 120 requests per minute per integration, replenished continuously, so a burst of that size is allowed. Counted per client rather than per address, so integrations sharing a host do not spend each other's allowance. A call made by a person is not counted.",
        "operationId": "WalletEntries",
        "parameters": [
          {
            "name": "take",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The ledger.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/WalletEntrySummary"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait the number of seconds in the `Retry-After` header. The body is a plain-text Persian sentence, not a problem document: this is refused before any endpoint runs.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [
              "wallet:read"
            ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/wallet/deposit": {
      "post": {
        "tags": [
          "Wallet"
        ],
        "summary": "Adds money to a merchant's wallet by hand.",
        "description": "Administrators only, and audited with a reason. This is the path for a\ntop-up that arrived by bank transfer rather than through the gateway, and\nit is the one place money appears in a wallet without a payment behind it.\n\n**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — none in particular; any authenticated caller with the rights below may call it.\n\n**Role** — restricted to `admin`.\n\n**Person only** — this grants or withdraws authority, so an integration calling it is refused with `403` and the code `client.person_required`. It must be done by the account's owner.\n\n**Rate limit** — 120 requests per minute per integration, replenished continuously, so a burst of that size is allowed. Counted per client rather than per address, so integrations sharing a host do not spend each other's allowance. A call made by a person is not counted.",
        "operationId": "WalletDeposit",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/DepositBody"
                  }
                ],
                "description": "A manual deposit into a merchant's wallet."
              }
            },
            "text/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/DepositBody"
                  }
                ],
                "description": "A manual deposit into a merchant's wallet."
              }
            },
            "application/*+json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/DepositBody"
                  }
                ],
                "description": "A manual deposit into a merchant's wallet."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The balance after the deposit.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WalletSummary"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait the number of seconds in the `Retry-After` header. The body is a plain-text Persian sentence, not a problem document: this is refused before any endpoint runs.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [ ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/sms": {
      "post": {
        "tags": [
          "Wallet"
        ],
        "summary": "Sends a message, charged to the caller's wallet.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — an integration needs `sms:send`. A person signing in for themselves needs none.\n\n**Rate limit** — 120 requests per minute per integration, replenished continuously, so a burst of that size is allowed. Counted per client rather than per address, so integrations sharing a host do not spend each other's allowance. A call made by a person is not counted.",
        "operationId": "WalletSend",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/SendSmsBody"
                  }
                ],
                "description": "A message to send."
              }
            },
            "text/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/SendSmsBody"
                  }
                ],
                "description": "A message to send."
              }
            },
            "application/*+json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/SendSmsBody"
                  }
                ],
                "description": "A message to send."
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "The provider accepted it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SentMessage"
                }
              }
            }
          },
          "400": {
            "description": "The number was invalid, or the wallet could not pay.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait the number of seconds in the `Retry-After` header. The body is a plain-text Persian sentence, not a problem document: this is refused before any endpoint runs.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [
              "sms:send"
            ]
          },
          {
            "apiKey": [ ]
          }
        ]
      },
      "get": {
        "tags": [
          "Wallet"
        ],
        "summary": "The messages the caller has sent.",
        "description": "**Authentication** — an access token (`Authorization: Bearer …`) or an integration's key (`X-Api-Key: …`).\n\n**Scope** — an integration needs `wallet:read`. A person signing in for themselves needs none.\n\n**Rate limit** — 120 requests per minute per integration, replenished continuously, so a burst of that size is allowed. Counted per client rather than per address, so integrations sharing a host do not spend each other's allowance. A call made by a person is not counted.",
        "operationId": "WalletMessages",
        "parameters": [
          {
            "name": "take",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The messages, with what each cost.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/SentMessage"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No credential was sent, or the token or key was not accepted. Re-authenticate once; do not retry in a loop.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Authenticated, but not permitted: a missing scope, a missing role, or an integration calling something only a person may do. Retrying changes nothing — the grant has to change.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait the number of seconds in the `Retry-After` header. The body is a plain-text Persian sentence, not a problem document: this is refused before any endpoint runs.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": [
              "wallet:read"
            ]
          },
          {
            "apiKey": [ ]
          }
        ]
      }
    },
    "/api/v1/sms/estimate": {
      "post": {
        "tags": [
          "Wallet"
        ],
        "summary": "Works out what a message would cost before sending it.",
        "description": "So a merchant composing a message sees the segment count change as they\ntype, rather than discovering after the fact that one Persian word turned\none segment into two.\n\n**Authentication** — none. Open to anyone.\n\n**Rate limit** — 120 requests per minute per integration, replenished continuously, so a burst of that size is allowed. Counted per client rather than per address, so integrations sharing a host do not spend each other's allowance. A call made by a person is not counted.",
        "operationId": "WalletEstimate",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/EstimateBody"
                  }
                ],
                "description": "Text to price."
              }
            },
            "text/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/EstimateBody"
                  }
                ],
                "description": "Text to price."
              }
            },
            "application/*+json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/EstimateBody"
                  }
                ],
                "description": "Text to price."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The segment count and encoding.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SmsCount"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests. Wait the number of seconds in the `Retry-After` header. The body is a plain-text Persian sentence, not a problem document: this is refused before any endpoint runs.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "AbuseReportDetail": {
        "type": "object",
        "properties": {
          "summary": {
            "allOf": [
              {
                "$ref": "#/components/schemas/AbuseReportSummary"
              }
            ],
            "description": "The list view of it."
          },
          "body": {
            "type": "string",
            "description": "What happened, in the complainant's words."
          },
          "reviewNote": {
            "type": "string",
            "description": "What the operator found.",
            "nullable": true
          },
          "reviewedAt": {
            "type": "string",
            "description": "When an operator last acted.",
            "format": "date-time",
            "nullable": true
          },
          "reviewedBy": {
            "type": "string",
            "description": "Which operator last acted.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "A complaint and what has been done about it."
      },
      "AbuseReportStatus": {
        "enum": [
          0,
          1,
          2
        ],
        "type": "integer",
        "description": "Where a complaint sent from the public site stands.\n\nValues:\n\n* `0` — `Received`\n* `1` — `Reviewing`\n* `2` — `Closed`",
        "format": "int32"
      },
      "AbuseReportSummary": {
        "type": "object",
        "properties": {
          "uuid": {
            "type": "string",
            "description": "Public identifier.",
            "format": "uuid"
          },
          "subject": {
            "type": "string",
            "description": "What it is about."
          },
          "reporterName": {
            "type": "string",
            "description": "Who is complaining."
          },
          "reporterMobile": {
            "type": "string",
            "description": "How to reach them."
          },
          "status": {
            "allOf": [
              {
                "$ref": "#/components/schemas/AbuseReportStatus"
              }
            ],
            "description": "Where a complaint sent from the public site stands.\n\nValues:\n\n* `0` — `Received`\n* `1` — `Reviewing`\n* `2` — `Closed`"
          },
          "submittedAt": {
            "type": "string",
            "description": "When it arrived.",
            "format": "date-time"
          }
        },
        "additionalProperties": false,
        "description": "A complaint, as a list shows it."
      },
      "ApiClientSummary": {
        "type": "object",
        "properties": {
          "uuid": {
            "type": "string",
            "description": "Public identifier.",
            "format": "uuid"
          },
          "clientId": {
            "type": "string",
            "description": "The identifier the integration authenticates with."
          },
          "name": {
            "type": "string",
            "description": "What it is."
          },
          "description": {
            "type": "string",
            "description": "What it is for.",
            "nullable": true
          },
          "ownerUserId": {
            "type": "integer",
            "description": "Whose data it acts on.",
            "format": "int64",
            "nullable": true
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "What it may do."
          },
          "isActive": {
            "type": "boolean",
            "description": "Whether it may currently authenticate."
          },
          "rateLimitTier": {
            "type": "string",
            "description": "Which tier of limits applies."
          },
          "ipAllowList": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Addresses it may call from."
          },
          "lastUsedAt": {
            "type": "string",
            "description": "When it last authenticated.",
            "format": "date-time",
            "nullable": true
          },
          "createdAt": {
            "type": "string",
            "description": "When it was created.",
            "format": "date-time"
          },
          "activeSecretCount": {
            "type": "integer",
            "description": "How many secrets currently work.",
            "format": "int32"
          }
        },
        "additionalProperties": false,
        "description": "An integration, as shown in a panel or returned by the API."
      },
      "ApiUsageDay": {
        "type": "object",
        "properties": {
          "day": {
            "type": "string",
            "description": "The day.",
            "format": "date"
          },
          "calls": {
            "type": "integer",
            "description": "How many calls.",
            "format": "int64"
          },
          "errors": {
            "type": "integer",
            "description": "How many failed, whichever way.",
            "format": "int64"
          }
        },
        "additionalProperties": false,
        "description": "One day's calls."
      },
      "ApiUsageEndpoint": {
        "type": "object",
        "properties": {
          "routeTemplate": {
            "type": "string",
            "description": "The endpoint."
          },
          "calls": {
            "type": "integer",
            "description": "How many calls.",
            "format": "int64"
          },
          "errors": {
            "type": "integer",
            "description": "How many failed.",
            "format": "int64"
          },
          "averageMs": {
            "type": "number",
            "description": "Mean duration.",
            "format": "double"
          },
          "p95Ms": {
            "type": "number",
            "description": "The duration 95% of calls came in under — the number that describes what a\nslow call feels like, which a mean hides.",
            "format": "double"
          }
        },
        "additionalProperties": false,
        "description": "One endpoint's calls over the period."
      },
      "ApiUsageReport": {
        "type": "object",
        "properties": {
          "from": {
            "type": "string",
            "description": "The first day included.",
            "format": "date"
          },
          "to": {
            "type": "string",
            "description": "The last day included.",
            "format": "date"
          },
          "totalCalls": {
            "type": "integer",
            "description": "How many calls were made.",
            "format": "int64"
          },
          "successCalls": {
            "type": "integer",
            "description": "How many returned a 2xx.",
            "format": "int64"
          },
          "clientErrorCalls": {
            "type": "integer",
            "description": "How many returned a 4xx.",
            "format": "int64"
          },
          "serverErrorCalls": {
            "type": "integer",
            "description": "How many returned a 5xx.",
            "format": "int64"
          },
          "rateLimitedCalls": {
            "type": "integer",
            "description": "How many were refused by the rate limiter.",
            "format": "int64"
          },
          "days": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiUsageDay"
            },
            "description": "Calls per day, for the chart."
          },
          "endpoints": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiUsageEndpoint"
            },
            "description": "The endpoints called, busiest first."
          }
        },
        "additionalProperties": false,
        "description": "What the integrations did over a period."
      },
      "AttachmentSummary": {
        "type": "object",
        "properties": {
          "uuid": {
            "type": "string",
            "description": "Public identifier.",
            "format": "uuid"
          },
          "type": {
            "allOf": [
              {
                "$ref": "#/components/schemas/AttachmentType"
              }
            ],
            "description": "What a stored document is.\n\nValues:\n\n* `0` — `Other`\n* `1` — `Avatar`\n* `2` — `NationalCard`\n* `3` — `BirthCertificate`\n* `4` — `IncorporationNotice`\n* `5` — `ChangesNotice`\n* `6` — `ArticlesOfAssociation`\n* `7` — `TradeLicence`\n* `8` — `Ticket`\n* `9` — `Product`\n* `10` — `BusinessLogo`"
          },
          "fileName": {
            "type": "string",
            "description": "The name as uploaded."
          },
          "contentType": {
            "type": "string",
            "description": "Its media type."
          },
          "sizeBytes": {
            "type": "integer",
            "description": "How large it is.",
            "format": "int64"
          },
          "description": {
            "type": "string",
            "description": "What it is for.",
            "nullable": true
          },
          "uploadedAt": {
            "type": "string",
            "description": "When it arrived.",
            "format": "date-time"
          },
          "terminalUuid": {
            "type": "string",
            "description": "Which terminal it belongs to, or null where it describes the account holder\nrather than one of their businesses.",
            "format": "uuid",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "A stored document."
      },
      "AttachmentType": {
        "enum": [
          0,
          1,
          2,
          3,
          4,
          5,
          6,
          7,
          8,
          9,
          10
        ],
        "type": "integer",
        "description": "What a stored document is.\n\nValues:\n\n* `0` — `Other`\n* `1` — `Avatar`\n* `2` — `NationalCard`\n* `3` — `BirthCertificate`\n* `4` — `IncorporationNotice`\n* `5` — `ChangesNotice`\n* `6` — `ArticlesOfAssociation`\n* `7` — `TradeLicence`\n* `8` — `Ticket`\n* `9` — `Product`\n* `10` — `BusinessLogo`",
        "format": "int32"
      },
      "BulkLinkResult": {
        "type": "object",
        "properties": {
          "created": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LinkSummary"
            },
            "description": "The links that exist now."
          },
          "skipped": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SkippedContact"
            },
            "description": "Contacts that produced nothing, and why — a missing mobile number, usually.\nReported rather than swallowed, so a merchant knows who will not receive it."
          }
        },
        "additionalProperties": false,
        "description": "What a bulk creation produced."
      },
      "ChangePasswordRequest": {
        "type": "object",
        "properties": {
          "currentPassword": {
            "type": "string",
            "description": "The password in force."
          },
          "newPassword": {
            "type": "string",
            "description": "What to replace it with."
          }
        },
        "additionalProperties": false,
        "description": "A password change by someone who knows the current one."
      },
      "ChangeTerminalStatusBody": {
        "type": "object",
        "properties": {
          "status": {
            "allOf": [
              {
                "$ref": "#/components/schemas/TerminalStatus"
              }
            ],
            "description": "How far a terminal has progressed through onboarding.\n\nValues:\n\n* `0` — `Suspended`\n* `1` — `Pending`\n* `2` — `Sent`\n* `3` — `Complete`"
          },
          "note": {
            "type": "string",
            "description": "Why. Shown to the merchant when it is a refusal.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "A decision about a terminal's status."
      },
      "ClientCredentialsRequest": {
        "type": "object",
        "properties": {
          "clientId": {
            "type": "string",
            "description": "The public client identifier."
          },
          "clientSecret": {
            "type": "string",
            "description": "The secret."
          },
          "grantType": {
            "type": "string",
            "description": "Always `client_credentials`."
          }
        },
        "additionalProperties": false,
        "description": "An OAuth2 client-credentials request."
      },
      "ClientTokenResponse": {
        "type": "object",
        "properties": {
          "accessToken": {
            "type": "string",
            "description": "The bearer token."
          },
          "tokenType": {
            "type": "string",
            "description": "Always `Bearer`."
          },
          "expiresIn": {
            "type": "integer",
            "description": "Seconds until it stops working.",
            "format": "int32"
          },
          "scope": {
            "type": "string",
            "description": "The granted scopes, space-delimited as the standard has it."
          }
        },
        "additionalProperties": false,
        "description": "An issued access token, in the shape OAuth2 clients expect."
      },
      "ContactRequest": {
        "type": "object",
        "properties": {
          "firstName": {
            "type": "string",
            "description": "Given name."
          },
          "lastName": {
            "type": "string",
            "description": "Family name."
          },
          "mobile": {
            "type": "string",
            "description": "Their number, in any spelling."
          },
          "email": {
            "type": "string",
            "description": "Optional.",
            "nullable": true
          },
          "groupUuid": {
            "type": "string",
            "description": "Which group they belong to, if any.",
            "format": "uuid",
            "nullable": true
          },
          "externalReference": {
            "type": "string",
            "description": "The merchant's own identifier for them.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "What is needed to save a contact."
      },
      "ContactSummary": {
        "type": "object",
        "properties": {
          "uuid": {
            "type": "string",
            "description": "Public identifier.",
            "format": "uuid"
          },
          "fullName": {
            "type": "string",
            "description": "How they are addressed."
          },
          "mobile": {
            "type": "string",
            "description": "Their number, normalised."
          },
          "email": {
            "type": "string",
            "description": "Their address, if known.",
            "nullable": true
          },
          "groupName": {
            "type": "string",
            "description": "Which group they are in.",
            "nullable": true
          },
          "externalReference": {
            "type": "string",
            "description": "The merchant's own identifier for them.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "A contact."
      },
      "CreateApiClientBody": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "What the integration is."
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "What it may do."
          },
          "description": {
            "type": "string",
            "description": "What it is for.",
            "nullable": true
          },
          "ownerUserId": {
            "type": "integer",
            "description": "Whose data it acts on. Administrators only.",
            "format": "int64",
            "nullable": true
          },
          "rateLimitTier": {
            "type": "string",
            "description": "Which tier of limits applies.",
            "nullable": true
          },
          "ipAllowList": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Addresses it may call from.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "What creating an integration needs."
      },
      "CreateGroupBody": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "What to call it."
          },
          "description": {
            "type": "string",
            "description": "What it is for.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "A new group."
      },
      "CreateLinkBody": {
        "type": "object",
        "properties": {
          "terminalUuid": {
            "type": "string",
            "description": "Which terminal takes the payment.",
            "format": "uuid"
          },
          "amount": {
            "type": "integer",
            "description": "How much, in minor units. Zero asks the payer to choose, which the terminal\nmust allow.",
            "format": "int64"
          },
          "currency": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Currency"
              }
            ],
            "description": "The unit an amount is expressed in.\n\nValues:\n\n* `1` — `Irr`\n* `2` — `Irt`"
          },
          "payerName": {
            "type": "string",
            "description": "Who is being asked to pay.",
            "nullable": true
          },
          "payerMobile": {
            "type": "string",
            "description": "Where to send it, in any spelling.",
            "nullable": true
          },
          "description": {
            "type": "string",
            "description": "What the payment is for.",
            "nullable": true
          },
          "expiresAt": {
            "type": "string",
            "description": "When it stops being payable.",
            "format": "date-time",
            "nullable": true
          },
          "sendSms": {
            "type": "boolean",
            "description": "Whether to text the link to the payer."
          }
        },
        "additionalProperties": false,
        "description": "What creating a link needs."
      },
      "CreateScheduleBody": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "What the merchant calls it."
          },
          "terminalUuid": {
            "type": "string",
            "description": "Which terminal takes the payments.",
            "format": "uuid"
          },
          "amount": {
            "type": "integer",
            "description": "How much, in minor units.",
            "format": "int64"
          },
          "step": {
            "allOf": [
              {
                "$ref": "#/components/schemas/ScheduleStep"
              }
            ],
            "description": "The interval between runs of a recurring link schedule.\n\nValues:\n\n* `0` — `Fixed`\n* `1` — `Day`\n* `2` — `Week`\n* `3` — `Month`\n* `4` — `Season`\n* `5` — `Year`"
          },
          "startsOn": {
            "type": "string",
            "description": "The first date, Jalali or ISO."
          },
          "currency": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Currency"
              }
            ],
            "description": "The unit an amount is expressed in.\n\nValues:\n\n* `1` — `Irr`\n* `2` — `Irt`"
          },
          "endsOn": {
            "type": "string",
            "description": "When to stop.",
            "nullable": true
          },
          "contactUuid": {
            "type": "string",
            "description": "One customer to bill.",
            "format": "uuid",
            "nullable": true
          },
          "groupUuid": {
            "type": "string",
            "description": "Or a whole group.",
            "format": "uuid",
            "nullable": true
          },
          "description": {
            "type": "string",
            "description": "What the payment is for.",
            "nullable": true
          },
          "sendSms": {
            "type": "boolean",
            "description": "Whether each link is texted when it is made."
          }
        },
        "additionalProperties": false,
        "description": "What setting up a recurring bill needs."
      },
      "Currency": {
        "enum": [
          1,
          2
        ],
        "type": "integer",
        "description": "The unit an amount is expressed in.\n\nValues:\n\n* `1` — `Irr`\n* `2` — `Irt`",
        "format": "int32"
      },
      "DailyTotal": {
        "type": "object",
        "properties": {
          "day": {
            "type": "string",
            "description": "The day, in Tehran.",
            "format": "date"
          },
          "amountRials": {
            "type": "integer",
            "description": "What arrived that day.",
            "format": "int64"
          },
          "successfulCount": {
            "type": "integer",
            "description": "How many payments.",
            "format": "int64"
          },
          "failedCount": {
            "type": "integer",
            "description": "How many failed attempts.",
            "format": "int64"
          }
        },
        "additionalProperties": false,
        "description": "One day's takings."
      },
      "DepositBody": {
        "type": "object",
        "properties": {
          "userId": {
            "type": "integer",
            "description": "Whose wallet.",
            "format": "int64"
          },
          "amountRials": {
            "type": "integer",
            "description": "How much to add.",
            "format": "int64"
          },
          "reason": {
            "type": "string",
            "description": "Why — recorded in the ledger and the audit trail."
          },
          "idempotencyKey": {
            "type": "string",
            "description": "Makes a retried deposit safe.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "A manual deposit into a merchant's wallet."
      },
      "EstimateBody": {
        "type": "object",
        "properties": {
          "body": {
            "type": "string",
            "description": "The message body."
          }
        },
        "additionalProperties": false,
        "description": "Text to price."
      },
      "ForgotPasswordRequest": {
        "type": "object",
        "properties": {
          "mobile": {
            "type": "string",
            "description": "The number the account signs in with."
          }
        },
        "additionalProperties": false,
        "description": "The start of a password reset."
      },
      "GatewayPerformance": {
        "type": "object",
        "properties": {
          "gateway": {
            "type": "string",
            "description": "Which bank."
          },
          "amountRials": {
            "type": "integer",
            "description": "What arrived through it.",
            "format": "int64"
          },
          "successfulCount": {
            "type": "integer",
            "description": "How many payments succeeded.",
            "format": "int64"
          },
          "failedCount": {
            "type": "integer",
            "description": "How many failed.",
            "format": "int64"
          },
          "successRate": {
            "type": "number",
            "description": "The share that succeeded, from 0 to 1.",
            "format": "double"
          }
        },
        "additionalProperties": false,
        "description": "How a bank performed."
      },
      "Gender": {
        "enum": [
          0,
          1
        ],
        "type": "integer",
        "description": "Gender, as required by the acquirer's registration forms.\n\nValues:\n\n* `0` — `Male`\n* `1` — `Female`",
        "format": "int32"
      },
      "GroupSummary": {
        "type": "object",
        "properties": {
          "uuid": {
            "type": "string",
            "description": "Public identifier.",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "description": "What it is called."
          },
          "description": {
            "type": "string",
            "description": "What it is for.",
            "nullable": true
          },
          "contactCount": {
            "type": "integer",
            "description": "How many are in it.",
            "format": "int32"
          }
        },
        "additionalProperties": false,
        "description": "A group, with its size."
      },
      "GroupedTotal": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string",
            "description": "What it is grouped by."
          },
          "label": {
            "type": "string",
            "description": "How to name it."
          },
          "amountRials": {
            "type": "integer",
            "description": "What arrived.",
            "format": "int64"
          },
          "count": {
            "type": "integer",
            "description": "How many payments.",
            "format": "int64"
          }
        },
        "additionalProperties": false,
        "description": "A total under some heading."
      },
      "ImportReport": {
        "type": "object",
        "properties": {
          "imported": {
            "type": "integer",
            "description": "How many contacts were added.",
            "format": "int32"
          },
          "updated": {
            "type": "integer",
            "description": "How many existing contacts were amended.",
            "format": "int32"
          },
          "skipped": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SkippedRow"
            },
            "description": "Which rows were not taken, and why."
          }
        },
        "additionalProperties": false,
        "description": "What an import did."
      },
      "IssueSecretBody": {
        "type": "object",
        "properties": {
          "label": {
            "type": "string",
            "description": "What the secret is for.",
            "nullable": true
          },
          "expiresAt": {
            "type": "string",
            "description": "When it should stop working.",
            "format": "date-time",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "What issuing a secret needs."
      },
      "IssuedSecret": {
        "type": "object",
        "properties": {
          "client": {
            "allOf": [
              {
                "$ref": "#/components/schemas/ApiClientSummary"
              }
            ],
            "description": "The integration the secret belongs to."
          },
          "secretUuid": {
            "type": "string",
            "description": "Public identifier of the secret itself.",
            "format": "uuid"
          },
          "secret": {
            "type": "string",
            "description": "The plaintext. Shown once."
          },
          "expiresAt": {
            "type": "string",
            "description": "When it stops being accepted, if it does.",
            "format": "date-time",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "A newly issued secret, with its plaintext."
      },
      "LinkFunnel": {
        "type": "object",
        "properties": {
          "created": {
            "type": "integer",
            "description": "How many were created.",
            "format": "int64"
          },
          "viewed": {
            "type": "integer",
            "description": "How many a payer opened.",
            "format": "int64"
          },
          "paid": {
            "type": "integer",
            "description": "How many were paid.",
            "format": "int64"
          },
          "expired": {
            "type": "integer",
            "description": "How many ran out of time.",
            "format": "int64"
          }
        },
        "additionalProperties": false,
        "description": "How far links got."
      },
      "LinkStatus": {
        "enum": [
          0,
          1,
          2,
          3,
          4,
          5
        ],
        "type": "integer",
        "description": "The life cycle of a payment link.\n\nValues:\n\n* `0` — `Pending`\n* `1` — `Viewed`\n* `2` — `InProgress`\n* `3` — `Succeeded`\n* `4` — `Failed`\n* `5` — `Expired`",
        "format": "int32"
      },
      "LinkStatusResponse": {
        "type": "object",
        "properties": {
          "uuid": {
            "type": "string",
            "description": "Public identifier.",
            "format": "uuid"
          },
          "shortId": {
            "type": "string",
            "description": "The short code."
          },
          "status": {
            "allOf": [
              {
                "$ref": "#/components/schemas/LinkStatus"
              }
            ],
            "description": "The life cycle of a payment link.\n\nValues:\n\n* `0` — `Pending`\n* `1` — `Viewed`\n* `2` — `InProgress`\n* `3` — `Succeeded`\n* `4` — `Failed`\n* `5` — `Expired`"
          },
          "isPaid": {
            "type": "boolean",
            "description": "Whether the money has arrived."
          },
          "expiresAt": {
            "type": "string",
            "description": "When it stops being payable.",
            "format": "date-time"
          }
        },
        "additionalProperties": false,
        "description": "A link's state, for polling."
      },
      "LinkSummary": {
        "type": "object",
        "properties": {
          "uuid": {
            "type": "string",
            "description": "Public identifier.",
            "format": "uuid"
          },
          "shortId": {
            "type": "string",
            "description": "The short code in the URL sent to the payer."
          },
          "url": {
            "type": "string",
            "description": "The address to send."
          },
          "amount": {
            "type": "integer",
            "description": "How much, in minor units.",
            "format": "int64"
          },
          "currency": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Currency"
              }
            ],
            "description": "The unit an amount is expressed in.\n\nValues:\n\n* `1` — `Irr`\n* `2` — `Irt`"
          },
          "status": {
            "allOf": [
              {
                "$ref": "#/components/schemas/LinkStatus"
              }
            ],
            "description": "The life cycle of a payment link.\n\nValues:\n\n* `0` — `Pending`\n* `1` — `Viewed`\n* `2` — `InProgress`\n* `3` — `Succeeded`\n* `4` — `Failed`\n* `5` — `Expired`"
          },
          "payerName": {
            "type": "string",
            "description": "Who was asked.",
            "nullable": true
          },
          "payerMobile": {
            "type": "string",
            "description": "Where it was sent. Masked in audit records, not here.",
            "nullable": true
          },
          "description": {
            "type": "string",
            "description": "What it is for.",
            "nullable": true
          },
          "expiresAt": {
            "type": "string",
            "description": "When it stops being payable.",
            "format": "date-time"
          },
          "createdAt": {
            "type": "string",
            "description": "When it was created.",
            "format": "date-time"
          },
          "terminalTitle": {
            "type": "string",
            "description": "Which terminal takes the payment.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "A link, as the merchant sees it."
      },
      "MerchantDossier": {
        "type": "object",
        "properties": {
          "userUuid": {
            "type": "string",
            "description": "Whose dossier.",
            "format": "uuid"
          },
          "mobile": {
            "type": "string",
            "description": "What they sign in with."
          },
          "displayName": {
            "type": "string",
            "description": "How they are named.",
            "nullable": true
          },
          "status": {
            "allOf": [
              {
                "$ref": "#/components/schemas/UserStatus"
              }
            ],
            "description": "Where a user account stands.\n\nValues:\n\n* `0` — `Pending`\n* `1` — `Active`\n* `2` — `Blocked`\n* `3` — `Suspended`"
          },
          "acquirerMerchantId": {
            "type": "string",
            "description": "The identifier the acquirer issued, once it has.",
            "nullable": true
          },
          "signatories": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SignatorySummary"
            },
            "description": "Everyone authorised to sign."
          },
          "documents": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AttachmentSummary"
            },
            "description": "The scans the merchant uploaded. The dossier is transcribed from these, so a\nreviewer needs both on one screen."
          }
        },
        "additionalProperties": false,
        "description": "A merchant's dossier, with what a reviewer needs beside it."
      },
      "MerchantDossierRequest": {
        "type": "object",
        "properties": {
          "acquirerMerchantId": {
            "type": "string",
            "description": "The identifier the acquirer issues once it approves. Administrators fill it\nin; a merchant has no way to know it.",
            "nullable": true
          },
          "signatories": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SignatoryRequest"
            },
            "description": "Everyone authorised to sign, in the order they appear on the form."
          }
        },
        "additionalProperties": false,
        "description": "What is needed to save a dossier."
      },
      "OpenTicketBody": {
        "type": "object",
        "properties": {
          "subject": {
            "type": "string",
            "description": "What it is about."
          },
          "body": {
            "type": "string",
            "description": "The first message."
          }
        },
        "additionalProperties": false,
        "description": "A new conversation."
      },
      "PriceBand": {
        "type": "object",
        "properties": {
          "floorRials": {
            "type": "integer",
            "description": "The lowest amount this band covers.",
            "format": "int64"
          },
          "ceilingRials": {
            "type": "integer",
            "description": "The highest amount this band covers.",
            "format": "int64"
          },
          "commissionRials": {
            "type": "integer",
            "description": "What is charged within it.",
            "format": "int64"
          }
        },
        "additionalProperties": false,
        "description": "One row of the published price list."
      },
      "ProblemDetails": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "nullable": true
          },
          "title": {
            "type": "string",
            "nullable": true
          },
          "status": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "detail": {
            "type": "string",
            "nullable": true
          },
          "instance": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": { }
      },
      "ProductPhoto": {
        "type": "object",
        "properties": {
          "uuid": {
            "type": "string",
            "description": "Public identifier, and what the public URL names.",
            "format": "uuid"
          },
          "fileName": {
            "type": "string",
            "description": "The name as uploaded."
          },
          "contentType": {
            "type": "string",
            "description": "Its media type."
          },
          "sizeBytes": {
            "type": "integer",
            "description": "How large it is.",
            "format": "int64"
          }
        },
        "additionalProperties": false,
        "description": "A product photograph."
      },
      "ProductRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "What it is called."
          },
          "amount": {
            "type": "integer",
            "description": "What it costs, in minor units of Currency.",
            "format": "int64"
          },
          "currency": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Currency"
              }
            ],
            "description": "The unit an amount is expressed in.\n\nValues:\n\n* `1` — `Irr`\n* `2` — `Irt`"
          },
          "description": {
            "type": "string",
            "description": "What it is.",
            "nullable": true
          },
          "isActive": {
            "type": "boolean",
            "description": "Whether it can currently be bought."
          }
        },
        "additionalProperties": false,
        "description": "What is needed to save a product."
      },
      "ProductSummary": {
        "type": "object",
        "properties": {
          "uuid": {
            "type": "string",
            "description": "Public identifier.",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "description": "What it is called."
          },
          "description": {
            "type": "string",
            "description": "What it is.",
            "nullable": true
          },
          "amount": {
            "type": "integer",
            "description": "What it costs, in minor units.",
            "format": "int64"
          },
          "currency": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Currency"
              }
            ],
            "description": "The unit an amount is expressed in.\n\nValues:\n\n* `1` — `Irr`\n* `2` — `Irt`"
          },
          "isActive": {
            "type": "boolean",
            "description": "Whether it can currently be bought."
          },
          "photos": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ProductPhoto"
            },
            "description": "Its photographs, oldest first."
          },
          "createdAt": {
            "type": "string",
            "description": "When it was added.",
            "format": "date-time"
          }
        },
        "additionalProperties": false,
        "description": "A product."
      },
      "RefreshRequest": {
        "type": "object",
        "properties": {
          "refreshToken": {
            "type": "string",
            "description": "The value issued alongside the last access token."
          }
        },
        "additionalProperties": false,
        "description": "A refresh token to exchange or revoke."
      },
      "RegisterRequest": {
        "type": "object",
        "properties": {
          "mobile": {
            "type": "string",
            "description": "The mobile number, which is also the username."
          },
          "password": {
            "type": "string",
            "description": "The chosen password."
          },
          "displayName": {
            "type": "string",
            "description": "The name shown in the panel and on receipts.",
            "nullable": true
          },
          "email": {
            "type": "string",
            "description": "Optional. Used for notices, never for signing in.",
            "nullable": true
          },
          "vendorId": {
            "type": "integer",
            "description": "The reseller the account belongs to, where one applies.",
            "format": "int64",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "What is needed to open an account."
      },
      "ReplyBody": {
        "type": "object",
        "properties": {
          "body": {
            "type": "string",
            "description": "What to say."
          }
        },
        "additionalProperties": false,
        "description": "A message added to a conversation."
      },
      "ReportSummary": {
        "type": "object",
        "properties": {
          "successfulAmountRials": {
            "type": "integer",
            "description": "What arrived.",
            "format": "int64"
          },
          "successfulCount": {
            "type": "integer",
            "description": "How many payments arrived.",
            "format": "int64"
          },
          "failedCount": {
            "type": "integer",
            "description": "How many did not.",
            "format": "int64"
          },
          "wageAmountRials": {
            "type": "integer",
            "description": "What the platform took in commission.",
            "format": "int64"
          },
          "netAmountRials": {
            "type": "integer",
            "description": "What the merchant keeps.",
            "format": "int64"
          },
          "averageAmountRials": {
            "type": "integer",
            "description": "The mean successful payment.",
            "format": "int64"
          },
          "successRate": {
            "type": "number",
            "description": "The share of attempts that succeeded, from 0 to 1.",
            "format": "double"
          }
        },
        "additionalProperties": false,
        "description": "The headline figures."
      },
      "ResetPasswordRequest": {
        "type": "object",
        "properties": {
          "mobile": {
            "type": "string",
            "description": "The number the account signs in with."
          },
          "token": {
            "type": "string",
            "description": "The token that was sent."
          },
          "newPassword": {
            "type": "string",
            "description": "The new password."
          }
        },
        "additionalProperties": false,
        "description": "The completion of a password reset."
      },
      "ScheduleStatus": {
        "enum": [
          0,
          1
        ],
        "type": "integer",
        "description": "Whether a schedule is currently producing links.\n\nValues:\n\n* `0` — `Enabled`\n* `1` — `Disabled`",
        "format": "int32"
      },
      "ScheduleStep": {
        "enum": [
          0,
          1,
          2,
          3,
          4,
          5
        ],
        "type": "integer",
        "description": "The interval between runs of a recurring link schedule.\n\nValues:\n\n* `0` — `Fixed`\n* `1` — `Day`\n* `2` — `Week`\n* `3` — `Month`\n* `4` — `Season`\n* `5` — `Year`",
        "format": "int32"
      },
      "ScheduleSummary": {
        "type": "object",
        "properties": {
          "uuid": {
            "type": "string",
            "description": "Public identifier.",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "description": "What it is called.",
            "nullable": true
          },
          "step": {
            "allOf": [
              {
                "$ref": "#/components/schemas/ScheduleStep"
              }
            ],
            "description": "The interval between runs of a recurring link schedule.\n\nValues:\n\n* `0` — `Fixed`\n* `1` — `Day`\n* `2` — `Week`\n* `3` — `Month`\n* `4` — `Season`\n* `5` — `Year`"
          },
          "status": {
            "allOf": [
              {
                "$ref": "#/components/schemas/ScheduleStatus"
              }
            ],
            "description": "Whether a schedule is currently producing links.\n\nValues:\n\n* `0` — `Enabled`\n* `1` — `Disabled`"
          },
          "amountRials": {
            "type": "integer",
            "description": "How much each link is for.",
            "format": "int64"
          },
          "recipient": {
            "type": "string",
            "description": "Who it bills.",
            "nullable": true
          },
          "lastRunAt": {
            "type": "string",
            "description": "When it last produced links.",
            "format": "date-time",
            "nullable": true
          },
          "nextRunAt": {
            "type": "string",
            "description": "When it next will, or null once finished.",
            "format": "date-time",
            "nullable": true
          },
          "endsAt": {
            "type": "string",
            "description": "When it stops.",
            "format": "date-time",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "A recurring bill."
      },
      "SendSmsBody": {
        "type": "object",
        "properties": {
          "destination": {
            "type": "string",
            "description": "Who receives it, in any spelling."
          },
          "body": {
            "type": "string",
            "description": "What to say."
          }
        },
        "additionalProperties": false,
        "description": "A message to send."
      },
      "SentMessage": {
        "type": "object",
        "properties": {
          "uuid": {
            "type": "string",
            "description": "Public identifier.",
            "format": "uuid"
          },
          "destination": {
            "type": "string",
            "description": "Who it went to."
          },
          "segments": {
            "type": "integer",
            "description": "How many messages it cost.",
            "format": "int32"
          },
          "costRials": {
            "type": "integer",
            "description": "What the merchant was charged.",
            "format": "int64"
          },
          "status": {
            "type": "string",
            "description": "Where it got to."
          },
          "sentAt": {
            "type": "string",
            "description": "When the provider accepted it.",
            "format": "date-time",
            "nullable": true
          },
          "error": {
            "type": "string",
            "description": "Why it failed, where it did.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "A message this system sent, or tried to."
      },
      "SetAbuseReportStatusBody": {
        "type": "object",
        "properties": {
          "status": {
            "allOf": [
              {
                "$ref": "#/components/schemas/AbuseReportStatus"
              }
            ],
            "description": "Where a complaint sent from the public site stands.\n\nValues:\n\n* `0` — `Received`\n* `1` — `Reviewing`\n* `2` — `Closed`"
          },
          "note": {
            "type": "string",
            "description": "What the operator found.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "Where a complaint now stands."
      },
      "SetScopesBody": {
        "type": "object",
        "properties": {
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "What the integration may do from now on."
          }
        },
        "additionalProperties": false,
        "description": "A replacement set of scopes."
      },
      "SignatoryRequest": {
        "type": "object",
        "properties": {
          "firstName": {
            "type": "string",
            "description": "Given name."
          },
          "lastName": {
            "type": "string",
            "description": "Family name."
          },
          "fatherName": {
            "type": "string",
            "description": "Father's name, as the acquirer's forms require.",
            "nullable": true
          },
          "nationalCode": {
            "type": "string",
            "description": "National code, in any spelling.",
            "nullable": true
          },
          "gender": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Gender"
              }
            ],
            "description": "Gender, as required by the acquirer's registration forms.\n\nValues:\n\n* `0` — `Male`\n* `1` — `Female`"
          },
          "education": {
            "type": "string",
            "description": "Highest level of education.",
            "nullable": true
          },
          "birthDate": {
            "type": "string",
            "description": "Date of birth.",
            "format": "date",
            "nullable": true
          },
          "birthPlace": {
            "type": "string",
            "description": "Place of birth.",
            "nullable": true
          },
          "birthCertificateNumber": {
            "type": "string",
            "description": "Birth certificate number.",
            "nullable": true
          },
          "birthCertificateSerial": {
            "type": "string",
            "description": "Birth certificate serial.",
            "nullable": true
          },
          "birthCertificateIssuedOn": {
            "type": "string",
            "description": "When it was issued.",
            "format": "date",
            "nullable": true
          },
          "birthCertificateIssuedAt": {
            "type": "string",
            "description": "Where it was issued.",
            "nullable": true
          },
          "phone": {
            "type": "string",
            "description": "Landline number.",
            "nullable": true
          },
          "mobile": {
            "type": "string",
            "description": "Mobile number, in any spelling.",
            "nullable": true
          },
          "email": {
            "type": "string",
            "description": "Email address.",
            "nullable": true
          },
          "address": {
            "type": "string",
            "description": "Postal address.",
            "nullable": true
          },
          "postalCode": {
            "type": "string",
            "description": "Postal code.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "One person authorised to sign for the business."
      },
      "SignatorySummary": {
        "type": "object",
        "properties": {
          "order": {
            "type": "integer",
            "description": "Where they appear on the form. 1 is the primary signatory.",
            "format": "int32"
          },
          "firstName": {
            "type": "string",
            "description": "Given name."
          },
          "lastName": {
            "type": "string",
            "description": "Family name."
          },
          "fatherName": {
            "type": "string",
            "description": "Father's name.",
            "nullable": true
          },
          "nationalCode": {
            "type": "string",
            "description": "National code, normalised.",
            "nullable": true
          },
          "gender": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Gender"
              }
            ],
            "description": "Gender, as required by the acquirer's registration forms.\n\nValues:\n\n* `0` — `Male`\n* `1` — `Female`"
          },
          "education": {
            "type": "string",
            "description": "Highest level of education.",
            "nullable": true
          },
          "birthDate": {
            "type": "string",
            "description": "Date of birth.",
            "format": "date",
            "nullable": true
          },
          "birthPlace": {
            "type": "string",
            "description": "Place of birth.",
            "nullable": true
          },
          "birthCertificateNumber": {
            "type": "string",
            "description": "Birth certificate number.",
            "nullable": true
          },
          "birthCertificateSerial": {
            "type": "string",
            "description": "Birth certificate serial.",
            "nullable": true
          },
          "birthCertificateIssuedOn": {
            "type": "string",
            "description": "When it was issued.",
            "format": "date",
            "nullable": true
          },
          "birthCertificateIssuedAt": {
            "type": "string",
            "description": "Where it was issued.",
            "nullable": true
          },
          "phone": {
            "type": "string",
            "description": "Landline number.",
            "nullable": true
          },
          "mobile": {
            "type": "string",
            "description": "Mobile number, normalised.",
            "nullable": true
          },
          "email": {
            "type": "string",
            "description": "Email address.",
            "nullable": true
          },
          "address": {
            "type": "string",
            "description": "Postal address.",
            "nullable": true
          },
          "postalCode": {
            "type": "string",
            "description": "Postal code.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "A signatory as held."
      },
      "SkippedContact": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Who.",
            "nullable": true
          },
          "reason": {
            "type": "string",
            "description": "Why."
          }
        },
        "additionalProperties": false,
        "description": "A contact a bulk creation passed over."
      },
      "SkippedRow": {
        "type": "object",
        "properties": {
          "line": {
            "type": "integer",
            "description": "Which line of the file.",
            "format": "int32"
          },
          "value": {
            "type": "string",
            "description": "What it said, truncated."
          },
          "reason": {
            "type": "string",
            "description": "Why it was skipped."
          }
        },
        "additionalProperties": false,
        "description": "A row an import could not take."
      },
      "SmsCount": {
        "type": "object",
        "properties": {
          "encoding": {
            "allOf": [
              {
                "$ref": "#/components/schemas/SmsEncoding"
              }
            ],
            "description": "How it will be encoded.\n\nValues:\n\n* `0` — `Gsm7Bit`\n* `1` — `Gsm7BitExtended`\n* `2` — `Utf16`"
          },
          "length": {
            "type": "integer",
            "description": "How many positions it occupies.",
            "format": "int32"
          },
          "perSegment": {
            "type": "integer",
            "description": "How many positions each segment holds.",
            "format": "int32"
          },
          "remaining": {
            "type": "integer",
            "description": "How many more positions fit before the next segment.",
            "format": "int32"
          },
          "segments": {
            "type": "integer",
            "description": "How many messages will be sent, and billed.",
            "format": "int32"
          }
        },
        "additionalProperties": false,
        "description": "What a message costs to send."
      },
      "SmsEncoding": {
        "enum": [
          0,
          1,
          2
        ],
        "type": "integer",
        "description": "How a message is encoded on the wire.\n\nValues:\n\n* `0` — `Gsm7Bit`\n* `1` — `Gsm7BitExtended`\n* `2` — `Utf16`",
        "format": "int32"
      },
      "SmsUsage": {
        "type": "object",
        "properties": {
          "messages": {
            "type": "integer",
            "description": "How many were sent.",
            "format": "int64"
          },
          "segments": {
            "type": "integer",
            "description": "How many segments they came to.",
            "format": "int64"
          },
          "costRials": {
            "type": "integer",
            "description": "What they cost.",
            "format": "int64"
          },
          "delivered": {
            "type": "integer",
            "description": "How many reached a handset.",
            "format": "int64"
          },
          "failed": {
            "type": "integer",
            "description": "How many did not.",
            "format": "int64"
          }
        },
        "additionalProperties": false,
        "description": "What messages cost."
      },
      "SplitAccountRequest": {
        "type": "object",
        "properties": {
          "iban": {
            "type": "string",
            "description": "Where this share goes."
          },
          "name": {
            "type": "string",
            "description": "Whose account it is.",
            "nullable": true
          },
          "percentage": {
            "type": "number",
            "description": "A proportional share, where the split is by percentage.",
            "format": "double",
            "nullable": true
          },
          "fixedAmountRials": {
            "type": "integer",
            "description": "A fixed share, where the split is by amount.",
            "format": "int64",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "One IBAN a terminal may split settlement to."
      },
      "SplitAccountSummary": {
        "type": "object",
        "properties": {
          "uuid": {
            "type": "string",
            "description": "Public identifier.",
            "format": "uuid"
          },
          "iban": {
            "type": "string",
            "description": "Where this share goes."
          },
          "name": {
            "type": "string",
            "description": "Whose account it is.",
            "nullable": true
          },
          "percentage": {
            "type": "number",
            "description": "Its proportional share.",
            "format": "double",
            "nullable": true
          },
          "fixedAmountRials": {
            "type": "integer",
            "description": "Its fixed share.",
            "format": "int64",
            "nullable": true
          },
          "isActive": {
            "type": "boolean",
            "description": "Whether it currently receives anything."
          }
        },
        "additionalProperties": false,
        "description": "One share of a split settlement."
      },
      "SubmitAbuseReportBody": {
        "type": "object",
        "properties": {
          "subject": {
            "type": "string",
            "description": "What it is about."
          },
          "reporterName": {
            "type": "string",
            "description": "Who is complaining."
          },
          "reporterMobile": {
            "type": "string",
            "description": "How to reach them."
          },
          "body": {
            "type": "string",
            "description": "What happened."
          }
        },
        "additionalProperties": false,
        "description": "A complaint being filed."
      },
      "SuspendBody": {
        "type": "object",
        "properties": {
          "reason": {
            "type": "string",
            "description": "The explanation, recorded in the audit trail.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "Why an integration is being suspended."
      },
      "TerminalCredentials": {
        "type": "object",
        "properties": {
          "acquirerTerminalId": {
            "type": "string",
            "description": "The terminal identifier the bank assigned.",
            "nullable": true
          },
          "acquirerMerchantId": {
            "type": "string",
            "description": "The merchant identifier the bank assigned.",
            "nullable": true
          },
          "hasSigningKey": {
            "type": "boolean",
            "description": "Whether a key is held. The key itself is never returned — not to the API, not\nto the screen that sets it. An operator needs to know whether one exists, not\nwhat it is."
          },
          "complete": {
            "type": "boolean",
            "description": "Whether the terminal can sign a payment request."
          }
        },
        "additionalProperties": false,
        "description": "What the acquirer issued for a terminal."
      },
      "TerminalDetail": {
        "type": "object",
        "properties": {
          "summary": {
            "allOf": [
              {
                "$ref": "#/components/schemas/TerminalSummary"
              }
            ],
            "description": "The terminal."
          },
          "minimumAmountRials": {
            "type": "integer",
            "description": "The smallest payment it accepts.",
            "format": "int64"
          },
          "payerSetsAmount": {
            "type": "boolean",
            "description": "Whether a payer may choose the amount."
          },
          "description": {
            "type": "string",
            "description": "What it is for.",
            "nullable": true
          },
          "splitAccounts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SplitAccountSummary"
            },
            "description": "Where settlement is divided."
          },
          "wage": {
            "allOf": [
              {
                "$ref": "#/components/schemas/TerminalWage"
              }
            ],
            "description": "What the platform charges on it, and who pays."
          },
          "credentials": {
            "allOf": [
              {
                "$ref": "#/components/schemas/TerminalCredentials"
              }
            ],
            "description": "What the acquirer issued, and whether a key is held."
          }
        },
        "additionalProperties": false,
        "description": "A terminal with its configuration and split accounts."
      },
      "TerminalEvent": {
        "type": "object",
        "properties": {
          "occurredAt": {
            "type": "string",
            "description": "When.",
            "format": "date-time"
          },
          "status": {
            "allOf": [
              {
                "$ref": "#/components/schemas/TerminalStatus"
              }
            ],
            "description": "How far a terminal has progressed through onboarding.\n\nValues:\n\n* `0` — `Suspended`\n* `1` — `Pending`\n* `2` — `Sent`\n* `3` — `Complete`"
          },
          "note": {
            "type": "string",
            "description": "Why.",
            "nullable": true
          },
          "actorDisplay": {
            "type": "string",
            "description": "Who decided.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "Something that happened to a terminal."
      },
      "TerminalRequest": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string",
            "description": "The name payers see."
          },
          "gatewayUuid": {
            "type": "string",
            "description": "Which bank gateway it settles through.",
            "format": "uuid"
          },
          "iban": {
            "type": "string",
            "description": "Where the money goes."
          },
          "path": {
            "type": "string",
            "description": "The public path of its hosted pay page.",
            "nullable": true
          },
          "minimumAmountRials": {
            "type": "integer",
            "description": "The smallest payment it accepts.",
            "format": "int64"
          },
          "payerSetsAmount": {
            "type": "boolean",
            "description": "Whether a payer may choose the amount."
          },
          "description": {
            "type": "string",
            "description": "What the terminal is for.",
            "nullable": true
          },
          "phone": {
            "type": "string",
            "description": "A contact number for the business.",
            "nullable": true
          },
          "address": {
            "type": "string",
            "description": "Where the business is.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "What is needed to register or reconfigure a terminal."
      },
      "TerminalStatus": {
        "enum": [
          0,
          1,
          2,
          3
        ],
        "type": "integer",
        "description": "How far a terminal has progressed through onboarding.\n\nValues:\n\n* `0` — `Suspended`\n* `1` — `Pending`\n* `2` — `Sent`\n* `3` — `Complete`",
        "format": "int32"
      },
      "TerminalSummary": {
        "type": "object",
        "properties": {
          "uuid": {
            "type": "string",
            "description": "Public identifier.",
            "format": "uuid"
          },
          "title": {
            "type": "string",
            "description": "The name payers see.",
            "nullable": true
          },
          "path": {
            "type": "string",
            "description": "The public path of its pay page.",
            "nullable": true
          },
          "status": {
            "allOf": [
              {
                "$ref": "#/components/schemas/TerminalStatus"
              }
            ],
            "description": "How far a terminal has progressed through onboarding.\n\nValues:\n\n* `0` — `Suspended`\n* `1` — `Pending`\n* `2` — `Sent`\n* `3` — `Complete`"
          },
          "gatewayName": {
            "type": "string",
            "description": "Which bank it settles through.",
            "nullable": true
          },
          "iban": {
            "type": "string",
            "description": "Where the money goes.",
            "nullable": true
          },
          "ownerUserId": {
            "type": "integer",
            "description": "Whose terminal it is.",
            "format": "int64"
          },
          "ownerUuid": {
            "type": "string",
            "description": "The owner's public identifier, so a reviewer can reach the merchant's\nregistration dossier from the terminal awaiting their decision.",
            "format": "uuid"
          },
          "ownerName": {
            "type": "string",
            "description": "How the owner is named, for the review queue.",
            "nullable": true
          },
          "createdAt": {
            "type": "string",
            "description": "When it was registered.",
            "format": "date-time"
          }
        },
        "additionalProperties": false,
        "description": "A terminal, as listed."
      },
      "TerminalWage": {
        "type": "object",
        "properties": {
          "mode": {
            "allOf": [
              {
                "$ref": "#/components/schemas/WageMode"
              }
            ],
            "description": "How a terminal's commission is worked out.\n\nValues:\n\n* `0` — `Brackets`\n* `1` — `Fixed`\n* `2` — `Percentage`\n* `3` — `None`"
          },
          "bearer": {
            "allOf": [
              {
                "$ref": "#/components/schemas/WageBearer"
              }
            ],
            "description": "Who bears the platform commission.\n\nValues:\n\n* `0` — `Customer`\n* `1` — `Merchant`"
          },
          "fixedRials": {
            "type": "integer",
            "description": "The flat fee, where the mode is Fixed.",
            "format": "int64"
          },
          "percentage": {
            "type": "number",
            "description": "The share, where the mode is Percentage.",
            "format": "double"
          },
          "minimumRials": {
            "type": "integer",
            "description": "The least a percentage may work out to.",
            "format": "int64",
            "nullable": true
          },
          "maximumRials": {
            "type": "integer",
            "description": "The most a percentage may work out to.",
            "format": "int64",
            "nullable": true
          },
          "brackets": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WageBandRequest"
            },
            "description": "The table in force — this terminal's own, or the platform default where it\nhas none of its own."
          },
          "usesPlatformBrackets": {
            "type": "boolean",
            "description": "Whether those bands come from the platform default rather than from this\nterminal. Worth saying on screen: editing them here creates a table for this\nterminal, it does not change the platform's."
          }
        },
        "additionalProperties": false,
        "description": "What a terminal is charged."
      },
      "TicketMessageSummary": {
        "type": "object",
        "properties": {
          "uuid": {
            "type": "string",
            "description": "Public identifier.",
            "format": "uuid"
          },
          "body": {
            "type": "string",
            "description": "What was written."
          },
          "fromSupport": {
            "type": "boolean",
            "description": "Whether support wrote it."
          },
          "authorName": {
            "type": "string",
            "description": "Who wrote it.",
            "nullable": true
          },
          "writtenAt": {
            "type": "string",
            "description": "When.",
            "format": "date-time"
          }
        },
        "additionalProperties": false,
        "description": "One message."
      },
      "TicketStatus": {
        "enum": [
          0,
          1,
          2
        ],
        "type": "integer",
        "description": "Where a support ticket stands.\n\nValues:\n\n* `0` — `Open`\n* `1` — `Answered`\n* `2` — `Closed`",
        "format": "int32"
      },
      "TicketSummary": {
        "type": "object",
        "properties": {
          "uuid": {
            "type": "string",
            "description": "Public identifier.",
            "format": "uuid"
          },
          "subject": {
            "type": "string",
            "description": "What it is about."
          },
          "status": {
            "allOf": [
              {
                "$ref": "#/components/schemas/TicketStatus"
              }
            ],
            "description": "Where a support ticket stands.\n\nValues:\n\n* `0` — `Open`\n* `1` — `Answered`\n* `2` — `Closed`"
          },
          "ownerName": {
            "type": "string",
            "description": "Who opened it.",
            "nullable": true
          },
          "ownerMobile": {
            "type": "string",
            "description": "How to reach them.",
            "nullable": true
          },
          "messageCount": {
            "type": "integer",
            "description": "How many messages it holds.",
            "format": "int32"
          },
          "lastMessageAt": {
            "type": "string",
            "description": "When it was last spoken on.",
            "format": "date-time",
            "nullable": true
          },
          "openedAt": {
            "type": "string",
            "description": "When it started.",
            "format": "date-time"
          }
        },
        "additionalProperties": false,
        "description": "A conversation, as a list shows it."
      },
      "TicketThread": {
        "type": "object",
        "properties": {
          "summary": {
            "allOf": [
              {
                "$ref": "#/components/schemas/TicketSummary"
              }
            ],
            "description": "The conversation."
          },
          "messages": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TicketMessageSummary"
            },
            "description": "What was said, oldest first."
          }
        },
        "additionalProperties": false,
        "description": "A conversation with its messages."
      },
      "TokenPair": {
        "type": "object",
        "properties": {
          "accessToken": {
            "type": "string",
            "description": "The bearer token, short-lived."
          },
          "accessTokenExpiresAt": {
            "type": "string",
            "description": "When the bearer token stops working.",
            "format": "date-time"
          },
          "refreshToken": {
            "type": "string",
            "description": "The value that buys a new pair. Shown once and stored only as a hash."
          },
          "refreshTokenExpiresAt": {
            "type": "string",
            "description": "When the refresh token stops working.",
            "format": "date-time"
          }
        },
        "additionalProperties": false,
        "description": "What a caller receives on signing in."
      },
      "TokenRequest": {
        "type": "object",
        "properties": {
          "mobile": {
            "type": "string",
            "description": "The mobile number, in any spelling."
          },
          "password": {
            "type": "string",
            "description": "The password."
          }
        },
        "additionalProperties": false,
        "description": "Credentials for a token request."
      },
      "TransactionPage": {
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TransactionRow"
            },
            "description": "The page."
          },
          "total": {
            "type": "integer",
            "description": "How many match the filter in total.",
            "format": "int64"
          },
          "sumAmountRials": {
            "type": "integer",
            "description": "What those matching payments came to.",
            "format": "int64"
          }
        },
        "additionalProperties": false,
        "description": "One page of transactions."
      },
      "TransactionRow": {
        "type": "object",
        "properties": {
          "uuid": {
            "type": "string",
            "description": "Public identifier.",
            "format": "uuid"
          },
          "occurredAt": {
            "type": "string",
            "description": "When it was attempted.",
            "format": "date-time"
          },
          "amountRials": {
            "type": "integer",
            "description": "The gross amount.",
            "format": "int64"
          },
          "wageRials": {
            "type": "integer",
            "description": "The commission taken.",
            "format": "int64"
          },
          "netRials": {
            "type": "integer",
            "description": "What the merchant keeps.",
            "format": "int64"
          },
          "succeeded": {
            "type": "boolean",
            "description": "Whether the money moved."
          },
          "status": {
            "allOf": [
              {
                "$ref": "#/components/schemas/TransactionStatus"
              }
            ],
            "description": "How far a payment attempt has progressed with the bank.\n\nValues:\n\n* `0` — `Pending`\n* `1` — `Requested`\n* `2` — `Responded`\n* `3` — `Verified`\n* `4` — `Refunded`"
          },
          "rrn": {
            "type": "string",
            "description": "The reference number.",
            "nullable": true
          },
          "traceNumber": {
            "type": "string",
            "description": "The bank's trace number.",
            "nullable": true
          },
          "maskedPan": {
            "type": "string",
            "description": "The card, masked.",
            "nullable": true
          },
          "payerName": {
            "type": "string",
            "description": "Who paid.",
            "nullable": true
          },
          "payerMobile": {
            "type": "string",
            "description": "Their number.",
            "nullable": true
          },
          "terminalTitle": {
            "type": "string",
            "description": "Which terminal took it.",
            "nullable": true
          },
          "gatewayName": {
            "type": "string",
            "description": "Which bank.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "One payment, as a report shows it."
      },
      "TransactionSort": {
        "enum": [
          0,
          1,
          2
        ],
        "type": "integer",
        "description": "What a transaction grid can be ordered by.\n\nValues:\n\n* `0` — `Date`\n* `1` — `Amount`\n* `2` — `Status`",
        "format": "int32"
      },
      "TransactionStatus": {
        "enum": [
          0,
          1,
          2,
          3,
          4
        ],
        "type": "integer",
        "description": "How far a payment attempt has progressed with the bank.\n\nValues:\n\n* `0` — `Pending`\n* `1` — `Requested`\n* `2` — `Responded`\n* `3` — `Verified`\n* `4` — `Refunded`",
        "format": "int32"
      },
      "UserStatus": {
        "enum": [
          0,
          1,
          2,
          3
        ],
        "type": "integer",
        "description": "Where a user account stands.\n\nValues:\n\n* `0` — `Pending`\n* `1` — `Active`\n* `2` — `Blocked`\n* `3` — `Suspended`",
        "format": "int32"
      },
      "UserSummary": {
        "type": "object",
        "properties": {
          "uuid": {
            "type": "string",
            "description": "The public identifier.",
            "format": "uuid"
          },
          "mobile": {
            "type": "string",
            "description": "The mobile number they sign in with."
          },
          "displayName": {
            "type": "string",
            "description": "How to address them.",
            "nullable": true
          },
          "status": {
            "allOf": [
              {
                "$ref": "#/components/schemas/UserStatus"
              }
            ],
            "description": "Where a user account stands.\n\nValues:\n\n* `0` — `Pending`\n* `1` — `Active`\n* `2` — `Blocked`\n* `3` — `Suspended`"
          },
          "roles": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "What they may do."
          },
          "mobileConfirmed": {
            "type": "boolean",
            "description": "Whether the number has been verified."
          }
        },
        "additionalProperties": false,
        "description": "What a caller needs to know about a signed-in user."
      },
      "WageBandRequest": {
        "type": "object",
        "properties": {
          "ceilingRials": {
            "type": "integer",
            "description": "The highest amount this band covers.",
            "format": "int64"
          },
          "commissionRials": {
            "type": "integer",
            "description": "What is charged within it.",
            "format": "int64"
          }
        },
        "additionalProperties": false,
        "description": "One band of a bracket table."
      },
      "WageBearer": {
        "enum": [
          0,
          1
        ],
        "type": "integer",
        "description": "Who bears the platform commission.\n\nValues:\n\n* `0` — `Customer`\n* `1` — `Merchant`",
        "format": "int32"
      },
      "WageConfigurationRequest": {
        "type": "object",
        "properties": {
          "mode": {
            "allOf": [
              {
                "$ref": "#/components/schemas/WageMode"
              }
            ],
            "description": "How a terminal's commission is worked out.\n\nValues:\n\n* `0` — `Brackets`\n* `1` — `Fixed`\n* `2` — `Percentage`\n* `3` — `None`"
          },
          "bearer": {
            "allOf": [
              {
                "$ref": "#/components/schemas/WageBearer"
              }
            ],
            "description": "Who bears the platform commission.\n\nValues:\n\n* `0` — `Customer`\n* `1` — `Merchant`"
          },
          "fixedRials": {
            "type": "integer",
            "description": "The flat fee, where the mode is Fixed.",
            "format": "int64"
          },
          "percentage": {
            "type": "number",
            "description": "The share, where the mode is Percentage.",
            "format": "double"
          },
          "minimumRials": {
            "type": "integer",
            "description": "The least a percentage may work out to.",
            "format": "int64",
            "nullable": true
          },
          "maximumRials": {
            "type": "integer",
            "description": "The most a percentage may work out to.",
            "format": "int64",
            "nullable": true
          },
          "brackets": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WageBandRequest"
            },
            "description": "The bracket table, where the mode is Brackets. An empty table means this\nterminal follows the platform default rather than charging nothing.",
            "nullable": true
          }
        },
        "additionalProperties": false,
        "description": "What is needed to price a terminal."
      },
      "WageMode": {
        "enum": [
          0,
          1,
          2,
          3
        ],
        "type": "integer",
        "description": "How a terminal's commission is worked out.\n\nValues:\n\n* `0` — `Brackets`\n* `1` — `Fixed`\n* `2` — `Percentage`\n* `3` — `None`",
        "format": "int32"
      },
      "WalletEntrySummary": {
        "type": "object",
        "properties": {
          "uuid": {
            "type": "string",
            "description": "Public identifier.",
            "format": "uuid"
          },
          "movement": {
            "allOf": [
              {
                "$ref": "#/components/schemas/WalletMovement"
              }
            ],
            "description": "Whether a wallet movement added or removed funds.\n\nValues:\n\n* `0` — `Credit`\n* `1` — `Debit`"
          },
          "amountRials": {
            "type": "integer",
            "description": "How much.",
            "format": "int64"
          },
          "balanceAfterRials": {
            "type": "integer",
            "description": "What the balance became.",
            "format": "int64"
          },
          "reason": {
            "type": "string",
            "description": "Why it moved.",
            "nullable": true
          },
          "occurredAt": {
            "type": "string",
            "description": "When.",
            "format": "date-time"
          }
        },
        "additionalProperties": false,
        "description": "One movement on a wallet."
      },
      "WalletMovement": {
        "enum": [
          0,
          1
        ],
        "type": "integer",
        "description": "Whether a wallet movement added or removed funds.\n\nValues:\n\n* `0` — `Credit`\n* `1` — `Debit`",
        "format": "int32"
      },
      "WalletSummary": {
        "type": "object",
        "properties": {
          "uuid": {
            "type": "string",
            "description": "Public identifier.",
            "format": "uuid"
          },
          "balanceRials": {
            "type": "integer",
            "description": "What is left, in rials.",
            "format": "int64"
          },
          "updatedAt": {
            "type": "string",
            "description": "When it last moved.",
            "format": "date-time"
          }
        },
        "additionalProperties": false,
        "description": "A merchant's balance."
      }
    },
    "securitySchemes": {
      "bearer": {
        "type": "http",
        "description": "An access token, sent as `Authorization: Bearer <token>`.\n\nAn integration gets one from `POST /api/v1/oauth/token` by exchanging its client id and secret; a person gets one from `POST /api/v1/auth/token` by signing in with a mobile number and password. Paste the token alone — the `Bearer` prefix is added for you.",
        "scheme": "bearer",
        "bearerFormat": "JWT"
      },
      "apiKey": {
        "type": "apiKey",
        "description": "An integration's client secret, sent directly on every call.\n\nFor callers that cannot manage a token exchange. It carries the same scopes and the same limits as a token, but puts the long-lived secret on the wire every time, so prefer the token where you can.",
        "name": "X-Api-Key",
        "in": "header"
      }
    }
  },
  "tags": [
    {
      "name": "OAuth",
      "description": "Exchange an integration's credentials for an access token, and read the scopes the platform understands."
    },
    {
      "name": "Auth",
      "description": "Sign a person in, refresh and revoke their session, and manage their password."
    },
    {
      "name": "Links",
      "description": "Payment links: create one or a group of them, poll for payment, and fetch the invoice."
    },
    {
      "name": "Schedules",
      "description": "Recurring collection — a link minted and sent on a cadence, pausable without being deleted."
    },
    {
      "name": "Transactions",
      "description": "What was actually collected, and the receipt for it."
    },
    {
      "name": "Reports",
      "description": "Aggregates over transactions, links and messages: totals, daily series, breakdowns by terminal and gateway, and the payment funnel."
    },
    {
      "name": "Terminals",
      "description": "The acquirer terminals money settles into, their split accounts and their wage. Registering or changing one requires the person, not their integration."
    },
    {
      "name": "Wallet",
      "description": "The SMS wallet: balance, ledger, and the endpoints that spend it by sending a message."
    },
    {
      "name": "Contacts",
      "description": "The address book — contacts, groups, import and export — which bulk links and schedules are addressed to."
    },
    {
      "name": "Products",
      "description": "The catalogue a link can be raised against, and its photographs."
    },
    {
      "name": "Merchants",
      "description": "The registration dossier an acquirer requires before a terminal can be issued."
    },
    {
      "name": "Documents",
      "description": "Files attached to a registration: uploading, listing and removing them."
    },
    {
      "name": "ApiClients",
      "description": "Integrations themselves — creating one, rotating its secret, narrowing its scopes, suspending it, and reading its usage. Every endpoint here requires the person behind the account."
    },
    {
      "name": "Tickets",
      "description": "Support conversations with the platform's operators."
    },
    {
      "name": "AbuseReports",
      "description": "A complaint about a merchant. Filing one is open to anyone; reading them is an operator's work."
    },
    {
      "name": "Pricing",
      "description": "The platform's published wage brackets — the same table payments are billed from. Open to anyone."
    }
  ]
}