{
  "openapi": "3.0.1",
  "info": {
    "title": "Qasimha Merchant API",
    "description": "Offer Buy-Now-Pay-Later (instalment) checkout to your customers and manage the resulting\norders programmatically.\n\n> **Audience:** merchant engineering teams integrating server-to-server.\n> **Region / currency:** Saudi Arabia (SA) / SAR at launch. All endpoints are versioned under `/api/v1`.\n\n## Authentication\n\nAuthenticate every request with a **merchant API key** sent in the `X-Api-Key` header:\n\n```http\nPOST /api/v1/pay/session/create HTTP/1.1\nHost: api.qasimha.com\nContent-Type: application/json\nX-Api-Key: sk_live_AbC123...\n```\n\nAPI keys are created in the **Merchant Portal → Settings → API Keys** (they cannot be minted\nvia the API). Sandbox keys are prefixed `sk_test_`, production keys `sk_live_`. The full key\nis shown **only once** at creation — store it in a secrets manager. Up to 10 active keys per\nmerchant; revoke any time from the portal.\n\nA **publishable** key (`pk_test_` / `pk_live_`, created the same way with type *publishable*) is\ndifferent: it is meant to sit in a web page or a mobile app, opens only `GET /widget/plans`, and is\nrefused by every other endpoint.\n\nThe key identifies your merchant account; all data is automatically scoped to it.\nYou never send a merchant ID — it is derived from the key.\n\n### Scopes\n\n| Scope | Grants |\n| --- | --- |\n| `orders:read` | List/read orders |\n| `orders:write` | Create sessions, capture, fulfil, refund |\n| `settlements:read` | Read settlements |\n| `webhooks:manage` | Manage webhook configuration |\n| `widget:read` | Publishable keys only: read plan quotes for the on-site widget |\n\nA request with a key that lacks the required scope is rejected with `403`.\n\nMerchant Portal sessions (JWT bearer) are also accepted on these endpoints — useful when\ntesting from the portal — but server-to-server integrations should always use API keys.\n\n## Response envelope\n\nEvery response uses a consistent envelope:\n\n```json\n{\n  \"success\": true,\n  \"code\": \"SESSION_CREATED\",\n  \"message\": \"Operation completed successfully\",\n  \"data\": { \"...\": \"...\" },\n  \"errors\": null\n}\n```\n\nOn error, `success` is `false`, `code` is a machine-readable error code, and `errors`\nmay contain field-level details:\n\n```json\n{\n  \"success\": false,\n  \"code\": \"VALIDATION_ERROR\",\n  \"message\": \"Validation failed\",\n  \"data\": null,\n  \"errors\": [ { \"field\": \"totalAmount\", \"message\": \"Must be greater than 0\" } ]\n}\n```\n\n## Error codes\n\n| HTTP | Code (examples) | Meaning |\n| --- | --- | --- |\n| `400` | `VALIDATION_ERROR`, `INVALID_STATUS`, `AMOUNT_EXCEEDED`, `INVALID_AMOUNT`, `PHONE_REQUIRED` | Bad input or an invalid order-state transition |\n| `401` | — | Missing, unknown, expired or revoked API key. No body; a `WWW-Authenticate` header |\n| `403` | `FORBIDDEN` | Authenticated, but the key lacks the required scope (or the portal role lacks the right) |\n| `404` | `NOT_FOUND`, `MERCHANT_NOT_FOUND` | Resource not found (or not yours) |\n| `409` | `DUPLICATE_ORDER_REFERENCE`, `MERCHANT_SUSPENDED`, `NOT_OPEN`, `REQUEST_IN_PROGRESS` | Conflict with the current state |\n| `422` | `IDEMPOTENCY_KEY_REUSED` | An `Idempotency-Key` reused with a different body |\n| `429` | `TOO_MANY_REQUESTS` | Rate limited. Wait for the `Retry-After` header, then retry |\n| `500` | `INTERNAL_ERROR` | Unexpected failure. Retry with the same `Idempotency-Key`; quote `uuid` to support |\n\nA body that is not valid JSON is refused with `400` and no body. Every operation below lists the\ncodes it can return and what each one means.\n\n## Integration flows\n\nThere are **two ways to start an order** — pick based on whether you have a website. **Both create the\nsame order and the same hosted customer journey; only how the customer reaches it differs.**\n\n| Flow | Endpoint | Use when | Customer reaches checkout via |\n|------|----------|----------|-------------------------------|\n| **Payment Session** | `POST /pay/session/create` | You have a website/app | You **redirect** them to the returned `paymentUrl` |\n| **Checkout Link / QR** | `POST /merchant/checkout-links` | You have no website (in-store, WhatsApp, social) | A **link (SMS'd)** or a **QR code (scanned)** |\n\nFor the Checkout Link / QR flow, the request field `channel` selects `\"Link\"` (a phone is required so\nwe can SMS it) or `\"Qr\"` (no phone — you print/display the code). **The Link and the QR are otherwise\nidentical**: both create one order and return the same hosted URL `<base>/pay/{orderId}`, where the\ncustomer completes one journey (verify identity → choose a plan → accept the contract → pay the first\ninstalment). One order type, one page, one backend — the QR vs link choice is purely how it's delivered.\n\nAfter checkout, both flows converge on the **same order lifecycle** below.\n\n## The checkout lifecycle\n\n```\n  Merchant backend                Qasimha BNPL                 Customer\n  ----------------                ------------                 --------\n  1. create session  ───────────────▶            (or: create checkout link / QR)\n                       ◀───────────── paymentUrl + orderId   (or: /pay/{orderId} link + QR)\n  2. redirect customer ───────────────────────────────────────▶ pays at paymentUrl / opens /pay/{id}\n                       ◀──────────────  webhook: order.confirmed (or order.declined)\n  3. fulfil order    ───────────────▶ (ship goods)\n  (optional) refund  ───────────────▶\n```\n\nKey order statuses: `AwaitingCheckout → Approved → Confirmed → (PartiallyFulfilled) →\nFullyFulfilled`, plus `Declined`, `Expired`, `Voided`, `Reversed`.\n\n### Checkout Link / QR sequence\n\nUse this when you have no website. You create the order up-front; the customer completes the same\nhosted journey at `/pay/{orderId}`. Link vs QR differ only in delivery (SMS vs scan).\n\n```\n  Merchant                     Qasimha BNPL                         Customer\n  --------                     ------------                         --------\n  1. POST /merchant/checkout-links\n     { amount, channel, phone? } ─────▶ create order (AwaitingCheckout)\n                                 ◀───── { orderId, url: <base>/pay/{orderId}, qr }\n  2a. channel \"Link\": Qasimha SMS's the link ───────────────────────▶ taps link\n  2b. channel \"Qr\":   you print/display the QR ─────────────────────▶ scans QR\n                                          (both open the SAME /pay/{orderId} page)\n  3. hosted journey on /pay/{orderId}:\n        verify phone (OTP) → verify ID (Nafath) → choose plan\n        → accept contract → pay 1st instalment ──────────────────────▶ completes\n                                 ◀───── webhook: order.confirmed (or order.declined)\n  4. fulfil order  ─────────────▶ (ship goods)\n  (optional) refund / cancel ──▶\n```\n\nPoll `GET /merchant/checkout-links/{id}` (or rely on webhooks) to track the order from\n`AwaitingCheckout` through completion. The `channel` field is `\"Link\"` (phone required) or `\"Qr\"`\n(no phone) — everything after order creation is identical.\n\n## Webhooks\n\nQasimha `POST`s a signed JSON event to each endpoint you register when an order changes, so you can\nkeep records in sync without polling. Register endpoints in the **Merchant Portal → Settings →\nWebhooks**, or with the `/merchant/webhooks` endpoints below (scope `webhooks:manage`). Each\nendpoint has its own signing secret, shown once when it is created or rotated. Up to 5 endpoints\nper account; each may subscribe to all events or to a list.\n\n### Events\n\n| Event | When |\n| --- | --- |\n| `order.confirmed` | Checkout finished — the customer paid the first instalment and signed the contract |\n| `order.declined` | The order was declined, or the customer abandoned it on the confirmation call |\n| `order.fulfilled` / `order.partially_fulfilled` | You recorded a shipment (`amount` = the shipment) |\n| `order.refunded` / `order.partially_refunded` | A refund was recorded (`amount` = that refund) |\n| `order.voided` | The order was voided (`amount` = what was released) |\n| `order.cancelled` | You cancelled an open checkout |\n| `order.expired` / `checkout_link.expired` | The customer never completed the checkout |\n| `ping` | Sent when you press *Send test event* in the portal |\n\n`order.completed` is accepted as a subscription alias of `order.confirmed` for one release.\n\n### Payload\n\n`Content-Type: application/json`\n\n```json\n{\n  \"id\": \"evt_9f8c1d2e3a4b5c6d7e8f90a1b2c3d4e5\",\n  \"type\": \"order.confirmed\",\n  \"createdAt\": \"2026-09-25T09:30:00Z\",\n  \"apiVersion\": \"v1\",\n  \"orderId\": \"9a2e6c4b-1111-4c2d-8a1f-000000000001\",\n  \"orderReferenceId\": \"MERCHANT-ORDER-12345\",\n  \"status\": \"Confirmed\",\n  \"amount\": 1000.00,\n  \"currency\": \"SAR\",\n  \"data\": {\n    \"order\": {\n      \"id\": \"9a2e6c4b-1111-4c2d-8a1f-000000000001\",\n      \"orderReferenceId\": \"MERCHANT-ORDER-12345\",\n      \"status\": \"Confirmed\",\n      \"channel\": \"Online\",\n      \"totalAmount\": 1000.00,\n      \"currencyCode\": \"SAR\",\n      \"capturedAmount\": 0,\n      \"refundedAmount\": 0,\n      \"voidedAmount\": 0,\n      \"instalments\": 4,\n      \"createdAt\": \"2026-09-25T09:00:00Z\",\n      \"confirmedAt\": \"2026-09-25T09:30:00Z\"\n    }\n  }\n}\n```\n\n### Verifying the signature\n\nEvery delivery carries these headers:\n\n| Header | Example | Purpose |\n| --- | --- | --- |\n| `X-Qasimha-Signature` | `t=1758790800,v1=1eb16bb1…` | HMAC-SHA256, see below; several `v1` values during a secret rotation |\n| `X-Qasimha-Timestamp` | `1758790800` | Unix seconds when this attempt was sent — the same value as `t` |\n| `X-Qasimha-Event` | `order.confirmed` | The event type |\n| `X-Qasimha-Event-Id` | `evt_9f8c…` | The event id — de-duplicate on it, retries repeat it |\n| `X-Qasimha-Delivery-Attempt` | `1` | 1 on the first attempt, then 2 to 6 |\n\nVerify before trusting the payload: read the request body **exactly as received** (do not\nre-serialize it), compute `HMAC-SHA256(secret, \"<t>.\" + rawBody)` as lowercase hex, and compare it\n(constant-time) against each `v1` value; accept when any matches. Reject when `t` is more than\nfive minutes from your clock. The Qasimha server SDKs (`webhooks.constructEvent`) do all of this.\n\n```js\n// Node.js (Express) — verify then ack\nconst crypto = require(\"crypto\");\nconst parts = Object.fromEntries(sigHeader.split(\",\").map((p) => p.split(\"=\")));\nconst expected = crypto\n  .createHmac(\"sha256\", process.env.QASIMHA_WEBHOOK_SECRET)\n  .update(parts.t + \".\" + rawBody)  // the exact bytes received\n  .digest(\"hex\");\nconst ok = crypto.timingSafeEqual(Buffer.from(parts.v1, \"hex\"), Buffer.from(expected, \"hex\"));\nif (!ok) return res.status(400).end();\n```\n\nRespond `2xx` within 15 seconds and process asynchronously. A delivery that fails (no `2xx`, a\ntimeout, a connection error) is retried five times over about 24 hours; the portal shows every\nattempt and lets you replay one. Treat a webhook as a hint: re-read the order\n(`GET /merchant/orders/{orderId}`) before fulfilling.\n\nAccounts configured by operations with a single global URL, and no endpoint of their own,\nkeep receiving that channel's original body (`eventId`, `eventType`, `status`,\n`orderReferenceId`, `amount`, `currency`, `occurredAt`) signed as\n`X-Qasimha-Signature: sha256=<HMAC-SHA256(secret, rawBody)>` for `order.completed`,\n`order.declined`, `order.voided` and `order.reversed`. Registering an endpoint switches the\naccount to the format above.\n\n## Publishable keys and the on-site widget\n\n`GET /widget/plans?amount=1200.00&currency=SAR&lang=ar` with the header `X-Public-Key: pk_…`\nanswers the plans your policy offers for that amount — instalment count and amount, first and last\ninstalment, fees including VAT, total payable — quoted from your price list with no customer data.\nIt is what `<qasimha-widget>` renders under a price and what the mobile SDKs' promo views show.\nCross-origin requests are allowed, the platform keeps a merchant's quote for an amount for one minute\n(the response itself is marked uncacheable like every API response; the widget caches per page), and a\npublishable key can do nothing else: it opens no order, no settlement and no customer data. Create one in the\nMerchant Portal (API Keys → type *publishable*).\n\n```json\n{\n  \"success\": true,\n  \"code\": \"OK\",\n  \"data\": {\n    \"merchantName\": \"Demo Store\",\n    \"currency\": \"SAR\",\n    \"minAmount\": 100,\n    \"maxAmount\": 20000,\n    \"plans\": [\n      { \"instalments\": 4, \"frequency\": \"monthly\", \"instalmentAmount\": 300.00, \"firstInstalmentAmount\": 300.00,\n        \"lastInstalmentAmount\": 300.00, \"feesIncludingVat\": 0, \"totalPayable\": 1200.00, \"shariaCompliant\": true }\n    ]\n  }\n}\n```\n\n`plans` is empty when the amount lies outside `[minAmount, maxAmount]` or the account has no active\npolicy. `shariaCompliant` is true for a plan with no finance charge (interest-free BNPL); the\nwidget shows its \"interest-free\" line only then.\n\n## Rate limits & idempotency\n\nEvery merchant endpoint shares one allowance per API key: 100 requests a minute by default. A refused\nrequest answers `429 TOO_MANY_REQUESTS` with a `Retry-After` header (seconds). The plan-quote\nendpoint for publishable keys has its own, larger allowance (600 a minute per key by default).\n\nSend an `Idempotency-Key` header (a fresh UUID v4 per attempt) on every `POST`, `PUT` and `DELETE`,\nand reuse it unchanged when you retry. A retry with the same key and body replays the original answer\nwith `Idempotent-Replay: true` instead of acting twice; the same key with a different body answers\n`422 IDEMPOTENCY_KEY_REUSED`; a retry while the first attempt is still running answers\n`409 REQUEST_IN_PROGRESS` (retry shortly with the same key). Keys are scoped to your merchant, the\nmethod and the path, and kept for 24 hours. Your `orderReferenceId` must also be unique per order.",
    "contact": {
      "name": "Qasimha Integration Support",
      "email": "customercare@qasimha.com"
    },
    "version": "v1"
  },
  "servers": [
    {
      "url": "https://api.qasimha.com",
      "description": "This environment"
    }
  ],
  "paths": {
    "/api/v1/merchant/checkout-links": {
      "post": {
        "tags": [
          "Merchant Checkout Links"
        ],
        "summary": "Create a payment link or QR order",
        "description": "Creates an order up-front and returns `url`, its hosted checkout page. No website or basket integration is\nneeded. Two channels:\n\n- `Link` (the default): `phone` is required. Qasimha sends the customer the link by SMS, by push\n  notification when the phone belongs to a registered app user, and by voice call where Qasimha has enabled it.\n  A failed message never fails the request.\n- `Qr`: nothing is sent. Show `url` as a QR code for the customer to scan.\n\n**When to call it.** In a shop, over WhatsApp or social media, or anywhere you sell without a checkout.\n\n**Scope.** API key with `orders:write`, or a Merchant Portal session with any role except viewer.\n\n**Amount.** Send `amount`, or `items` (the amount is then `Σ quantity × unitPrice`), or both when they are\nequal. The order is financed in your account currency.\n\n**Reference.** Leave out `orderReferenceId` and one is generated as `YYDDDNNNNN`: the UTC year and day of the\nyear, then five digits, for example `2628041980`. The financing contract number repeats it.\n\n**Real-estate merchants** must send `ejarContractNumber` (the Ejar contract the order finances, at most 50\ncharacters); for any other merchant it is ignored.\n\n**Language.** `language` (`ar` or `en`) sets the SMS and push wording and the language the page opens in.\nOmitted: the `Accept-Language` header, then Arabic.\n\n**What happens next.** The order is `AwaitingCheckout` and expires two hours after creation. The customer\ncompletes the same hosted journey as a payment session. Follow it with `GET /api/v1/merchant/checkout-links/{id}`\nor with webhooks:\n\n| Outcome | Order status | Webhook |\n| --- | --- | --- |\n| The customer finished checkout | `Confirmed` | `order.confirmed` |\n| The credit decision declined the customer | `Declined` | `order.declined` |\n| Not finished within two hours | `Expired` | `checkout_link.expired` |\n| You cancelled it | `Cancelled` | `order.cancelled` |\n\n**Return URLs (optional).** `merchantUrl { success, failure, cancel }` is where the hosted page sends the\ncustomer at the end, with `orderId`, `orderReferenceId` and `status` appended to the URL's query string\n(`confirmed` on success; `declined`, `expired` or `failed` on failure; `cancelled`). Absolute URLs, at most\n1024 characters, no `#fragment`; `https`, `http` only on `localhost` / `127.0.0.1`, or a custom app scheme\n(`myshop://…`). `success` and `failure` go together; `cancel` defaults to `failure`; `notification` is\nreserved and refused. A return is a navigation, not a proof: confirm with `GET /api/v1/merchant/orders/{id}`\nbefore you treat the order as paid.\n\n**Idempotency.** Send an `Idempotency-Key` header (a fresh UUID v4 per attempt) and reuse it unchanged\non retries: a retry with the same key and body replays the original answer with `Idempotent-Replay: true`\ninstead of acting twice. Same key with a different body: `422 IDEMPOTENCY_KEY_REUSED`. First attempt still\nrunning: `409 REQUEST_IN_PROGRESS`, retry shortly.\n\n**Errors.**\n\n| HTTP | Code | Meaning |\n| --- | --- | --- |\n| `400` | `INVALID_AMOUNT` | No positive `amount` and no items, or more than 9999999999.99. |\n| `400` | `AMOUNT_ITEMS_MISMATCH` | `amount` and the items total disagree. |\n| `400` | `INVALID_ITEM` | An item with quantity 0 or less or a negative price, or a field too long (name and imageUrl 500, sku 100). |\n| `400` | `PHONE_REQUIRED` | `channel` is `Link` and there is no `phone`. |\n| `400` | `EJAR_CONTRACT_NUMBER_REQUIRED` | A real-estate merchant sent no `ejarContractNumber`. |\n| `400` | `INVALID_EJAR_CONTRACT_NUMBER` | `ejarContractNumber` is longer than 50 characters. |\n| `400` | `INVALID_ORDER_REFERENCE` | `orderReferenceId` is longer than 255 characters. |\n| `400` | `VALIDATION_ERROR` | `merchantUrl` breaks a rule; `errors` names the field. |\n| `401` | — | No credential, or an unknown, expired or revoked API key. No body. |\n| `403` | `FORBIDDEN` | The key lacks `orders:write`, or the portal user is a viewer. |\n| `404` | `MERCHANT_NOT_FOUND` | The merchant behind the credential no longer exists. |\n| `409` | `DUPLICATE_ORDER_REFERENCE` | The reference is already used. The message names the order when it is yours. |\n| `409` | `MERCHANT_SUSPENDED` | Your account is suspended and cannot create orders. |\n| `409` | `REQUEST_IN_PROGRESS` | The first attempt with this `Idempotency-Key` is still running. |\n| `422` | `IDEMPOTENCY_KEY_REUSED` | This `Idempotency-Key` was used with a different body. |\n| `429` | `TOO_MANY_REQUESTS` | Rate limited; wait `Retry-After` seconds. |",
        "operationId": "CreateCheckoutLink",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "description": "A unique key for this attempt, ideally a UUID v4, reused unchanged when you retry it. A retry with the same key and body returns the original response, with `Idempotent-Replay: true`, instead of acting twice. The same key with a different body is `422 IDEMPOTENCY_KEY_REUSED`; a retry while the first attempt is still running is `409 REQUEST_IN_PROGRESS`. Error answers are replayed too, so fix the request and send it with a new key. Keys are scoped to your merchant, the method and the path, and are kept for 24 hours. Optional, but recommended on every POST, PUT and DELETE.",
            "schema": {
              "type": "string"
            },
            "example": "5f0c7a52-3e1b-4c8e-9a2d-6b7f1e0d4c3a"
          },
          {
            "name": "Accept-Language",
            "in": "header",
            "description": "Fallback language for the customer (`ar` or `en`) when the body has no `language`; Arabic when neither says otherwise.",
            "schema": {
              "type": "string"
            },
            "example": "ar"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateCheckoutLinkRequest"
              },
              "examples": {
                "smsLink": {
                  "summary": "Link sent by SMS",
                  "value": {
                    "amount": 250,
                    "channel": "Link",
                    "phone": "+966512345678",
                    "language": "ar",
                    "orderReferenceId": "MERCHANT-ORDER-12345"
                  }
                },
                "qrWithItems": {
                  "summary": "QR with product lines, amount from the items",
                  "value": {
                    "channel": "Qr",
                    "language": "en",
                    "items": [
                      {
                        "name": "Wireless Headphones",
                        "sku": "SKU-1001",
                        "quantity": 1,
                        "unitPrice": 1000,
                        "imageUrl": "https://shop.example/images/sku-1001.jpg"
                      },
                      {
                        "name": "Protective Case",
                        "sku": "SKU-2002",
                        "quantity": 2,
                        "unitPrice": 50
                      }
                    ],
                    "merchantUrl": {
                      "success": "https://shop.example/thanks",
                      "failure": "https://shop.example/sorry"
                    }
                  }
                },
                "realEstate": {
                  "summary": "Real-estate merchant: rent financed under an Ejar contract",
                  "value": {
                    "amount": 18000,
                    "channel": "Link",
                    "phone": "+966512345678",
                    "ejarContractNumber": "EJ-2026-000123"
                  }
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created. The order is `AwaitingCheckout` and expires in two hours. For `Link` the customer has been sent `url` by SMS.",
            "headers": {
              "Idempotent-Replay": {
                "description": "`true` when this response is the stored answer to an earlier request with the same `Idempotency-Key`. Absent otherwise.",
                "schema": {
                  "type": "string",
                  "example": "true"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MerchantCheckoutLinkCreatedDtoApiResponse"
                },
                "examples": {
                  "link": {
                    "summary": "Link created",
                    "value": {
                      "success": true,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "LINK_CREATED",
                      "message": "Checkout link created",
                      "data": {
                        "id": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61",
                        "url": "https://.../pay/3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61?lang=ar",
                        "channel": "Link",
                        "amount": 250,
                        "currency": "SAR",
                        "phone": "+966512345678",
                        "language": "ar",
                        "orderReferenceId": "MERCHANT-ORDER-12345",
                        "ejarContractNumber": null,
                        "items": null,
                        "status": "AwaitingCheckout",
                        "expiresAt": "2026-10-20T11:00:00Z"
                      },
                      "errors": null
                    }
                  },
                  "qr": {
                    "summary": "QR created with items and a generated reference",
                    "value": {
                      "success": true,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "LINK_CREATED",
                      "message": "Checkout link created",
                      "data": {
                        "id": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61",
                        "url": "https://.../pay/3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61?lang=en",
                        "channel": "Qr",
                        "amount": 1100,
                        "currency": "SAR",
                        "phone": null,
                        "language": "en",
                        "orderReferenceId": "2629300417",
                        "ejarContractNumber": null,
                        "items": [
                          {
                            "name": "Wireless Headphones",
                            "sku": "SKU-1001",
                            "quantity": 1,
                            "unitPrice": 1000,
                            "total": 1000,
                            "imageUrl": "https://shop.example/images/sku-1001.jpg"
                          },
                          {
                            "name": "Protective Case",
                            "sku": "SKU-2002",
                            "quantity": 2,
                            "unitPrice": 50,
                            "total": 100,
                            "imageUrl": null
                          }
                        ],
                        "status": "AwaitingCheckout",
                        "expiresAt": "2026-10-20T11:00:00Z"
                      },
                      "errors": null
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request. `INVALID_AMOUNT` (no positive amount and no items, or above 9999999999.99), `AMOUNT_ITEMS_MISMATCH`, `INVALID_ITEM` (quantity 0 or less, negative price, or a field too long), `PHONE_REQUIRED` (a `Link` without `phone`), `EJAR_CONTRACT_NUMBER_REQUIRED` / `INVALID_EJAR_CONTRACT_NUMBER` (real-estate merchants), `INVALID_ORDER_REFERENCE` (longer than 255 characters), `VALIDATION_ERROR` (a bad `merchantUrl`, field details in `errors`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "phoneRequired": {
                    "summary": "Link without a phone",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "PHONE_REQUIRED",
                      "message": "Customer phone number is required for a payment link.",
                      "data": null,
                      "errors": [ ]
                    }
                  },
                  "mismatch": {
                    "summary": "amount disagrees with the items",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "AMOUNT_ITEMS_MISMATCH",
                      "message": "amount (999) does not equal the items total (250.00). Send one or the other, or make them agree.",
                      "data": null,
                      "errors": [ ]
                    }
                  },
                  "badReturnUrl": {
                    "summary": "Return URL not allowed",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "VALIDATION_ERROR",
                      "message": "One or more validation errors occurred.",
                      "data": null,
                      "errors": [
                        {
                          "field": "MerchantUrl.Success",
                          "message": "must use https, http on localhost, or a custom app scheme."
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: no credential, or an unknown, expired or revoked API key. A publishable `pk_` key sent as `X-Api-Key` is refused the same way. The answer has no body and carries a `WWW-Authenticate` header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden: the API key lacks `orders:write`, or the Merchant Portal user's role does not allow this action. Code `FORBIDDEN`. A token that is not a merchant credential at all is refused with no body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "missingScope": {
                    "summary": "API key without orders:write",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "FORBIDDEN",
                      "message": "This API key lacks the orders:write scope.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "`MERCHANT_NOT_FOUND`: the merchant behind the credential no longer exists.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "merchantNotFound": {
                    "summary": "Merchant missing",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "MERCHANT_NOT_FOUND",
                      "message": "Merchant not found.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "Conflict. `DUPLICATE_ORDER_REFERENCE`: the reference is already used (the message names the order when it is yours). `MERCHANT_SUSPENDED`: your account is suspended. `REQUEST_IN_PROGRESS`: the first attempt with this `Idempotency-Key` is still running.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "duplicateReference": {
                    "summary": "Reference already used",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "DUPLICATE_ORDER_REFERENCE",
                      "message": "You already created an order with reference 'MERCHANT-ORDER-12345' (id 3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61).",
                      "data": null,
                      "errors": [ ]
                    }
                  },
                  "merchantSuspended": {
                    "summary": "Account suspended",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "MERCHANT_SUSPENDED",
                      "message": "This merchant is suspended and cannot create orders.",
                      "data": null,
                      "errors": [ ]
                    }
                  },
                  "requestInProgress": {
                    "summary": "Same Idempotency-Key still running",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "REQUEST_IN_PROGRESS",
                      "message": "A request with this Idempotency-Key is still being processed. Wait a moment and retry with the same key.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "`IDEMPOTENCY_KEY_REUSED`: this `Idempotency-Key` was already used with a different body. Use a new key for a new request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "idempotencyKeyReused": {
                    "summary": "Same Idempotency-Key, different body",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "IDEMPOTENCY_KEY_REUSED",
                      "message": "This Idempotency-Key was already used for a different request. Send a new key for a new request.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: more than the allowance for your API key in the current one-minute window (100 requests a minute by default, shared by every merchant endpoint the key calls). Wait for `Retry-After` seconds, then retry with the same `Idempotency-Key`.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "rateLimited": {
                    "summary": "Rate limited",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "TOO_MANY_REQUESTS",
                      "message": "Too many requests. Try again in 42 second(s).",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          }
        }
      },
      "get": {
        "tags": [
          "Merchant Checkout Links"
        ],
        "summary": "List payment-link and QR orders",
        "description": "Returns your 50 most recent hosted-checkout orders, newest first: payment links and QR codes, and also\npayment sessions (`channel: \"Online\"`) and POS orders (`channel: \"Pos\"`). There is no paging or filter.\n\nEach row has the display status (a link past its deadline reads `Expired` even before the background job has\nexpired it), the customer phone masked to its last three digits, the deadline, and `waiting: true` while the\ncheckout is still open.\n\n**When to call it.** To show a board of open links. Read one order with `GET /api/v1/merchant/checkout-links/{id}`\nor `GET /api/v1/merchant/orders/{id}`.\n\n**Scope.** API key with `orders:read` (or `orders:write`), or any Merchant Portal session. Read-only.\n\n**Errors.**\n\n| HTTP | Code | Meaning |\n| --- | --- | --- |\n| `401` | — | No credential, or an unknown, expired or revoked API key. No body. |\n| `403` | `FORBIDDEN` | The key has neither `orders:read` nor `orders:write`. |\n| `429` | `TOO_MANY_REQUESTS` | Rate limited; wait `Retry-After` seconds. |",
        "operationId": "ListCheckoutLinks",
        "responses": {
          "200": {
            "description": "Your 50 most recent hosted-checkout orders, newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MerchantCheckoutLinkListDtoApiResponse"
                },
                "examples": {
                  "links": {
                    "summary": "Two links",
                    "value": {
                      "success": true,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "SUCCESS",
                      "message": "ok",
                      "data": {
                        "items": [
                          {
                            "id": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61",
                            "orderReferenceId": "MERCHANT-ORDER-12345",
                            "ejarContractNumber": null,
                            "amount": 250,
                            "currency": "SAR",
                            "phone": "*****678",
                            "channel": "Link",
                            "status": "AwaitingCheckout",
                            "decisionStatus": null,
                            "expiresAt": "2026-10-20T11:00:00Z",
                            "createdAt": "2026-10-20T09:00:00Z",
                            "waiting": true
                          },
                          {
                            "id": "7b1d3f5a-9c2e-4a6b-8d0f-1e3a5c7b9d2f",
                            "orderReferenceId": "2629200311",
                            "ejarContractNumber": null,
                            "amount": 1100,
                            "currency": "SAR",
                            "phone": null,
                            "channel": "Qr",
                            "status": "Confirmed",
                            "decisionStatus": "APPROVED",
                            "expiresAt": "2026-10-19T16:30:00Z",
                            "createdAt": "2026-10-19T14:30:00Z",
                            "waiting": false
                          }
                        ]
                      },
                      "errors": null
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: no credential, or an unknown, expired or revoked API key. A publishable `pk_` key sent as `X-Api-Key` is refused the same way. The answer has no body and carries a `WWW-Authenticate` header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden: the API key lacks `orders:read`, or the Merchant Portal user's role does not allow this action. Code `FORBIDDEN`. A token that is not a merchant credential at all is refused with no body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "missingScope": {
                    "summary": "API key without orders:read",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "FORBIDDEN",
                      "message": "This API key is missing the required scope: orders:read",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: more than the allowance for your API key in the current one-minute window (100 requests a minute by default, shared by every merchant endpoint the key calls). Wait for `Retry-After` seconds, then retry with the same `Idempotency-Key`.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "rateLimited": {
                    "summary": "Rate limited",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "TOO_MANY_REQUESTS",
                      "message": "Too many requests. Try again in 42 second(s).",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/merchant/checkout-links/{id}": {
      "get": {
        "tags": [
          "Merchant Checkout Links"
        ],
        "summary": "Get a payment-link or QR order's status",
        "description": "Returns the status of one hosted-checkout order. `completed` turns `true` once the customer has finished\ncheckout; `declined` is `true` when the credit decision declined them.\n\n**When to call it.** To poll a link you created when you do not use webhooks; every few seconds while the\ncustomer is in front of you, and no longer than the two-hour deadline.\n\n**Scope.** API key with `orders:read` (or `orders:write`), or any Merchant Portal session.\n\n**Side effect.** A checkout past its deadline is expired by this read: its status becomes `Expired` and the\n`checkout_link.expired` (or, for a payment session, `order.expired`) webhook is sent, if it had not been already.\n\n**Errors.**\n\n| HTTP | Code | Meaning |\n| --- | --- | --- |\n| `401` | — | No credential, or an unknown, expired or revoked API key. No body. |\n| `403` | `FORBIDDEN` | The key has neither `orders:read` nor `orders:write`. |\n| `404` | `NOT_FOUND` | No hosted-checkout order with this id belongs to you. |\n| `429` | `TOO_MANY_REQUESTS` | Rate limited; wait `Retry-After` seconds. |",
        "operationId": "GetCheckoutLink",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The order id returned by `POST /api/v1/merchant/checkout-links` (any hosted-checkout order id works).",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61"
          }
        ],
        "responses": {
          "200": {
            "description": "The link's current status.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MerchantCheckoutLinkStatusDtoApiResponse"
                },
                "examples": {
                  "open": {
                    "summary": "Still waiting for the customer",
                    "value": {
                      "success": true,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "SUCCESS",
                      "message": "ok",
                      "data": {
                        "id": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61",
                        "orderReferenceId": "MERCHANT-ORDER-12345",
                        "ejarContractNumber": null,
                        "amount": 250,
                        "currency": "SAR",
                        "phone": "*****678",
                        "channel": "Link",
                        "status": "AwaitingCheckout",
                        "decisionStatus": null,
                        "expiresAt": "2026-10-20T11:00:00Z",
                        "createdAt": "2026-10-20T09:00:00Z",
                        "completed": false,
                        "declined": false
                      },
                      "errors": null
                    }
                  },
                  "completed": {
                    "summary": "Customer finished checkout",
                    "value": {
                      "success": true,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "SUCCESS",
                      "message": "ok",
                      "data": {
                        "id": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61",
                        "orderReferenceId": "MERCHANT-ORDER-12345",
                        "ejarContractNumber": null,
                        "amount": 250,
                        "currency": "SAR",
                        "phone": "*****678",
                        "channel": "Link",
                        "status": "Confirmed",
                        "decisionStatus": "APPROVED",
                        "expiresAt": "2026-10-20T11:00:00Z",
                        "createdAt": "2026-10-20T09:00:00Z",
                        "completed": true,
                        "declined": false
                      },
                      "errors": null
                    }
                  },
                  "expired": {
                    "summary": "Deadline passed",
                    "value": {
                      "success": true,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "SUCCESS",
                      "message": "ok",
                      "data": {
                        "id": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61",
                        "orderReferenceId": "MERCHANT-ORDER-12345",
                        "ejarContractNumber": null,
                        "amount": 250,
                        "currency": "SAR",
                        "phone": "*****678",
                        "channel": "Link",
                        "status": "Expired",
                        "decisionStatus": null,
                        "expiresAt": "2026-10-20T11:00:00Z",
                        "createdAt": "2026-10-20T09:00:00Z",
                        "completed": false,
                        "declined": false
                      },
                      "errors": null
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: no credential, or an unknown, expired or revoked API key. A publishable `pk_` key sent as `X-Api-Key` is refused the same way. The answer has no body and carries a `WWW-Authenticate` header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden: the API key lacks `orders:read`, or the Merchant Portal user's role does not allow this action. Code `FORBIDDEN`. A token that is not a merchant credential at all is refused with no body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "missingScope": {
                    "summary": "API key without orders:read",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "FORBIDDEN",
                      "message": "This API key is missing the required scope: orders:read",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND`: no hosted-checkout order with this id belongs to you.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "notFound": {
                    "summary": "Unknown id, or not yours",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "NOT_FOUND",
                      "message": "Checkout link not found.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: more than the allowance for your API key in the current one-minute window (100 requests a minute by default, shared by every merchant endpoint the key calls). Wait for `Retry-After` seconds, then retry with the same `Idempotency-Key`.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "rateLimited": {
                    "summary": "Rate limited",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "TOO_MANY_REQUESTS",
                      "message": "Too many requests. Try again in 42 second(s).",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/merchant/checkout-links/{id}/cancel": {
      "post": {
        "tags": [
          "Merchant Checkout Links"
        ],
        "summary": "Cancel a payment-link or QR order",
        "description": "Cancels a hosted checkout the customer has not finished. The order becomes `Cancelled`, the link stops\nworking, and the `order.cancelled` webhook is sent.\n\n**When to call it.** When the sale is off before the customer paid: the customer changed their mind, or you\nsent the link with the wrong amount (cancel it and create a new one).\n\n**Scope.** API key with `orders:write`, or a Merchant Portal session with any role except viewer.\n\n**Precondition.** Status `AwaitingCheckout` and not past the deadline. A checkout past its deadline is\nexpired first (`checkout_link.expired` is sent) and the cancel then answers `409 NOT_OPEN`. To undo a\nconfirmed order, use `POST /api/v1/merchant/orders/{id}/cancel` instead.\n\n**Idempotency.** Send an `Idempotency-Key` header (a fresh UUID v4 per attempt) and reuse it unchanged\non retries: a retry with the same key and body replays the original answer with `Idempotent-Replay: true`\ninstead of acting twice. Same key with a different body: `422 IDEMPOTENCY_KEY_REUSED`. First attempt still\nrunning: `409 REQUEST_IN_PROGRESS`, retry shortly.\n\n**Errors.**\n\n| HTTP | Code | Meaning |\n| --- | --- | --- |\n| `401` | — | No credential, or an unknown, expired or revoked API key. No body. |\n| `403` | `FORBIDDEN` | The key lacks `orders:write`, or the portal user is a viewer. |\n| `404` | `NOT_FOUND` | No hosted-checkout order with this id belongs to you. |\n| `409` | `NOT_OPEN` | The checkout is no longer open: completed, declined, already cancelled or expired. |\n| `409` | `REQUEST_IN_PROGRESS` | The first attempt with this `Idempotency-Key` is still running. |\n| `422` | `IDEMPOTENCY_KEY_REUSED` | This `Idempotency-Key` was used with a different body. |\n| `429` | `TOO_MANY_REQUESTS` | Rate limited; wait `Retry-After` seconds. |",
        "operationId": "CancelCheckoutLink",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The order id returned by `POST /api/v1/merchant/checkout-links` (any hosted-checkout order id works).",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61"
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "description": "A unique key for this attempt, ideally a UUID v4, reused unchanged when you retry it. A retry with the same key and body returns the original response, with `Idempotent-Replay: true`, instead of acting twice. The same key with a different body is `422 IDEMPOTENCY_KEY_REUSED`; a retry while the first attempt is still running is `409 REQUEST_IN_PROGRESS`. Error answers are replayed too, so fix the request and send it with a new key. Keys are scoped to your merchant, the method and the path, and are kept for 24 hours. Optional, but recommended on every POST, PUT and DELETE.",
            "schema": {
              "type": "string"
            },
            "example": "5f0c7a52-3e1b-4c8e-9a2d-6b7f1e0d4c3a"
          }
        ],
        "responses": {
          "200": {
            "description": "Cancelled. Code `LINK_CANCELLED`; `data` is the link with `status: \"Cancelled\"`.",
            "headers": {
              "Idempotent-Replay": {
                "description": "`true` when this response is the stored answer to an earlier request with the same `Idempotency-Key`. Absent otherwise.",
                "schema": {
                  "type": "string",
                  "example": "true"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MerchantCheckoutLinkStatusDtoApiResponse"
                },
                "examples": {
                  "cancelled": {
                    "summary": "Link cancelled",
                    "value": {
                      "success": true,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "LINK_CANCELLED",
                      "message": "Checkout link cancelled",
                      "data": {
                        "id": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61",
                        "orderReferenceId": "MERCHANT-ORDER-12345",
                        "ejarContractNumber": null,
                        "amount": 250,
                        "currency": "SAR",
                        "phone": "*****678",
                        "channel": "Link",
                        "status": "Cancelled",
                        "decisionStatus": null,
                        "expiresAt": "2026-10-20T11:00:00Z",
                        "createdAt": "2026-10-20T09:00:00Z",
                        "completed": false,
                        "declined": false
                      },
                      "errors": null
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: no credential, or an unknown, expired or revoked API key. A publishable `pk_` key sent as `X-Api-Key` is refused the same way. The answer has no body and carries a `WWW-Authenticate` header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden: the API key lacks `orders:write`, or the Merchant Portal user's role does not allow this action. Code `FORBIDDEN`. A token that is not a merchant credential at all is refused with no body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "missingScope": {
                    "summary": "API key without orders:write",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "FORBIDDEN",
                      "message": "This API key lacks the orders:write scope.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND`: no hosted-checkout order with this id belongs to you.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "notFound": {
                    "summary": "Unknown id, or not yours",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "NOT_FOUND",
                      "message": "Checkout link not found.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "Conflict. `NOT_OPEN`: the checkout is no longer open (completed, declined, already cancelled or expired). `REQUEST_IN_PROGRESS`: the first attempt with this `Idempotency-Key` is still running.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "notOpen": {
                    "summary": "Already expired",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "NOT_OPEN",
                      "message": "Cannot cancel — status Expired.",
                      "data": null,
                      "errors": [ ]
                    }
                  },
                  "requestInProgress": {
                    "summary": "Same Idempotency-Key still running",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "REQUEST_IN_PROGRESS",
                      "message": "A request with this Idempotency-Key is still being processed. Wait a moment and retry with the same key.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "`IDEMPOTENCY_KEY_REUSED`: this `Idempotency-Key` was already used with a different body. Use a new key for a new request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "idempotencyKeyReused": {
                    "summary": "Same Idempotency-Key, different body",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "IDEMPOTENCY_KEY_REUSED",
                      "message": "This Idempotency-Key was already used for a different request. Send a new key for a new request.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: more than the allowance for your API key in the current one-minute window (100 requests a minute by default, shared by every merchant endpoint the key calls). Wait for `Retry-After` seconds, then retry with the same `Idempotency-Key`.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "rateLimited": {
                    "summary": "Rate limited",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "TOO_MANY_REQUESTS",
                      "message": "Too many requests. Try again in 42 second(s).",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/merchant/orders": {
      "get": {
        "tags": [
          "Merchant Orders"
        ],
        "summary": "List orders",
        "description": "Lists your orders, newest first, one page at a time. Each row is a summary; read one order in full with\n`GET /api/v1/merchant/orders/{id}`.\n\n**When to call it.** For reconciliation and back-office screens. To follow a single order, prefer webhooks or\nthe order read.\n\n**Scope.** API key with `orders:read` (or `orders:write`), or any Merchant Portal session. Read-only.\n\n**Filters.** `status` takes an order status name or a Merchant Portal tab (`approved`, `authorised`,\n`captured`, `refunded`, `cancelled`), see the parameter. `search` matches your reference and the customer's\nname, email and phone. `page` starts at 1; `pageSize` defaults to 20.\n\n**Note.** In these rows `capturedAmount` is the amount you have fulfilled (shipped); on the order read it is\nwhat the customer has paid.\n\n**Errors.**\n\n| HTTP | Code | Meaning |\n| --- | --- | --- |\n| `400` | `INVALID_STATUS` | `status` is neither a status name nor a tab. |\n| `400` | `VALIDATION_ERROR` | `page` below 1, or a negative `pageSize`. |\n| `401` | — | No credential, or an unknown, expired or revoked API key. No body. |\n| `403` | `FORBIDDEN` | The key has neither `orders:read` nor `orders:write`. |\n| `429` | `TOO_MANY_REQUESTS` | Rate limited; wait `Retry-After` seconds. |",
        "operationId": "MerchantListOrders",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "description": "Filter by status. Either an order status name (case-insensitive): Draft, New, Approved, Declined, Expired, Confirmed, FullyFulfilled, PartiallyFulfilled, FullyReversed, PartiallyReversed, Voided, Updated, AwaitingCheckout, Cancelled, Settled, WrittenOff; or a Merchant Portal tab: `approved` (New + Approved), `authorised` or `authorized` (Confirmed), `captured` (FullyFulfilled + PartiallyFulfilled), `refunded` (FullyReversed + PartiallyReversed), `cancelled` or `canceled` (Voided + Cancelled). Anything else is `400 INVALID_STATUS`. Leave it out for every status.",
            "schema": {
              "type": "string"
            },
            "example": "captured"
          },
          {
            "name": "search",
            "in": "query",
            "description": "Case-sensitive substring matched against your order reference and the customer's name, email and phone. Leave it out for no search.",
            "schema": {
              "type": "string"
            },
            "example": "MERCHANT-ORDER-123"
          },
          {
            "name": "page",
            "in": "query",
            "description": "Page number, starting at 1 (the default). 0 or less is `400 VALIDATION_ERROR`.",
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 1
            },
            "example": 1
          },
          {
            "name": "pageSize",
            "in": "query",
            "description": "Rows per page, default 20. Use 1 to 100; a negative value is `400 VALIDATION_ERROR`.",
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 20
            },
            "example": 20
          }
        ],
        "responses": {
          "200": {
            "description": "One page of your orders, newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MerchantOrderPageDtoApiResponse"
                },
                "examples": {
                  "page": {
                    "summary": "First page",
                    "value": {
                      "success": true,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "SUCCESS",
                      "message": "Orders retrieved",
                      "data": {
                        "data": [
                          {
                            "id": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61",
                            "orderReferenceId": "MERCHANT-ORDER-12345",
                            "status": "Confirmed",
                            "policyCode": "BNPL_POLICY_V41",
                            "policyType": "BNPL",
                            "totalAmount": 1278.5,
                            "currencyCode": "SAR",
                            "createdAt": "2026-10-20T09:00:00Z",
                            "customerId": "8d2e4f60-7a1b-4c3d-9e5f-6a7b8c9d0e1f",
                            "customerName": "Noura Al-Shammari",
                            "customerEmail": "noura@example.com",
                            "customerPhone": "+966512345678",
                            "instalments": 4,
                            "channel": "Online",
                            "capturedAmount": 0,
                            "refundedAmount": 0,
                            "ejarContractNumber": null
                          }
                        ],
                        "pagination": {
                          "totalItems": 57,
                          "page": 1,
                          "pageSize": 1,
                          "totalPages": 57
                        }
                      },
                      "errors": null
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request. `INVALID_STATUS`: the `status` filter is not a status or tab name. `VALIDATION_ERROR`: `page` below 1 or a negative `pageSize`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "invalidStatus": {
                    "summary": "Unknown status filter",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "INVALID_STATUS",
                      "message": "Unknown status filter 'shipped'.",
                      "data": null,
                      "errors": [ ]
                    }
                  },
                  "badPage": {
                    "summary": "page 0",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "VALIDATION_ERROR",
                      "message": "page must be 1 or greater.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: no credential, or an unknown, expired or revoked API key. A publishable `pk_` key sent as `X-Api-Key` is refused the same way. The answer has no body and carries a `WWW-Authenticate` header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden: the API key lacks `orders:read`, or the Merchant Portal user's role does not allow this action. Code `FORBIDDEN`. A token that is not a merchant credential at all is refused with no body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "missingScope": {
                    "summary": "API key without orders:read",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "FORBIDDEN",
                      "message": "This API key is missing the required scope: orders:read",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: more than the allowance for your API key in the current one-minute window (100 requests a minute by default, shared by every merchant endpoint the key calls). Wait for `Retry-After` seconds, then retry with the same `Idempotency-Key`.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "rateLimited": {
                    "summary": "Rate limited",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "TOO_MANY_REQUESTS",
                      "message": "Too many requests. Try again in 42 second(s).",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/merchant/orders/counts": {
      "get": {
        "tags": [
          "Merchant Orders"
        ],
        "summary": "Count orders per tab",
        "description": "Returns how many of your orders are in each Merchant Portal tab: `all`, `approved` (New + Approved),\n`authorised` (Confirmed), `captured` (FullyFulfilled + PartiallyFulfilled), `refunded` (FullyReversed +\nPartiallyReversed) and `cancelled` (Voided + Cancelled). Each tab counts exactly the orders that\n`GET /api/v1/merchant/orders?status=<tab>` lists.\n\n**When to call it.** For dashboard badges.\n\n**Scope.** API key with `orders:read` (or `orders:write`), or any Merchant Portal session. Read-only.\n\n**Errors.**\n\n| HTTP | Code | Meaning |\n| --- | --- | --- |\n| `401` | — | No credential, or an unknown, expired or revoked API key. No body. |\n| `403` | `FORBIDDEN` | The key has neither `orders:read` nor `orders:write`. |\n| `429` | `TOO_MANY_REQUESTS` | Rate limited; wait `Retry-After` seconds. |",
        "operationId": "MerchantOrderCounts",
        "responses": {
          "200": {
            "description": "Order counts per tab.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MerchantOrderCountsDtoApiResponse"
                },
                "examples": {
                  "counts": {
                    "summary": "Counts",
                    "value": {
                      "success": true,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "SUCCESS",
                      "message": "Counts retrieved",
                      "data": {
                        "all": 57,
                        "approved": 2,
                        "authorised": 18,
                        "captured": 25,
                        "refunded": 3,
                        "cancelled": 4
                      },
                      "errors": null
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: no credential, or an unknown, expired or revoked API key. A publishable `pk_` key sent as `X-Api-Key` is refused the same way. The answer has no body and carries a `WWW-Authenticate` header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden: the API key lacks `orders:read`, or the Merchant Portal user's role does not allow this action. Code `FORBIDDEN`. A token that is not a merchant credential at all is refused with no body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "missingScope": {
                    "summary": "API key without orders:read",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "FORBIDDEN",
                      "message": "This API key is missing the required scope: orders:read",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: more than the allowance for your API key in the current one-minute window (100 requests a minute by default, shared by every merchant endpoint the key calls). Wait for `Retry-After` seconds, then retry with the same `Idempotency-Key`.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "rateLimited": {
                    "summary": "Rate limited",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "TOO_MANY_REQUESTS",
                      "message": "Too many requests. Try again in 42 second(s).",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/merchant/orders/export.xlsx": {
      "get": {
        "tags": [
          "Merchant Orders"
        ],
        "summary": "Export orders to Excel",
        "description": "Downloads your orders as an Excel workbook (`.xlsx`), newest first, every matching order in one file.\n\n**When to call it.** For offline reconciliation or a finance hand-off.\n\n**Scope.** API key with `orders:read` (or `orders:write`), or any Merchant Portal session. Read-only.\n\n**Filters.** The same `status` and `search` as `GET /api/v1/merchant/orders`, with one difference: a tab that\ncovers several statuses exports only its first one (`approved` exports `New`, `captured` exports\n`FullyFulfilled`, `refunded` exports `FullyReversed`, `cancelled` exports `Voided`). Use status names, or the\nlist endpoint, when you need the exact set.\n\n**The file.** One sheet, `Orders`: Order Reference, Status, Channel, Payment Type, Total Amount, Currency,\nCustomer, Customer Email, Customer Mobile, National ID, Merchant ID, Policy, Decision, Created At (KSA).\nIt contains customer personal data: store it accordingly.\n\n**Errors.**\n\n| HTTP | Code | Meaning |\n| --- | --- | --- |\n| `400` | `INVALID_STATUS` | `status` is neither a status name nor a tab. |\n| `401` | — | No credential, or an unknown, expired or revoked API key. No body. |\n| `403` | `FORBIDDEN` | The key has neither `orders:read` nor `orders:write`. |\n| `429` | `TOO_MANY_REQUESTS` | Rate limited; wait `Retry-After` seconds. |",
        "operationId": "MerchantExportOrders",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "description": "Filter by status. Either an order status name (case-insensitive): Draft, New, Approved, Declined, Expired, Confirmed, FullyFulfilled, PartiallyFulfilled, FullyReversed, PartiallyReversed, Voided, Updated, AwaitingCheckout, Cancelled, Settled, WrittenOff; or a Merchant Portal tab: `approved` (New + Approved), `authorised` or `authorized` (Confirmed), `captured` (FullyFulfilled + PartiallyFulfilled), `refunded` (FullyReversed + PartiallyReversed), `cancelled` or `canceled` (Voided + Cancelled). Anything else is `400 INVALID_STATUS`. Leave it out for every status.",
            "schema": {
              "type": "string"
            },
            "example": "captured"
          },
          {
            "name": "search",
            "in": "query",
            "description": "Case-sensitive substring matched against your order reference and the customer's name, email and phone. Leave it out for no search.",
            "schema": {
              "type": "string"
            },
            "example": "MERCHANT-ORDER-123"
          }
        ],
        "responses": {
          "200": {
            "description": "The workbook, as `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`, named `orders-yyyyMMdd-HHmm.xlsx` (UTC). One sheet, `Orders`, with a header row and the columns: Order Reference, Status, Channel, Payment Type, Total Amount, Currency, Customer, Customer Email, Customer Mobile, National ID, Merchant ID, Policy, Decision, Created At (KSA)."
          },
          "400": {
            "description": "`INVALID_STATUS`: the `status` filter is not a status or tab name.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "invalidStatus": {
                    "summary": "Unknown status filter",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "INVALID_STATUS",
                      "message": "Unknown status filter 'shipped'.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: no credential, or an unknown, expired or revoked API key. A publishable `pk_` key sent as `X-Api-Key` is refused the same way. The answer has no body and carries a `WWW-Authenticate` header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden: the API key lacks `orders:read`, or the Merchant Portal user's role does not allow this action. Code `FORBIDDEN`. A token that is not a merchant credential at all is refused with no body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "missingScope": {
                    "summary": "API key without orders:read",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "FORBIDDEN",
                      "message": "This API key is missing the required scope: orders:read",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: more than the allowance for your API key in the current one-minute window (100 requests a minute by default, shared by every merchant endpoint the key calls). Wait for `Retry-After` seconds, then retry with the same `Idempotency-Key`.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "rateLimited": {
                    "summary": "Rate limited",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "TOO_MANY_REQUESTS",
                      "message": "Too many requests. Try again in 42 second(s).",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/merchant/orders/{id}": {
      "get": {
        "tags": [
          "Merchant Orders"
        ],
        "summary": "Get an order",
        "description": "Returns one order in full: the basket (`items`), the customer, the repayment schedule (`installments`),\nyour fulfilments, refunds (`reversals`) and voids, and every status change (`statusHistory`). Enum values\nare names (`\"Confirmed\"`, `\"PayByInstalments\"`, `\"Online\"`, `\"Paid\"`), never numbers.\n\n**When to call it.** Before you ship: a webhook or a customer returning to your site is a hint, this read is\nthe proof. Also to find out how much you can refund (`refundableAmount`).\n\n**Scope.** API key with `orders:read` (or `orders:write`), or any Merchant Portal session. Read-only.\n\n**Amounts.** `capturedAmount` and `collectedAmount` are what the customer has paid so far; `fulfilledAmount`\nis what you have shipped; `refundedAmount` (= `reversedAmount`) what has been refunded; `voidedAmount` the\nbalance released by a void; `refundableAmount` what you can still refund.\n\n**Errors.**\n\n| HTTP | Code | Meaning |\n| --- | --- | --- |\n| `401` | — | No credential, or an unknown, expired or revoked API key. No body. |\n| `403` | `FORBIDDEN` | The key has neither `orders:read` nor `orders:write`. |\n| `404` | `NOT_FOUND` | No order with this id belongs to you. |\n| `429` | `TOO_MANY_REQUESTS` | Rate limited; wait `Retry-After` seconds. |",
        "operationId": "MerchantGetOrder",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The Qasimha order id: `installmentId` from a payment session, or `id` from a checkout link.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61"
          }
        ],
        "responses": {
          "200": {
            "description": "The order.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MerchantOrderDtoApiResponse"
                },
                "examples": {
                  "confirmed": {
                    "summary": "Confirmed order, first instalment paid",
                    "value": {
                      "success": true,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "SUCCESS",
                      "message": "Order retrieved",
                      "data": {
                        "id": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61",
                        "orderReferenceId": "MERCHANT-ORDER-12345",
                        "status": "Confirmed",
                        "paymentType": "PayByInstalments",
                        "channel": "Online",
                        "checkoutChannel": "Online",
                        "totalAmount": 1278.5,
                        "taxAmount": 163.5,
                        "shippingAmount": 25,
                        "discountAmount": 0,
                        "currencyCode": "SAR",
                        "countryCode": "SA",
                        "description": "Order #12345 from the web store",
                        "instalments": 4,
                        "downPaymentAmount": 0,
                        "approvedAmount": 1278.5,
                        "capturedAmount": 319.63,
                        "fulfilledAmount": 0,
                        "refundedAmount": 0,
                        "refundableAmount": 319.63,
                        "collectedAmount": 319.63,
                        "voidedAmount": 0,
                        "reversedAmount": 0,
                        "paymentUrl": "https://.../pay/3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61?lang=ar",
                        "providerSessionId": null,
                        "providerOrderId": null,
                        "createdAt": "2026-10-20T09:00:00Z",
                        "confirmedAt": "2026-10-20T09:25:00Z",
                        "fulfilledAt": null,
                        "voidedAt": null,
                        "reversedAt": null,
                        "settledAt": null,
                        "checkoutExpiresAt": "2026-10-20T11:00:00Z",
                        "expiresAt": null,
                        "ejarContractNumber": null,
                        "notificationLanguage": "ar",
                        "contractNumber": "01-26293-0012345",
                        "customer": {
                          "id": "8d2e4f60-7a1b-4c3d-9e5f-6a7b8c9d0e1f",
                          "name": "Noura Al-Shammari",
                          "email": "noura@example.com",
                          "phone": "+966512345678"
                        },
                        "items": [
                          {
                            "referenceId": "LINE-1",
                            "name": "Wireless Headphones",
                            "type": "physical",
                            "sku": "SKU-1001",
                            "quantity": 1,
                            "unitPrice": 1000,
                            "totalAmount": 1000,
                            "taxAmount": 150,
                            "discountAmount": 0,
                            "imageUrl": "https://shop.example/images/sku-1001.jpg"
                          },
                          {
                            "referenceId": "LINE-2",
                            "name": "Protective Case",
                            "type": "physical",
                            "sku": "SKU-2002",
                            "quantity": 2,
                            "unitPrice": 50,
                            "totalAmount": 90,
                            "taxAmount": 13.5,
                            "discountAmount": 10,
                            "imageUrl": null
                          }
                        ],
                        "installments": [
                          {
                            "sequence": 1,
                            "dueDate": "2026-10-20T00:00:00Z",
                            "amount": 319.63,
                            "principal": 319.63,
                            "profit": 0,
                            "status": "Paid",
                            "paidAmount": 319.63,
                            "paidAt": "2026-10-20T09:24:00Z",
                            "providerRef": "chg_TS02A1520261020x8Y91234567"
                          },
                          {
                            "sequence": 2,
                            "dueDate": "2026-11-20T00:00:00Z",
                            "amount": 319.63,
                            "principal": 319.63,
                            "profit": 0,
                            "status": "Scheduled",
                            "paidAmount": 0,
                            "paidAt": null,
                            "providerRef": null
                          },
                          {
                            "sequence": 3,
                            "dueDate": "2026-12-20T00:00:00Z",
                            "amount": 319.63,
                            "principal": 319.63,
                            "profit": 0,
                            "status": "Scheduled",
                            "paidAmount": 0,
                            "paidAt": null,
                            "providerRef": null
                          },
                          {
                            "sequence": 4,
                            "dueDate": "2027-01-20T00:00:00Z",
                            "amount": 319.61,
                            "principal": 319.61,
                            "profit": 0,
                            "status": "Scheduled",
                            "paidAmount": 0,
                            "paidAt": null,
                            "providerRef": null
                          }
                        ],
                        "fulfillments": [ ],
                        "reversals": [ ],
                        "voids": [ ],
                        "statusHistory": [
                          {
                            "fromStatus": null,
                            "toStatus": "AwaitingCheckout",
                            "trigger": "LinkCreated",
                            "createdAt": "2026-10-20T09:00:00Z"
                          },
                          {
                            "fromStatus": "AwaitingCheckout",
                            "toStatus": "Confirmed",
                            "trigger": "CheckoutCompleted",
                            "createdAt": "2026-10-20T09:25:00Z"
                          }
                        ]
                      },
                      "errors": null
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: no credential, or an unknown, expired or revoked API key. A publishable `pk_` key sent as `X-Api-Key` is refused the same way. The answer has no body and carries a `WWW-Authenticate` header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden: the API key lacks `orders:read`, or the Merchant Portal user's role does not allow this action. Code `FORBIDDEN`. A token that is not a merchant credential at all is refused with no body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "missingScope": {
                    "summary": "API key without orders:read",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "FORBIDDEN",
                      "message": "This API key is missing the required scope: orders:read",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND`: no order with this id belongs to you.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "notFound": {
                    "summary": "Unknown id, or not yours",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "NOT_FOUND",
                      "message": "Order not found",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: more than the allowance for your API key in the current one-minute window (100 requests a minute by default, shared by every merchant endpoint the key calls). Wait for `Retry-After` seconds, then retry with the same `Idempotency-Key`.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "rateLimited": {
                    "summary": "Rate limited",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "TOO_MANY_REQUESTS",
                      "message": "Too many requests. Try again in 42 second(s).",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/merchant/orders/by-reference/{orderReferenceId}": {
      "get": {
        "tags": [
          "Merchant Orders"
        ],
        "summary": "Get an order by your reference",
        "description": "The same order as `GET /api/v1/merchant/orders/{id}`, found by the `orderReferenceId` you sent (or that was\ngenerated) when it was created.\n\n**When to call it.** When you hold only your own reference: a return URL or webhook you matched by reference,\nor a store that never saved the Qasimha id.\n\n**Scope.** API key with `orders:read` (or `orders:write`), or any Merchant Portal session. Read-only.\n\n**Matching.** Exact and case-sensitive. The reference is the rest of the path, so `ORDER/2026/1` is\n`/by-reference/ORDER/2026/1`; percent-encoding is decoded too.\n\n**Errors.**\n\n| HTTP | Code | Meaning |\n| --- | --- | --- |\n| `400` | `VALIDATION_ERROR` | The reference is empty or longer than 255 characters. |\n| `401` | — | No credential, or an unknown, expired or revoked API key. No body. |\n| `403` | `FORBIDDEN` | The key has neither `orders:read` nor `orders:write`. |\n| `404` | `NOT_FOUND` | None of your orders has this reference. |\n| `429` | `TOO_MANY_REQUESTS` | Rate limited; wait `Retry-After` seconds. |",
        "operationId": "MerchantGetOrderByReference",
        "parameters": [
          {
            "name": "orderReferenceId",
            "in": "path",
            "description": "Your order reference exactly as sent when the order was created: case-sensitive, 1 to 255 characters. It may contain `/` (`/by-reference/ORDER/2026/1`); percent-encoded characters are decoded.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "MERCHANT-ORDER-12345"
          }
        ],
        "responses": {
          "200": {
            "description": "The order.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MerchantOrderDtoApiResponse"
                },
                "examples": {
                  "confirmed": {
                    "summary": "Confirmed order, first instalment paid",
                    "value": {
                      "success": true,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "SUCCESS",
                      "message": "Order retrieved",
                      "data": {
                        "id": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61",
                        "orderReferenceId": "MERCHANT-ORDER-12345",
                        "status": "Confirmed",
                        "paymentType": "PayByInstalments",
                        "channel": "Online",
                        "checkoutChannel": "Online",
                        "totalAmount": 1278.5,
                        "taxAmount": 163.5,
                        "shippingAmount": 25,
                        "discountAmount": 0,
                        "currencyCode": "SAR",
                        "countryCode": "SA",
                        "description": "Order #12345 from the web store",
                        "instalments": 4,
                        "downPaymentAmount": 0,
                        "approvedAmount": 1278.5,
                        "capturedAmount": 319.63,
                        "fulfilledAmount": 0,
                        "refundedAmount": 0,
                        "refundableAmount": 319.63,
                        "collectedAmount": 319.63,
                        "voidedAmount": 0,
                        "reversedAmount": 0,
                        "paymentUrl": "https://.../pay/3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61?lang=ar",
                        "providerSessionId": null,
                        "providerOrderId": null,
                        "createdAt": "2026-10-20T09:00:00Z",
                        "confirmedAt": "2026-10-20T09:25:00Z",
                        "fulfilledAt": null,
                        "voidedAt": null,
                        "reversedAt": null,
                        "settledAt": null,
                        "checkoutExpiresAt": "2026-10-20T11:00:00Z",
                        "expiresAt": null,
                        "ejarContractNumber": null,
                        "notificationLanguage": "ar",
                        "contractNumber": "01-26293-0012345",
                        "customer": {
                          "id": "8d2e4f60-7a1b-4c3d-9e5f-6a7b8c9d0e1f",
                          "name": "Noura Al-Shammari",
                          "email": "noura@example.com",
                          "phone": "+966512345678"
                        },
                        "items": [
                          {
                            "referenceId": "LINE-1",
                            "name": "Wireless Headphones",
                            "type": "physical",
                            "sku": "SKU-1001",
                            "quantity": 1,
                            "unitPrice": 1000,
                            "totalAmount": 1000,
                            "taxAmount": 150,
                            "discountAmount": 0,
                            "imageUrl": "https://shop.example/images/sku-1001.jpg"
                          },
                          {
                            "referenceId": "LINE-2",
                            "name": "Protective Case",
                            "type": "physical",
                            "sku": "SKU-2002",
                            "quantity": 2,
                            "unitPrice": 50,
                            "totalAmount": 90,
                            "taxAmount": 13.5,
                            "discountAmount": 10,
                            "imageUrl": null
                          }
                        ],
                        "installments": [
                          {
                            "sequence": 1,
                            "dueDate": "2026-10-20T00:00:00Z",
                            "amount": 319.63,
                            "principal": 319.63,
                            "profit": 0,
                            "status": "Paid",
                            "paidAmount": 319.63,
                            "paidAt": "2026-10-20T09:24:00Z",
                            "providerRef": "chg_TS02A1520261020x8Y91234567"
                          },
                          {
                            "sequence": 2,
                            "dueDate": "2026-11-20T00:00:00Z",
                            "amount": 319.63,
                            "principal": 319.63,
                            "profit": 0,
                            "status": "Scheduled",
                            "paidAmount": 0,
                            "paidAt": null,
                            "providerRef": null
                          },
                          {
                            "sequence": 3,
                            "dueDate": "2026-12-20T00:00:00Z",
                            "amount": 319.63,
                            "principal": 319.63,
                            "profit": 0,
                            "status": "Scheduled",
                            "paidAmount": 0,
                            "paidAt": null,
                            "providerRef": null
                          },
                          {
                            "sequence": 4,
                            "dueDate": "2027-01-20T00:00:00Z",
                            "amount": 319.61,
                            "principal": 319.61,
                            "profit": 0,
                            "status": "Scheduled",
                            "paidAmount": 0,
                            "paidAt": null,
                            "providerRef": null
                          }
                        ],
                        "fulfillments": [ ],
                        "reversals": [ ],
                        "voids": [ ],
                        "statusHistory": [
                          {
                            "fromStatus": null,
                            "toStatus": "AwaitingCheckout",
                            "trigger": "LinkCreated",
                            "createdAt": "2026-10-20T09:00:00Z"
                          },
                          {
                            "fromStatus": "AwaitingCheckout",
                            "toStatus": "Confirmed",
                            "trigger": "CheckoutCompleted",
                            "createdAt": "2026-10-20T09:25:00Z"
                          }
                        ]
                      },
                      "errors": null
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`VALIDATION_ERROR`: the reference is empty or longer than 255 characters.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "tooLong": {
                    "summary": "Reference too long",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "VALIDATION_ERROR",
                      "message": "orderReferenceId must be 1 to 255 characters.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: no credential, or an unknown, expired or revoked API key. A publishable `pk_` key sent as `X-Api-Key` is refused the same way. The answer has no body and carries a `WWW-Authenticate` header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden: the API key lacks `orders:read`, or the Merchant Portal user's role does not allow this action. Code `FORBIDDEN`. A token that is not a merchant credential at all is refused with no body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "missingScope": {
                    "summary": "API key without orders:read",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "FORBIDDEN",
                      "message": "This API key is missing the required scope: orders:read",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND`: none of your orders has this reference.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "notFound": {
                    "summary": "Unknown id, or not yours",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "NOT_FOUND",
                      "message": "Order not found",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: more than the allowance for your API key in the current one-minute window (100 requests a minute by default, shared by every merchant endpoint the key calls). Wait for `Retry-After` seconds, then retry with the same `Idempotency-Key`.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "rateLimited": {
                    "summary": "Rate limited",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "TOO_MANY_REQUESTS",
                      "message": "Too many requests. Try again in 42 second(s).",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/merchant/orders/{id}/contract": {
      "get": {
        "tags": [
          "Merchant Orders"
        ],
        "summary": "Download the order's financing contract",
        "description": "Downloads the financing contract the customer accepted for this order, as a file: a PDF (`application/pdf`),\nor a Word document when the PDF could not be produced. The `Content-Disposition` header carries the file name.\n\n**When to call it.** After checkout, to keep a copy with your order records. The copy is stored shortly after\nthe customer accepts the contract, so it may not be there the moment the order becomes `Confirmed`; retry a\n`404` a little later.\n\n**Scope.** API key with `orders:read` (or `orders:write`), or any Merchant Portal session. Read-only.\n\n**Errors.**\n\n| HTTP | Code | Meaning |\n| --- | --- | --- |\n| `401` | — | No credential, or an unknown, expired or revoked API key. No body. |\n| `403` | `FORBIDDEN` | The key has neither `orders:read` nor `orders:write`. |\n| `404` | `NOT_FOUND` | The order is not yours, or no contract has been stored for it. |\n| `429` | `TOO_MANY_REQUESTS` | Rate limited; wait `Retry-After` seconds. |",
        "operationId": "MerchantGetOrderContract",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The Qasimha order id: `installmentId` from a payment session, or `id` from a checkout link.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61"
          }
        ],
        "responses": {
          "200": {
            "description": "The signed financing contract as a file download: `application/pdf` (or a `.docx` Word document when the PDF could not be produced), with a `Content-Disposition` file name."
          },
          "401": {
            "description": "Unauthorized: no credential, or an unknown, expired or revoked API key. A publishable `pk_` key sent as `X-Api-Key` is refused the same way. The answer has no body and carries a `WWW-Authenticate` header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden: the API key lacks `orders:read`, or the Merchant Portal user's role does not allow this action. Code `FORBIDDEN`. A token that is not a merchant credential at all is refused with no body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "missingScope": {
                    "summary": "API key without orders:read",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "FORBIDDEN",
                      "message": "This API key is missing the required scope: orders:read",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND`: the order is not yours, or no contract has been stored for it yet (the customer has not accepted one).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "noContract": {
                    "summary": "No contract yet",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "NOT_FOUND",
                      "message": "No contract on this order.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: more than the allowance for your API key in the current one-minute window (100 requests a minute by default, shared by every merchant endpoint the key calls). Wait for `Retry-After` seconds, then retry with the same `Idempotency-Key`.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "rateLimited": {
                    "summary": "Rate limited",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "TOO_MANY_REQUESTS",
                      "message": "Too many requests. Try again in 42 second(s).",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/merchant/orders/{id}/capture": {
      "post": {
        "tags": [
          "Merchant Orders"
        ],
        "summary": "Capture (confirm) an approved order",
        "description": "Confirms an order that was approved but not yet confirmed, moving it from `New` or `Approved` to `Confirmed`.\n\n**When to call it.** Rarely. A hosted-checkout order (payment session, link, QR, POS) is already `Confirmed`\nwhen the customer finishes, and capturing it answers `400 INVALID_STATUS`. Capture is for orders approved\noutside the hosted checkout.\n\n**Scope.** API key with `orders:write`, or a Merchant Portal session with the owner, admin or finance role.\n\n**Side effects.** The `order.confirmed` webhook is sent, and the order starts to count toward your\nsettlement balance.\n\n`status` in the response is the order status as a **number**, unlike the order reads: 0 Draft, 1 New, 2 Approved, 3 Declined, 4 Expired, 5 Confirmed, 6 FullyFulfilled, 7 PartiallyFulfilled, 8 FullyReversed, 9 PartiallyReversed, 10 Voided, 11 Updated, 12 AwaitingCheckout, 13 Cancelled, 14 Settled, 15 WrittenOff.\n\n**Idempotency.** Send an `Idempotency-Key` header (a fresh UUID v4 per attempt) and reuse it unchanged\non retries: a retry with the same key and body replays the original answer with `Idempotent-Replay: true`\ninstead of acting twice. Same key with a different body: `422 IDEMPOTENCY_KEY_REUSED`. First attempt still\nrunning: `409 REQUEST_IN_PROGRESS`, retry shortly.\n\n**Errors.**\n\n| HTTP | Code | Meaning |\n| --- | --- | --- |\n| `400` | `INVALID_STATUS` | The order is not `New` or `Approved`. |\n| `401` | — | No credential, or an unknown, expired or revoked API key. No body. |\n| `403` | `FORBIDDEN` | The key lacks `orders:write` (or has neither `orders:read` nor `orders:write`), or the portal role is not owner, admin or finance. |\n| `404` | `NOT_FOUND` | No order with this id belongs to you. |\n| `409` | `REQUEST_IN_PROGRESS` | The first attempt with this `Idempotency-Key` is still running. |\n| `422` | `IDEMPOTENCY_KEY_REUSED` | This `Idempotency-Key` was used with a different body. |\n| `429` | `TOO_MANY_REQUESTS` | Rate limited; wait `Retry-After` seconds. |",
        "operationId": "MerchantCaptureOrder",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The Qasimha order id: `installmentId` from a payment session, or `id` from a checkout link.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61"
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "description": "A unique key for this attempt, ideally a UUID v4, reused unchanged when you retry it. A retry with the same key and body returns the original response, with `Idempotent-Replay: true`, instead of acting twice. The same key with a different body is `422 IDEMPOTENCY_KEY_REUSED`; a retry while the first attempt is still running is `409 REQUEST_IN_PROGRESS`. Error answers are replayed too, so fix the request and send it with a new key. Keys are scoped to your merchant, the method and the path, and are kept for 24 hours. Optional, but recommended on every POST, PUT and DELETE.",
            "schema": {
              "type": "string"
            },
            "example": "5f0c7a52-3e1b-4c8e-9a2d-6b7f1e0d4c3a"
          }
        ],
        "responses": {
          "200": {
            "description": "Captured. The order is now `Confirmed`. `status` is a number here (5).",
            "headers": {
              "Idempotent-Replay": {
                "description": "`true` when this response is the stored answer to an earlier request with the same `Idempotency-Key`. Absent otherwise.",
                "schema": {
                  "type": "string",
                  "example": "true"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MerchantOrderCaptureResultDtoApiResponse"
                },
                "examples": {
                  "captured": {
                    "summary": "Order confirmed",
                    "value": {
                      "success": true,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "SUCCESS",
                      "message": "Order confirmed",
                      "data": {
                        "id": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61",
                        "status": 5,
                        "confirmedAt": "2026-10-20T09:30:00Z"
                      },
                      "errors": null
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`INVALID_STATUS`: the order is not `New` or `Approved`. A hosted-checkout order is already `Confirmed` when the customer finishes, so it answers this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "invalidStatus": {
                    "summary": "Already confirmed",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "INVALID_STATUS",
                      "message": "Order cannot be confirmed in status Confirmed",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: no credential, or an unknown, expired or revoked API key. A publishable `pk_` key sent as `X-Api-Key` is refused the same way. The answer has no body and carries a `WWW-Authenticate` header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden: the API key lacks `orders:write`, or the Merchant Portal user's role does not allow this action. Code `FORBIDDEN`. A token that is not a merchant credential at all is refused with no body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "missingScope": {
                    "summary": "API key without orders:write",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "FORBIDDEN",
                      "message": "Your role or API-key scope isn't allowed to capture orders.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND`: no order with this id belongs to you.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "notFound": {
                    "summary": "Unknown id, or not yours",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "NOT_FOUND",
                      "message": "Order not found",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "`REQUEST_IN_PROGRESS`: the first attempt with this `Idempotency-Key` is still running; retry shortly.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "requestInProgress": {
                    "summary": "Same Idempotency-Key still running",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "REQUEST_IN_PROGRESS",
                      "message": "A request with this Idempotency-Key is still being processed. Wait a moment and retry with the same key.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "`IDEMPOTENCY_KEY_REUSED`: this `Idempotency-Key` was already used with a different body. Use a new key for a new request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "idempotencyKeyReused": {
                    "summary": "Same Idempotency-Key, different body",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "IDEMPOTENCY_KEY_REUSED",
                      "message": "This Idempotency-Key was already used for a different request. Send a new key for a new request.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: more than the allowance for your API key in the current one-minute window (100 requests a minute by default, shared by every merchant endpoint the key calls). Wait for `Retry-After` seconds, then retry with the same `Idempotency-Key`.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "rateLimited": {
                    "summary": "Rate limited",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "TOO_MANY_REQUESTS",
                      "message": "Too many requests. Try again in 42 second(s).",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/merchant/orders/{id}/fulfill": {
      "post": {
        "tags": [
          "Merchant Orders"
        ],
        "summary": "Fulfil (ship) an order",
        "description": "Records that you shipped all or part of a confirmed order, with optional carrier and tracking details.\n\n**When to call it.** When the goods leave your warehouse (or the service is delivered). Call it once per\nshipment for partial deliveries.\n\n**Scope.** API key with `orders:write`, or a Merchant Portal session with the owner, admin or finance role.\n\n**Precondition.** Status `Confirmed` or `PartiallyFulfilled`.\n\n**Amount.** Leave out `shippedAmount` (send `{}`) to fulfil everything still outstanding\n(`totalAmount − fulfilledAmount`). Otherwise send a positive amount no larger than what is outstanding; it is\nrounded to two decimals. `0` is refused: it does not mean \"nothing shipped yet\".\n\n**Side effects.** When the running total reaches `totalAmount` the order becomes `FullyFulfilled` and the\n`order.fulfilled` webhook is sent; otherwise it becomes `PartiallyFulfilled` and `order.partially_fulfilled`\nis sent. The webhook's `amount` is this shipment.\n\n`status` in the response is the order status as a **number**, unlike the order reads: 0 Draft, 1 New, 2 Approved, 3 Declined, 4 Expired, 5 Confirmed, 6 FullyFulfilled, 7 PartiallyFulfilled, 8 FullyReversed, 9 PartiallyReversed, 10 Voided, 11 Updated, 12 AwaitingCheckout, 13 Cancelled, 14 Settled, 15 WrittenOff.\n\n**Idempotency.** Send an `Idempotency-Key` header (a fresh UUID v4 per attempt) and reuse it unchanged\non retries: a retry with the same key and body replays the original answer with `Idempotent-Replay: true`\ninstead of acting twice. Same key with a different body: `422 IDEMPOTENCY_KEY_REUSED`. First attempt still\nrunning: `409 REQUEST_IN_PROGRESS`, retry shortly.\n\n**Errors.**\n\n| HTTP | Code | Meaning |\n| --- | --- | --- |\n| `400` | `INVALID_STATUS` | The order is not `Confirmed` or `PartiallyFulfilled`. |\n| `400` | `INVALID_SHIPPED_AMOUNT` | `shippedAmount` is 0, negative, or above the outstanding amount. |\n| `401` | — | No credential, or an unknown, expired or revoked API key. No body. |\n| `403` | `FORBIDDEN` | The key lacks `orders:write` (or has neither `orders:read` nor `orders:write`), or the portal role is not owner, admin or finance. |\n| `404` | `NOT_FOUND` | No order with this id belongs to you. |\n| `409` | `REQUEST_IN_PROGRESS` | The first attempt with this `Idempotency-Key` is still running. |\n| `422` | `IDEMPOTENCY_KEY_REUSED` | This `Idempotency-Key` was used with a different body. |\n| `429` | `TOO_MANY_REQUESTS` | Rate limited; wait `Retry-After` seconds. |",
        "operationId": "MerchantFulfillOrder",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The Qasimha order id: `installmentId` from a payment session, or `id` from a checkout link.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61"
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "description": "A unique key for this attempt, ideally a UUID v4, reused unchanged when you retry it. A retry with the same key and body returns the original response, with `Idempotent-Replay: true`, instead of acting twice. The same key with a different body is `422 IDEMPOTENCY_KEY_REUSED`; a retry while the first attempt is still running is `409 REQUEST_IN_PROGRESS`. Error answers are replayed too, so fix the request and send it with a new key. Keys are scoped to your merchant, the method and the path, and are kept for 24 hours. Optional, but recommended on every POST, PUT and DELETE.",
            "schema": {
              "type": "string"
            },
            "example": "5f0c7a52-3e1b-4c8e-9a2d-6b7f1e0d4c3a"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MerchantFulfillRequest"
              },
              "examples": {
                "everything": {
                  "summary": "Ship everything still outstanding",
                  "value": {
                    "shippingCompany": "Aramex",
                    "trackingNumber": "ARX123456789",
                    "trackingUrl": "https://shop.example/track/ARX123456789"
                  }
                },
                "partial": {
                  "summary": "Ship part of the order",
                  "value": {
                    "shippedAmount": 1000,
                    "shippingCompany": "SMSA Express",
                    "trackingNumber": "SMSA290011223"
                  }
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Fulfilment recorded. `status` is a number here: 6 (FullyFulfilled) or 7 (PartiallyFulfilled); `fulfilledAmount` is the running total.",
            "headers": {
              "Idempotent-Replay": {
                "description": "`true` when this response is the stored answer to an earlier request with the same `Idempotency-Key`. Absent otherwise.",
                "schema": {
                  "type": "string",
                  "example": "true"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MerchantOrderFulfilmentResultDtoApiResponse"
                },
                "examples": {
                  "fullyFulfilled": {
                    "summary": "Everything shipped",
                    "value": {
                      "success": true,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "SUCCESS",
                      "message": "Order fulfilled",
                      "data": {
                        "id": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61",
                        "status": 6,
                        "fulfilledAmount": 1278.5
                      },
                      "errors": null
                    }
                  },
                  "partiallyFulfilled": {
                    "summary": "Part shipped",
                    "value": {
                      "success": true,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "SUCCESS",
                      "message": "Order fulfilled",
                      "data": {
                        "id": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61",
                        "status": 7,
                        "fulfilledAmount": 1000
                      },
                      "errors": null
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request. `INVALID_STATUS`: the order is not `Confirmed` or `PartiallyFulfilled`. `INVALID_SHIPPED_AMOUNT`: `shippedAmount` is 0, negative, or above what is still outstanding.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "zero": {
                    "summary": "shippedAmount 0",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "INVALID_SHIPPED_AMOUNT",
                      "message": "shippedAmount must be greater than zero. Omit the field entirely to fulfil everything still outstanding.",
                      "data": null,
                      "errors": [ ]
                    }
                  },
                  "tooMuch": {
                    "summary": "Above the outstanding amount",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "INVALID_SHIPPED_AMOUNT",
                      "message": "shippedAmount 500.00 exceeds the 278.50 still outstanding on this order.",
                      "data": null,
                      "errors": [ ]
                    }
                  },
                  "invalidStatus": {
                    "summary": "Order not confirmed",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "INVALID_STATUS",
                      "message": "Order cannot be fulfilled in status AwaitingCheckout",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: no credential, or an unknown, expired or revoked API key. A publishable `pk_` key sent as `X-Api-Key` is refused the same way. The answer has no body and carries a `WWW-Authenticate` header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden: the API key lacks `orders:write`, or the Merchant Portal user's role does not allow this action. Code `FORBIDDEN`. A token that is not a merchant credential at all is refused with no body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "missingScope": {
                    "summary": "API key without orders:write",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "FORBIDDEN",
                      "message": "Your role or API-key scope isn't allowed to fulfill orders.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND`: no order with this id belongs to you.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "notFound": {
                    "summary": "Unknown id, or not yours",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "NOT_FOUND",
                      "message": "Order not found",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "`REQUEST_IN_PROGRESS`: the first attempt with this `Idempotency-Key` is still running; retry shortly.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "requestInProgress": {
                    "summary": "Same Idempotency-Key still running",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "REQUEST_IN_PROGRESS",
                      "message": "A request with this Idempotency-Key is still being processed. Wait a moment and retry with the same key.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "`IDEMPOTENCY_KEY_REUSED`: this `Idempotency-Key` was already used with a different body. Use a new key for a new request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "idempotencyKeyReused": {
                    "summary": "Same Idempotency-Key, different body",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "IDEMPOTENCY_KEY_REUSED",
                      "message": "This Idempotency-Key was already used for a different request. Send a new key for a new request.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: more than the allowance for your API key in the current one-minute window (100 requests a minute by default, shared by every merchant endpoint the key calls). Wait for `Retry-After` seconds, then retry with the same `Idempotency-Key`.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "rateLimited": {
                    "summary": "Rate limited",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "TOO_MANY_REQUESTS",
                      "message": "Too many requests. Try again in 42 second(s).",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/merchant/orders/{id}/refund": {
      "post": {
        "tags": [
          "Merchant Orders"
        ],
        "summary": "Refund an order",
        "description": "Refunds part or all of what the customer has paid on a shipped order, for a return or a price adjustment.\n\n**When to call it.** After a return or a goodwill adjustment on an order you have fulfilled. To stop an order\nyou have not shipped, cancel it instead.\n\n**Scope.** API key with `orders:write`, or a Merchant Portal session with the owner, admin or finance role.\n\n**Precondition.** Status `FullyFulfilled`, `PartiallyFulfilled` or `PartiallyReversed`.\n\n**Amount.** Greater than 0 and at most the order's `refundableAmount`: what the customer has paid\n(`collectedAmount`) less what was already refunded. The unpaid balance is never refunded.\n\n**Side effects.** Once everything the customer paid has been refunded the order becomes `FullyReversed`, its\nremaining unpaid instalments are cancelled and the `order.refunded` webhook is sent; otherwise it becomes\n`PartiallyReversed` and `order.partially_refunded` is sent. The webhook's `amount` is this refund. Refunds\nreduce your settlement balance; your agreement's MDR refund terms apply.\n\n`status` in the response is the order status as a **number**, unlike the order reads: 0 Draft, 1 New, 2 Approved, 3 Declined, 4 Expired, 5 Confirmed, 6 FullyFulfilled, 7 PartiallyFulfilled, 8 FullyReversed, 9 PartiallyReversed, 10 Voided, 11 Updated, 12 AwaitingCheckout, 13 Cancelled, 14 Settled, 15 WrittenOff.\n\n**Idempotency.** Send an `Idempotency-Key` header (a fresh UUID v4 per attempt) and reuse it unchanged\non retries: a retry with the same key and body replays the original answer with `Idempotent-Replay: true`\ninstead of acting twice. Same key with a different body: `422 IDEMPOTENCY_KEY_REUSED`. First attempt still\nrunning: `409 REQUEST_IN_PROGRESS`, retry shortly.\n\n**Errors.**\n\n| HTTP | Code | Meaning |\n| --- | --- | --- |\n| `400` | `INVALID_STATUS` | The order is not `FullyFulfilled`, `PartiallyFulfilled` or `PartiallyReversed`. |\n| `400` | `INVALID_AMOUNT` | `amount` is 0 or negative. |\n| `400` | `AMOUNT_EXCEEDED` | `amount` is above `refundableAmount`. |\n| `401` | — | No credential, or an unknown, expired or revoked API key. No body. |\n| `403` | `FORBIDDEN` | The key lacks `orders:write` (or has neither `orders:read` nor `orders:write`), or the portal role is not owner, admin or finance. |\n| `404` | `NOT_FOUND` | No order with this id belongs to you. |\n| `409` | `REQUEST_IN_PROGRESS` | The first attempt with this `Idempotency-Key` is still running. |\n| `422` | `IDEMPOTENCY_KEY_REUSED` | This `Idempotency-Key` was used with a different body. |\n| `429` | `TOO_MANY_REQUESTS` | Rate limited; wait `Retry-After` seconds. |",
        "operationId": "MerchantRefundOrder",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The Qasimha order id: `installmentId` from a payment session, or `id` from a checkout link.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61"
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "description": "A unique key for this attempt, ideally a UUID v4, reused unchanged when you retry it. A retry with the same key and body returns the original response, with `Idempotent-Replay: true`, instead of acting twice. The same key with a different body is `422 IDEMPOTENCY_KEY_REUSED`; a retry while the first attempt is still running is `409 REQUEST_IN_PROGRESS`. Error answers are replayed too, so fix the request and send it with a new key. Keys are scoped to your merchant, the method and the path, and are kept for 24 hours. Optional, but recommended on every POST, PUT and DELETE.",
            "schema": {
              "type": "string"
            },
            "example": "5f0c7a52-3e1b-4c8e-9a2d-6b7f1e0d4c3a"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MerchantRefundRequest"
              },
              "examples": {
                "partial": {
                  "summary": "Partial refund for a returned item",
                  "value": {
                    "amount": 300,
                    "reason": "Customer return"
                  }
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Refund recorded. `status` is a number here: 8 (FullyReversed) or 9 (PartiallyReversed); `refundedAmount` is the running total.",
            "headers": {
              "Idempotent-Replay": {
                "description": "`true` when this response is the stored answer to an earlier request with the same `Idempotency-Key`. Absent otherwise.",
                "schema": {
                  "type": "string",
                  "example": "true"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MerchantOrderRefundResultDtoApiResponse"
                },
                "examples": {
                  "partiallyRefunded": {
                    "summary": "Part refunded",
                    "value": {
                      "success": true,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "SUCCESS",
                      "message": "Refund processed",
                      "data": {
                        "id": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61",
                        "status": 9,
                        "refundedAmount": 300
                      },
                      "errors": null
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request. `INVALID_STATUS`: the order is not `FullyFulfilled`, `PartiallyFulfilled` or `PartiallyReversed`. `INVALID_AMOUNT`: `amount` is 0 or negative. `AMOUNT_EXCEEDED`: `amount` is above the order's `refundableAmount`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "exceeded": {
                    "summary": "More than was collected",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "AMOUNT_EXCEEDED",
                      "message": "Refund amount exceeds available 319.63",
                      "data": null,
                      "errors": [ ]
                    }
                  },
                  "invalidAmount": {
                    "summary": "Zero amount",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "INVALID_AMOUNT",
                      "message": "Refund amount must be greater than zero",
                      "data": null,
                      "errors": [ ]
                    }
                  },
                  "invalidStatus": {
                    "summary": "Not shipped yet",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "INVALID_STATUS",
                      "message": "Order cannot be refunded in status Confirmed",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: no credential, or an unknown, expired or revoked API key. A publishable `pk_` key sent as `X-Api-Key` is refused the same way. The answer has no body and carries a `WWW-Authenticate` header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden: the API key lacks `orders:write`, or the Merchant Portal user's role does not allow this action. Code `FORBIDDEN`. A token that is not a merchant credential at all is refused with no body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "missingScope": {
                    "summary": "API key without orders:write",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "FORBIDDEN",
                      "message": "Your role or API-key scope isn't allowed to refund orders.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND`: no order with this id belongs to you.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "notFound": {
                    "summary": "Unknown id, or not yours",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "NOT_FOUND",
                      "message": "Order not found",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "`REQUEST_IN_PROGRESS`: the first attempt with this `Idempotency-Key` is still running; retry shortly.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "requestInProgress": {
                    "summary": "Same Idempotency-Key still running",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "REQUEST_IN_PROGRESS",
                      "message": "A request with this Idempotency-Key is still being processed. Wait a moment and retry with the same key.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "`IDEMPOTENCY_KEY_REUSED`: this `Idempotency-Key` was already used with a different body. Use a new key for a new request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "idempotencyKeyReused": {
                    "summary": "Same Idempotency-Key, different body",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "IDEMPOTENCY_KEY_REUSED",
                      "message": "This Idempotency-Key was already used for a different request. Send a new key for a new request.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: more than the allowance for your API key in the current one-minute window (100 requests a minute by default, shared by every merchant endpoint the key calls). Wait for `Retry-After` seconds, then retry with the same `Idempotency-Key`.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "rateLimited": {
                    "summary": "Rate limited",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "TOO_MANY_REQUESTS",
                      "message": "Too many requests. Try again in 42 second(s).",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/merchant/orders/{id}/cancel": {
      "post": {
        "tags": [
          "Merchant Orders"
        ],
        "summary": "Cancel (void) an order",
        "description": "Cancels an order you will not ship.\n\n- `Confirmed` (checkout finished, nothing shipped): the order is **voided**. Its unpaid instalments are\n  cancelled, `voidedAmount` records the balance released, and the `order.voided` webhook is sent.\n- `AwaitingCheckout` (the customer has not finished): the checkout is **cancelled**, the link stops working,\n  and the `order.cancelled` webhook is sent.\n\n**When to call it.** When the sale is off before you ship: out of stock, or the customer withdrew. Once you\nhave shipped, refund instead.\n\n**Scope.** API key with `orders:write`, or a Merchant Portal session with the owner, admin or finance role.\n\n**Side effects.** A voided order leaves your settlement balance; your agreement's MDR refund terms apply.\n\n`status` in the response is the order status as a **number**, unlike the order reads: 0 Draft, 1 New, 2 Approved, 3 Declined, 4 Expired, 5 Confirmed, 6 FullyFulfilled, 7 PartiallyFulfilled, 8 FullyReversed, 9 PartiallyReversed, 10 Voided, 11 Updated, 12 AwaitingCheckout, 13 Cancelled, 14 Settled, 15 WrittenOff. The answer is `10` (Voided) or `13` (Cancelled).\n\n**Idempotency.** Send an `Idempotency-Key` header (a fresh UUID v4 per attempt) and reuse it unchanged\non retries: a retry with the same key and body replays the original answer with `Idempotent-Replay: true`\ninstead of acting twice. Same key with a different body: `422 IDEMPOTENCY_KEY_REUSED`. First attempt still\nrunning: `409 REQUEST_IN_PROGRESS`, retry shortly.\n\n**Errors.**\n\n| HTTP | Code | Meaning |\n| --- | --- | --- |\n| `400` | `INVALID_STATUS` | The order is neither `Confirmed` nor `AwaitingCheckout`. |\n| `401` | — | No credential, or an unknown, expired or revoked API key. No body. |\n| `403` | `FORBIDDEN` | The key lacks `orders:write` (or has neither `orders:read` nor `orders:write`), or the portal role is not owner, admin or finance. |\n| `404` | `NOT_FOUND` | No order with this id belongs to you. |\n| `409` | `REQUEST_IN_PROGRESS` | The first attempt with this `Idempotency-Key` is still running. |\n| `422` | `IDEMPOTENCY_KEY_REUSED` | This `Idempotency-Key` was used with a different body. |\n| `429` | `TOO_MANY_REQUESTS` | Rate limited; wait `Retry-After` seconds. |",
        "operationId": "MerchantCancelOrder",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The Qasimha order id: `installmentId` from a payment session, or `id` from a checkout link.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61"
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "description": "A unique key for this attempt, ideally a UUID v4, reused unchanged when you retry it. A retry with the same key and body returns the original response, with `Idempotent-Replay: true`, instead of acting twice. The same key with a different body is `422 IDEMPOTENCY_KEY_REUSED`; a retry while the first attempt is still running is `409 REQUEST_IN_PROGRESS`. Error answers are replayed too, so fix the request and send it with a new key. Keys are scoped to your merchant, the method and the path, and are kept for 24 hours. Optional, but recommended on every POST, PUT and DELETE.",
            "schema": {
              "type": "string"
            },
            "example": "5f0c7a52-3e1b-4c8e-9a2d-6b7f1e0d4c3a"
          }
        ],
        "responses": {
          "200": {
            "description": "Cancelled. `status` is a number here: 10 (Voided) for a confirmed order, message `Order cancelled`; 13 (Cancelled) for an open checkout, message `Checkout cancelled`.",
            "headers": {
              "Idempotent-Replay": {
                "description": "`true` when this response is the stored answer to an earlier request with the same `Idempotency-Key`. Absent otherwise.",
                "schema": {
                  "type": "string",
                  "example": "true"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MerchantOrderCancelResultDtoApiResponse"
                },
                "examples": {
                  "voided": {
                    "summary": "Confirmed order voided",
                    "value": {
                      "success": true,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "SUCCESS",
                      "message": "Order cancelled",
                      "data": {
                        "id": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61",
                        "status": 10
                      },
                      "errors": null
                    }
                  },
                  "checkoutCancelled": {
                    "summary": "Open checkout cancelled",
                    "value": {
                      "success": true,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "SUCCESS",
                      "message": "Checkout cancelled",
                      "data": {
                        "id": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61",
                        "status": 13
                      },
                      "errors": null
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`INVALID_STATUS`: the order is neither `AwaitingCheckout` nor `Confirmed` (for example already shipped: refund it instead).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "invalidStatus": {
                    "summary": "Already shipped",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "INVALID_STATUS",
                      "message": "Order cannot be cancelled in status FullyFulfilled",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: no credential, or an unknown, expired or revoked API key. A publishable `pk_` key sent as `X-Api-Key` is refused the same way. The answer has no body and carries a `WWW-Authenticate` header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden: the API key lacks `orders:write`, or the Merchant Portal user's role does not allow this action. Code `FORBIDDEN`. A token that is not a merchant credential at all is refused with no body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "missingScope": {
                    "summary": "API key without orders:write",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "FORBIDDEN",
                      "message": "Your role or API-key scope isn't allowed to cancel orders.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND`: no order with this id belongs to you.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "notFound": {
                    "summary": "Unknown id, or not yours",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "NOT_FOUND",
                      "message": "Order not found",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "`REQUEST_IN_PROGRESS`: the first attempt with this `Idempotency-Key` is still running; retry shortly.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "requestInProgress": {
                    "summary": "Same Idempotency-Key still running",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "REQUEST_IN_PROGRESS",
                      "message": "A request with this Idempotency-Key is still being processed. Wait a moment and retry with the same key.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "`IDEMPOTENCY_KEY_REUSED`: this `Idempotency-Key` was already used with a different body. Use a new key for a new request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "idempotencyKeyReused": {
                    "summary": "Same Idempotency-Key, different body",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "IDEMPOTENCY_KEY_REUSED",
                      "message": "This Idempotency-Key was already used for a different request. Send a new key for a new request.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: more than the allowance for your API key in the current one-minute window (100 requests a minute by default, shared by every merchant endpoint the key calls). Wait for `Retry-After` seconds, then retry with the same `Idempotency-Key`.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "rateLimited": {
                    "summary": "Rate limited",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "TOO_MANY_REQUESTS",
                      "message": "Too many requests. Try again in 42 second(s).",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/merchant/settlements": {
      "get": {
        "tags": [
          "Merchant Settlements"
        ],
        "summary": "List settlements",
        "description": "Lists the settlement batches paid out to you, newest first, one page at a time. Amounts are net of fees.\n\n**When to call it.** To reconcile payouts in your bank account against Qasimha batches.\n\n**Scope.** API key with `settlements:read`, or a Merchant Portal session with the owner, admin or finance role. Read-only.\n\nEach row is the settlement record as stored; `createdBy`, `updatedBy`, `isDeleted` and `domainEvents` are\ninternal bookkeeping you can ignore.\n\n**Errors.**\n\n| HTTP | Code | Meaning |\n| --- | --- | --- |\n| `400` | `VALIDATION_ERROR` | `page` below 1, or a negative `pageSize`. |\n| `401` | — | No credential, or an unknown, expired or revoked API key. No body. |\n| `403` | `FORBIDDEN` | The key lacks `settlements:read`, or the portal role is not owner, admin or finance. |\n| `429` | `TOO_MANY_REQUESTS` | Rate limited; wait `Retry-After` seconds. |",
        "operationId": "MerchantListSettlements",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "description": "Page number, starting at 1 (the default). 0 or less is `400 VALIDATION_ERROR`.",
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 1
            },
            "example": 1
          },
          {
            "name": "pageSize",
            "in": "query",
            "description": "Rows per page, default 20. Use 1 to 100; a negative value is `400 VALIDATION_ERROR`.",
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 20
            },
            "example": 20
          }
        ],
        "responses": {
          "200": {
            "description": "One page of your settlements, newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MerchantSettlementPageDtoApiResponse"
                },
                "examples": {
                  "page": {
                    "summary": "One settlement",
                    "value": {
                      "success": true,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "SUCCESS",
                      "message": "Settlements retrieved",
                      "data": {
                        "data": [
                          {
                            "merchantId": "5a6b7c8d-9e0f-4a1b-8c2d-3e4f5a6b7c8d",
                            "providerSettlementId": "STL-2026-10-15-0007",
                            "amount": 12450.75,
                            "currencyCode": "SAR",
                            "settledAt": "2026-10-15T06:00:00Z",
                            "status": "SETTLED",
                            "orderCount": 14,
                            "metadata": null,
                            "id": "e1f2a3b4-c5d6-4e7f-8a9b-0c1d2e3f4a5b",
                            "createdAt": "2026-10-15T06:00:00Z",
                            "createdBy": null,
                            "updatedAt": null,
                            "updatedBy": null,
                            "isDeleted": false,
                            "domainEvents": [ ]
                          }
                        ],
                        "pagination": {
                          "totalItems": 9,
                          "page": 1,
                          "pageSize": 1,
                          "totalPages": 9
                        }
                      },
                      "errors": null
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "`VALIDATION_ERROR`: `page` below 1 or a negative `pageSize`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "badPage": {
                    "summary": "page 0",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "VALIDATION_ERROR",
                      "message": "page must be 1 or greater.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: no credential, or an unknown, expired or revoked API key. A publishable `pk_` key sent as `X-Api-Key` is refused the same way. The answer has no body and carries a `WWW-Authenticate` header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden: the API key lacks `settlements:read`, or the Merchant Portal user's role does not allow this action. Code `FORBIDDEN`. A token that is not a merchant credential at all is refused with no body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "missingScope": {
                    "summary": "API key without settlements:read",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "FORBIDDEN",
                      "message": "Your role or API-key scope isn't allowed to view settlements.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: more than the allowance for your API key in the current one-minute window (100 requests a minute by default, shared by every merchant endpoint the key calls). Wait for `Retry-After` seconds, then retry with the same `Idempotency-Key`.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "rateLimited": {
                    "summary": "Rate limited",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "TOO_MANY_REQUESTS",
                      "message": "Too many requests. Try again in 42 second(s).",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/merchant/settlements/overview": {
      "get": {
        "tags": [
          "Merchant Settlements"
        ],
        "summary": "Get the settlement overview",
        "description": "Returns your settlement headline figures: the payout account, the cycle, and the **not-paid-yet** balance,\nwhich is what you are owed for orders not yet settled.\n\n`notPaidYet` covers your orders in `Confirmed`, `PartiallyFulfilled`, `FullyFulfilled` or `Updated`. For each\none it takes `totalAmount − refunds − voids`, less your MDR fee on that amount and 15% VAT on the fee.\nPayouts are weekly and need at least `minimumSettlementAmount` (2500): below it, `belowMinimum` is `true`,\n`upcomingPayment` is 0 and `remainingForNextSettlement` says how much is missing.\n\n**When to call it.** For a finance dashboard, or to forecast the next payout.\n\n**Scope.** API key with `settlements:read`, or a Merchant Portal session with the owner, admin or finance role. Read-only.\n\n**Errors.**\n\n| HTTP | Code | Meaning |\n| --- | --- | --- |\n| `401` | — | No credential, or an unknown, expired or revoked API key. No body. |\n| `403` | `FORBIDDEN` | The key lacks `settlements:read`, or the portal role is not owner, admin or finance. |\n| `429` | `TOO_MANY_REQUESTS` | Rate limited; wait `Retry-After` seconds. |",
        "operationId": "MerchantSettlementOverview",
        "responses": {
          "200": {
            "description": "Your settlement figures.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MerchantSettlementOverviewDtoApiResponse"
                },
                "examples": {
                  "overview": {
                    "summary": "Overview",
                    "value": {
                      "success": true,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "SUCCESS",
                      "message": "Overview retrieved",
                      "data": {
                        "currency": "SAR",
                        "iban": "SA0380000000608010167519",
                        "beneficiaryName": "Example Trading Co.",
                        "settlementCycle": "Once a week",
                        "settlementModel": "Flexible",
                        "minimumSettlementAmount": 2500,
                        "notPaidYet": 8412.4,
                        "currentBalance": 8412.4,
                        "remainingForNextSettlement": 0,
                        "belowMinimum": false,
                        "upcomingPayment": 8412.4,
                        "expectedBy": "2026-10-21T00:00:00Z",
                        "nextCombineDate": "2026-10-24T00:00:00Z",
                        "lastSettledAmount": 12450.75,
                        "lastSettlementDate": "2026-10-15T06:00:00Z",
                        "totalSettled": 96320.1,
                        "settlementCount": 9
                      },
                      "errors": null
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: no credential, or an unknown, expired or revoked API key. A publishable `pk_` key sent as `X-Api-Key` is refused the same way. The answer has no body and carries a `WWW-Authenticate` header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden: the API key lacks `settlements:read`, or the Merchant Portal user's role does not allow this action. Code `FORBIDDEN`. A token that is not a merchant credential at all is refused with no body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "missingScope": {
                    "summary": "API key without settlements:read",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "FORBIDDEN",
                      "message": "Your role or API-key scope isn't allowed to view settlements.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: more than the allowance for your API key in the current one-minute window (100 requests a minute by default, shared by every merchant endpoint the key calls). Wait for `Retry-After` seconds, then retry with the same `Idempotency-Key`.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "rateLimited": {
                    "summary": "Rate limited",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "TOO_MANY_REQUESTS",
                      "message": "Too many requests. Try again in 42 second(s).",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/merchant/webhooks": {
      "get": {
        "tags": [
          "Merchant Webhooks"
        ],
        "summary": "List webhook endpoints",
        "description": "Returns every webhook endpoint registered for your account, newest first, with its subscription and state.\nSecrets are never returned: `secretHint` is the last four characters of the current secret, so you can tell\nwhich one a server holds.\n\n**Scope.** API key with `webhooks:manage`, or a Merchant Portal session with the owner or admin role. Every change is written to your account's audit log.\n\n**Errors.**\n\n| HTTP | Code | Meaning |\n| --- | --- | --- |\n| `401` | — | No credential, or an unknown, expired or revoked API key. No body. |\n| `403` | `FORBIDDEN` | The key lacks `webhooks:manage`, or the portal role is not owner or admin. |\n| `429` | `TOO_MANY_REQUESTS` | Rate limited; wait `Retry-After` seconds. |",
        "operationId": "MerchantListWebhooks",
        "responses": {
          "200": {
            "description": "Your endpoints, newest first; an empty array when none.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MerchantWebhookEndpointDtoIReadOnlyListApiResponse"
                },
                "examples": {
                  "endpoints": {
                    "summary": "One endpoint",
                    "value": {
                      "success": true,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "SUCCESS",
                      "message": "Webhook endpoints retrieved",
                      "data": [
                        {
                          "id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
                          "url": "https://shop.example/webhooks/qasimha",
                          "description": "Production store",
                          "events": [
                            "order.confirmed",
                            "order.refunded"
                          ],
                          "isActive": true,
                          "secretHint": "3a9f",
                          "previousSecretExpiresAt": null,
                          "createdAt": "2026-10-20T09:00:00Z",
                          "updatedAt": null
                        }
                      ],
                      "errors": null
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: no credential, or an unknown, expired or revoked API key. A publishable `pk_` key sent as `X-Api-Key` is refused the same way. The answer has no body and carries a `WWW-Authenticate` header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden: the API key lacks `webhooks:manage`, or the Merchant Portal user's role does not allow this action. Code `FORBIDDEN`. A token that is not a merchant credential at all is refused with no body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "missingScope": {
                    "summary": "API key without webhooks:manage",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "FORBIDDEN",
                      "message": "This API key is missing the required scope: webhooks:manage",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: more than the allowance for your API key in the current one-minute window (100 requests a minute by default, shared by every merchant endpoint the key calls). Wait for `Retry-After` seconds, then retry with the same `Idempotency-Key`.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "rateLimited": {
                    "summary": "Rate limited",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "TOO_MANY_REQUESTS",
                      "message": "Too many requests. Try again in 42 second(s).",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Merchant Webhooks"
        ],
        "summary": "Register a webhook endpoint",
        "description": "Registers an HTTPS URL to receive signed order events and returns its signing secret. The secret is shown\n**only in this response**: store it in a secrets manager.\n\n**When to call it.** Once per environment while you set up the integration (or register the endpoint in the\nMerchant Portal under Settings, Webhooks).\n\n**Scope.** API key with `webhooks:manage`, or a Merchant Portal session with the owner or admin role. Every change is written to your account's audit log.\n\n**Events.** `events` lists the types to receive; leave it out (or `[]`) for every event. Known types:\n`order.confirmed`, `order.declined`, `order.fulfilled`, `order.partially_fulfilled`, `order.refunded`, `order.partially_refunded`, `order.voided`, `order.cancelled`, `order.expired`, `checkout_link.expired`. `order.completed` is accepted as an alias of `order.confirmed`. Up to 5 endpoints per account.\n\n**Deliveries.** Each event is `POST`ed as JSON, signed with\n`X-Qasimha-Signature: t=<unix>,v1=<HMAC-SHA256(secret, \"<t>.\" + body)>`. Answer `2xx` within 15 seconds;\na failed delivery is retried five times over about 24 hours. Registering your first endpoint switches the\naccount from any earlier single notification URL to this format.\n\n**Idempotency.** Send an `Idempotency-Key` header (a fresh UUID v4 per attempt) and reuse it unchanged\non retries: a retry with the same key and body replays the original answer with `Idempotent-Replay: true`\ninstead of acting twice. Same key with a different body: `422 IDEMPOTENCY_KEY_REUSED`. First attempt still\nrunning: `409 REQUEST_IN_PROGRESS`, retry shortly.\n\n**Errors.**\n\n| HTTP | Code | Meaning |\n| --- | --- | --- |\n| `400` | `URL_REQUIRED` | No `url`. |\n| `400` | `URL_INVALID` | `url` is not an absolute URL. |\n| `400` | `URL_SCHEME` | `url` is not `https`. |\n| `400` | `URL_PRIVATE` | `url` points at a private, local or loopback address. |\n| `400` | `URL_TOO_LONG` | `url` is over 1024 characters. |\n| `400` | `URL_USERINFO` | `url` carries credentials (`user:pass@`). |\n| `400` | `URL_FRAGMENT` | `url` carries a `#fragment`. |\n| `400` | `VALIDATION_ERROR` | `description` is over 200 characters. |\n| `400` | `INVALID_EVENT` | An entry of `events` is not a known event type. |\n| `400` | `LIMIT_EXCEEDED` | You already have 5 endpoints. |\n| `401` | — | No credential, or an unknown, expired or revoked API key. No body. |\n| `403` | `FORBIDDEN` | The key lacks `webhooks:manage`, or the portal role is not owner or admin. |\n| `409` | `REQUEST_IN_PROGRESS` | The first attempt with this `Idempotency-Key` is still running. |\n| `422` | `IDEMPOTENCY_KEY_REUSED` | This `Idempotency-Key` was used with a different body. |\n| `429` | `TOO_MANY_REQUESTS` | Rate limited; wait `Retry-After` seconds. |",
        "operationId": "MerchantCreateWebhook",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "description": "A unique key for this attempt, ideally a UUID v4, reused unchanged when you retry it. A retry with the same key and body returns the original response, with `Idempotent-Replay: true`, instead of acting twice. The same key with a different body is `422 IDEMPOTENCY_KEY_REUSED`; a retry while the first attempt is still running is `409 REQUEST_IN_PROGRESS`. Error answers are replayed too, so fix the request and send it with a new key. Keys are scoped to your merchant, the method and the path, and are kept for 24 hours. Optional, but recommended on every POST, PUT and DELETE.",
            "schema": {
              "type": "string"
            },
            "example": "5f0c7a52-3e1b-4c8e-9a2d-6b7f1e0d4c3a"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateMerchantWebhookRequest"
              },
              "examples": {
                "someEvents": {
                  "summary": "Two events",
                  "value": {
                    "url": "https://shop.example/webhooks/qasimha",
                    "description": "Production store",
                    "events": [
                      "order.confirmed",
                      "order.refunded"
                    ]
                  }
                },
                "allEvents": {
                  "summary": "Every event",
                  "value": {
                    "url": "https://shop.example/webhooks/qasimha-all"
                  }
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Registered. Code `WEBHOOK_CREATED`. `data.secret` is shown only now.",
            "headers": {
              "Idempotent-Replay": {
                "description": "`true` when this response is the stored answer to an earlier request with the same `Idempotency-Key`. Absent otherwise.",
                "schema": {
                  "type": "string",
                  "example": "true"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MerchantWebhookSecretDtoApiResponse"
                },
                "examples": {
                  "created": {
                    "summary": "Endpoint with its secret",
                    "value": {
                      "success": true,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "WEBHOOK_CREATED",
                      "message": "Webhook endpoint created",
                      "data": {
                        "id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
                        "url": "https://shop.example/webhooks/qasimha",
                        "description": "Production store",
                        "events": [
                          "order.confirmed",
                          "order.refunded"
                        ],
                        "isActive": true,
                        "secret": "whsec_4c1f0e2a9b7d6c5e3f1a0b9c8d7e6f5a4b3c2d1e0f9a3a9f",
                        "secretHint": "3a9f",
                        "previousSecretExpiresAt": null,
                        "createdAt": "2026-10-20T09:00:00Z"
                      },
                      "errors": null
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request: `URL_REQUIRED`, `URL_INVALID` (not an absolute URL), `URL_SCHEME` (not `https`), `URL_PRIVATE` (a private, local or loopback address), `URL_TOO_LONG` (over 1024 characters), `URL_USERINFO` (credentials in the URL), `URL_FRAGMENT` (a `#fragment`), `VALIDATION_ERROR` (description over 200 characters), `INVALID_EVENT` (an unknown event type), `LIMIT_EXCEEDED` (you already have 5 endpoints).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "scheme": {
                    "summary": "Not https",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "URL_SCHEME",
                      "message": "url must use https://.",
                      "data": null,
                      "errors": [ ]
                    }
                  },
                  "private": {
                    "summary": "Private address",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "URL_PRIVATE",
                      "message": "url must point at a public host, not a private or local address.",
                      "data": null,
                      "errors": [ ]
                    }
                  },
                  "unknownEvent": {
                    "summary": "Unknown event type",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "INVALID_EVENT",
                      "message": "Unknown event type 'order.shipped'. Known types: order.confirmed, order.declined, order.fulfilled, order.partially_fulfilled, order.refunded, order.partially_refunded, order.voided, order.cancelled, order.expired, checkout_link.expired.",
                      "data": null,
                      "errors": [ ]
                    }
                  },
                  "limit": {
                    "summary": "Five endpoints already",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "LIMIT_EXCEEDED",
                      "message": "Maximum 5 webhook endpoints per account.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: no credential, or an unknown, expired or revoked API key. A publishable `pk_` key sent as `X-Api-Key` is refused the same way. The answer has no body and carries a `WWW-Authenticate` header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden: the API key lacks `webhooks:manage`, or the Merchant Portal user's role does not allow this action. Code `FORBIDDEN`. A token that is not a merchant credential at all is refused with no body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "missingScope": {
                    "summary": "API key without webhooks:manage",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "FORBIDDEN",
                      "message": "This API key is missing the required scope: webhooks:manage",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "`REQUEST_IN_PROGRESS`: the first attempt with this `Idempotency-Key` is still running; retry shortly.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "requestInProgress": {
                    "summary": "Same Idempotency-Key still running",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "REQUEST_IN_PROGRESS",
                      "message": "A request with this Idempotency-Key is still being processed. Wait a moment and retry with the same key.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "`IDEMPOTENCY_KEY_REUSED`: this `Idempotency-Key` was already used with a different body. Use a new key for a new request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "idempotencyKeyReused": {
                    "summary": "Same Idempotency-Key, different body",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "IDEMPOTENCY_KEY_REUSED",
                      "message": "This Idempotency-Key was already used for a different request. Send a new key for a new request.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: more than the allowance for your API key in the current one-minute window (100 requests a minute by default, shared by every merchant endpoint the key calls). Wait for `Retry-After` seconds, then retry with the same `Idempotency-Key`.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "rateLimited": {
                    "summary": "Rate limited",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "TOO_MANY_REQUESTS",
                      "message": "Too many requests. Try again in 42 second(s).",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/merchant/webhooks/{id}": {
      "get": {
        "tags": [
          "Merchant Webhooks"
        ],
        "summary": "Get a webhook endpoint",
        "description": "Returns one webhook endpoint: its URL, subscription, state and `secretHint`. The secret itself is never\nreturned; rotate it if you lost it.\n\n**Scope.** API key with `webhooks:manage`, or a Merchant Portal session with the owner or admin role. Every change is written to your account's audit log.\n\n**Errors.**\n\n| HTTP | Code | Meaning |\n| --- | --- | --- |\n| `401` | — | No credential, or an unknown, expired or revoked API key. No body. |\n| `403` | `FORBIDDEN` | The key lacks `webhooks:manage`, or the portal role is not owner or admin. |\n| `404` | `NOT_FOUND` | No webhook endpoint with this id belongs to you. |\n| `429` | `TOO_MANY_REQUESTS` | Rate limited; wait `Retry-After` seconds. |",
        "operationId": "MerchantGetWebhook",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Webhook endpoint id, from `GET /api/v1/merchant/webhooks` or the create response.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"
          }
        ],
        "responses": {
          "200": {
            "description": "The endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MerchantWebhookEndpointDtoApiResponse"
                },
                "examples": {
                  "endpoint": {
                    "summary": "Endpoint",
                    "value": {
                      "success": true,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "SUCCESS",
                      "message": "Webhook endpoint retrieved",
                      "data": {
                        "id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
                        "url": "https://shop.example/webhooks/qasimha",
                        "description": "Production store",
                        "events": [
                          "order.confirmed",
                          "order.refunded"
                        ],
                        "isActive": true,
                        "secretHint": "3a9f",
                        "previousSecretExpiresAt": null,
                        "createdAt": "2026-10-20T09:00:00Z",
                        "updatedAt": null
                      },
                      "errors": null
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: no credential, or an unknown, expired or revoked API key. A publishable `pk_` key sent as `X-Api-Key` is refused the same way. The answer has no body and carries a `WWW-Authenticate` header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden: the API key lacks `webhooks:manage`, or the Merchant Portal user's role does not allow this action. Code `FORBIDDEN`. A token that is not a merchant credential at all is refused with no body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "missingScope": {
                    "summary": "API key without webhooks:manage",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "FORBIDDEN",
                      "message": "This API key is missing the required scope: webhooks:manage",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND`: no webhook endpoint with this id belongs to you.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "notFound": {
                    "summary": "Unknown endpoint",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "NOT_FOUND",
                      "message": "Webhook endpoint not found",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: more than the allowance for your API key in the current one-minute window (100 requests a minute by default, shared by every merchant endpoint the key calls). Wait for `Retry-After` seconds, then retry with the same `Idempotency-Key`.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "rateLimited": {
                    "summary": "Rate limited",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "TOO_MANY_REQUESTS",
                      "message": "Too many requests. Try again in 42 second(s).",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Merchant Webhooks"
        ],
        "summary": "Update a webhook endpoint",
        "description": "Changes an endpoint's URL, description, subscribed events or active flag. A field you leave out keeps its\nvalue; `events: []` means every event; `description: \"\"` clears the label. The signing secret does not change.\n\n**When to call it.** To move the receiver, change what it subscribes to, or pause it during maintenance\n(`isActive: false`). Events raised while an endpoint is paused are not queued for it, and are not delivered\nwhen you resume it.\n\n**Scope.** API key with `webhooks:manage`, or a Merchant Portal session with the owner or admin role. Every change is written to your account's audit log.\n\n**Idempotency.** Send an `Idempotency-Key` header (a fresh UUID v4 per attempt) and reuse it unchanged\non retries: a retry with the same key and body replays the original answer with `Idempotent-Replay: true`\ninstead of acting twice. Same key with a different body: `422 IDEMPOTENCY_KEY_REUSED`. First attempt still\nrunning: `409 REQUEST_IN_PROGRESS`, retry shortly.\n\n**Errors.**\n\n| HTTP | Code | Meaning |\n| --- | --- | --- |\n| `400` | `URL_REQUIRED` | No `url`. |\n| `400` | `URL_INVALID` | `url` is not an absolute URL. |\n| `400` | `URL_SCHEME` | `url` is not `https`. |\n| `400` | `URL_PRIVATE` | `url` points at a private, local or loopback address. |\n| `400` | `URL_TOO_LONG` | `url` is over 1024 characters. |\n| `400` | `URL_USERINFO` | `url` carries credentials (`user:pass@`). |\n| `400` | `URL_FRAGMENT` | `url` carries a `#fragment`. |\n| `400` | `VALIDATION_ERROR` | `description` is over 200 characters. |\n| `400` | `INVALID_EVENT` | An entry of `events` is not a known event type. |\n| `401` | — | No credential, or an unknown, expired or revoked API key. No body. |\n| `403` | `FORBIDDEN` | The key lacks `webhooks:manage`, or the portal role is not owner or admin. |\n| `404` | `NOT_FOUND` | No webhook endpoint with this id belongs to you. |\n| `409` | `REQUEST_IN_PROGRESS` | The first attempt with this `Idempotency-Key` is still running. |\n| `422` | `IDEMPOTENCY_KEY_REUSED` | This `Idempotency-Key` was used with a different body. |\n| `429` | `TOO_MANY_REQUESTS` | Rate limited; wait `Retry-After` seconds. |",
        "operationId": "MerchantUpdateWebhook",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Webhook endpoint id, from `GET /api/v1/merchant/webhooks` or the create response.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "description": "A unique key for this attempt, ideally a UUID v4, reused unchanged when you retry it. A retry with the same key and body returns the original response, with `Idempotent-Replay: true`, instead of acting twice. The same key with a different body is `422 IDEMPOTENCY_KEY_REUSED`; a retry while the first attempt is still running is `409 REQUEST_IN_PROGRESS`. Error answers are replayed too, so fix the request and send it with a new key. Keys are scoped to your merchant, the method and the path, and are kept for 24 hours. Optional, but recommended on every POST, PUT and DELETE.",
            "schema": {
              "type": "string"
            },
            "example": "5f0c7a52-3e1b-4c8e-9a2d-6b7f1e0d4c3a"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateMerchantWebhookRequest"
              },
              "examples": {
                "pause": {
                  "summary": "Pause deliveries",
                  "value": {
                    "isActive": false
                  }
                },
                "events": {
                  "summary": "Change the subscription",
                  "value": {
                    "events": [
                      "order.confirmed",
                      "order.declined",
                      "order.refunded",
                      "order.partially_refunded"
                    ]
                  }
                },
                "move": {
                  "summary": "Move to a new URL",
                  "value": {
                    "url": "https://shop.example/webhooks/qasimha-v2",
                    "description": "Production store (v2)"
                  }
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Updated. Code `WEBHOOK_UPDATED`; `data` is the endpoint after the change.",
            "headers": {
              "Idempotent-Replay": {
                "description": "`true` when this response is the stored answer to an earlier request with the same `Idempotency-Key`. Absent otherwise.",
                "schema": {
                  "type": "string",
                  "example": "true"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MerchantWebhookEndpointDtoApiResponse"
                },
                "examples": {
                  "updated": {
                    "summary": "Paused endpoint",
                    "value": {
                      "success": true,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "WEBHOOK_UPDATED",
                      "message": "Webhook endpoint updated",
                      "data": {
                        "id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
                        "url": "https://shop.example/webhooks/qasimha",
                        "description": "Production store",
                        "events": [
                          "order.confirmed",
                          "order.refunded"
                        ],
                        "isActive": false,
                        "secretHint": "3a9f",
                        "previousSecretExpiresAt": null,
                        "createdAt": "2026-10-20T09:00:00Z",
                        "updatedAt": "2026-10-21T08:00:00Z"
                      },
                      "errors": null
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request: `URL_REQUIRED`, `URL_INVALID` (not an absolute URL), `URL_SCHEME` (not `https`), `URL_PRIVATE` (a private, local or loopback address), `URL_TOO_LONG` (over 1024 characters), `URL_USERINFO` (credentials in the URL), `URL_FRAGMENT` (a `#fragment`), `VALIDATION_ERROR` (description over 200 characters), `INVALID_EVENT` (an unknown event type).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "scheme": {
                    "summary": "Not https",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "URL_SCHEME",
                      "message": "url must use https://.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: no credential, or an unknown, expired or revoked API key. A publishable `pk_` key sent as `X-Api-Key` is refused the same way. The answer has no body and carries a `WWW-Authenticate` header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden: the API key lacks `webhooks:manage`, or the Merchant Portal user's role does not allow this action. Code `FORBIDDEN`. A token that is not a merchant credential at all is refused with no body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "missingScope": {
                    "summary": "API key without webhooks:manage",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "FORBIDDEN",
                      "message": "This API key is missing the required scope: webhooks:manage",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND`: no webhook endpoint with this id belongs to you.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "notFound": {
                    "summary": "Unknown endpoint",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "NOT_FOUND",
                      "message": "Webhook endpoint not found",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "`REQUEST_IN_PROGRESS`: the first attempt with this `Idempotency-Key` is still running; retry shortly.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "requestInProgress": {
                    "summary": "Same Idempotency-Key still running",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "REQUEST_IN_PROGRESS",
                      "message": "A request with this Idempotency-Key is still being processed. Wait a moment and retry with the same key.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "`IDEMPOTENCY_KEY_REUSED`: this `Idempotency-Key` was already used with a different body. Use a new key for a new request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "idempotencyKeyReused": {
                    "summary": "Same Idempotency-Key, different body",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "IDEMPOTENCY_KEY_REUSED",
                      "message": "This Idempotency-Key was already used for a different request. Send a new key for a new request.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: more than the allowance for your API key in the current one-minute window (100 requests a minute by default, shared by every merchant endpoint the key calls). Wait for `Retry-After` seconds, then retry with the same `Idempotency-Key`.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "rateLimited": {
                    "summary": "Rate limited",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "TOO_MANY_REQUESTS",
                      "message": "Too many requests. Try again in 42 second(s).",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Merchant Webhooks"
        ],
        "summary": "Delete a webhook endpoint",
        "description": "Removes the endpoint. No further event is delivered to it, and deliveries still waiting for a retry end as\n`Failed`. To stop deliveries for a while, pause the endpoint instead (`PUT` with `isActive: false`).\n\n**Scope.** API key with `webhooks:manage`, or a Merchant Portal session with the owner or admin role. Every change is written to your account's audit log.\n\n**Idempotency.** Send an `Idempotency-Key` header (a fresh UUID v4 per attempt) and reuse it unchanged\non retries: a retry with the same key and body replays the original answer with `Idempotent-Replay: true`\ninstead of acting twice. Same key with a different body: `422 IDEMPOTENCY_KEY_REUSED`. First attempt still\nrunning: `409 REQUEST_IN_PROGRESS`, retry shortly.\n\n**Errors.**\n\n| HTTP | Code | Meaning |\n| --- | --- | --- |\n| `401` | — | No credential, or an unknown, expired or revoked API key. No body. |\n| `403` | `FORBIDDEN` | The key lacks `webhooks:manage`, or the portal role is not owner or admin. |\n| `404` | `NOT_FOUND` | No webhook endpoint with this id belongs to you. |\n| `409` | `REQUEST_IN_PROGRESS` | The first attempt with this `Idempotency-Key` is still running. |\n| `422` | `IDEMPOTENCY_KEY_REUSED` | This `Idempotency-Key` was used with a different body. |\n| `429` | `TOO_MANY_REQUESTS` | Rate limited; wait `Retry-After` seconds. |",
        "operationId": "MerchantDeleteWebhook",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Webhook endpoint id, from `GET /api/v1/merchant/webhooks` or the create response.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "description": "A unique key for this attempt, ideally a UUID v4, reused unchanged when you retry it. A retry with the same key and body returns the original response, with `Idempotent-Replay: true`, instead of acting twice. The same key with a different body is `422 IDEMPOTENCY_KEY_REUSED`; a retry while the first attempt is still running is `409 REQUEST_IN_PROGRESS`. Error answers are replayed too, so fix the request and send it with a new key. Keys are scoped to your merchant, the method and the path, and are kept for 24 hours. Optional, but recommended on every POST, PUT and DELETE.",
            "schema": {
              "type": "string"
            },
            "example": "5f0c7a52-3e1b-4c8e-9a2d-6b7f1e0d4c3a"
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted. Code `WEBHOOK_DELETED`; `data` is `null`.",
            "headers": {
              "Idempotent-Replay": {
                "description": "`true` when this response is the stored answer to an earlier request with the same `Idempotency-Key`. Absent otherwise.",
                "schema": {
                  "type": "string",
                  "example": "true"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "deleted": {
                    "summary": "Deleted",
                    "value": {
                      "success": true,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "WEBHOOK_DELETED",
                      "message": "Webhook endpoint deleted",
                      "data": null,
                      "errors": null
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: no credential, or an unknown, expired or revoked API key. A publishable `pk_` key sent as `X-Api-Key` is refused the same way. The answer has no body and carries a `WWW-Authenticate` header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden: the API key lacks `webhooks:manage`, or the Merchant Portal user's role does not allow this action. Code `FORBIDDEN`. A token that is not a merchant credential at all is refused with no body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "missingScope": {
                    "summary": "API key without webhooks:manage",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "FORBIDDEN",
                      "message": "This API key is missing the required scope: webhooks:manage",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND`: no webhook endpoint with this id belongs to you.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "notFound": {
                    "summary": "Unknown endpoint",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "NOT_FOUND",
                      "message": "Webhook endpoint not found",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "`REQUEST_IN_PROGRESS`: the first attempt with this `Idempotency-Key` is still running; retry shortly.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "requestInProgress": {
                    "summary": "Same Idempotency-Key still running",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "REQUEST_IN_PROGRESS",
                      "message": "A request with this Idempotency-Key is still being processed. Wait a moment and retry with the same key.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "`IDEMPOTENCY_KEY_REUSED`: this `Idempotency-Key` was already used with a different body. Use a new key for a new request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "idempotencyKeyReused": {
                    "summary": "Same Idempotency-Key, different body",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "IDEMPOTENCY_KEY_REUSED",
                      "message": "This Idempotency-Key was already used for a different request. Send a new key for a new request.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: more than the allowance for your API key in the current one-minute window (100 requests a minute by default, shared by every merchant endpoint the key calls). Wait for `Retry-After` seconds, then retry with the same `Idempotency-Key`.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "rateLimited": {
                    "summary": "Rate limited",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "TOO_MANY_REQUESTS",
                      "message": "Too many requests. Try again in 42 second(s).",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/merchant/webhooks/{id}/rotate-secret": {
      "post": {
        "tags": [
          "Merchant Webhooks"
        ],
        "summary": "Rotate an endpoint's signing secret",
        "description": "Issues a new signing secret and returns it **only in this response**. For the next 24 hours every delivery to\nthis endpoint is signed with both the new and the previous secret (two `v1` values in `X-Qasimha-Signature`),\nso you can deploy the new secret without dropping a delivery. `previousSecretExpiresAt` says when the old one\nstops.\n\n**When to call it.** On a schedule, when someone with access leaves, or when a secret may have leaked.\n\n**Scope.** API key with `webhooks:manage`, or a Merchant Portal session with the owner or admin role. Every change is written to your account's audit log.\n\n**Idempotency.** Send an `Idempotency-Key` header (a fresh UUID v4 per attempt) and reuse it unchanged\non retries: a retry with the same key and body replays the original answer with `Idempotent-Replay: true`\ninstead of acting twice. Same key with a different body: `422 IDEMPOTENCY_KEY_REUSED`. First attempt still\nrunning: `409 REQUEST_IN_PROGRESS`, retry shortly.\n\n**Errors.**\n\n| HTTP | Code | Meaning |\n| --- | --- | --- |\n| `401` | — | No credential, or an unknown, expired or revoked API key. No body. |\n| `403` | `FORBIDDEN` | The key lacks `webhooks:manage`, or the portal role is not owner or admin. |\n| `404` | `NOT_FOUND` | No webhook endpoint with this id belongs to you. |\n| `409` | `REQUEST_IN_PROGRESS` | The first attempt with this `Idempotency-Key` is still running. |\n| `422` | `IDEMPOTENCY_KEY_REUSED` | This `Idempotency-Key` was used with a different body. |\n| `429` | `TOO_MANY_REQUESTS` | Rate limited; wait `Retry-After` seconds. |",
        "operationId": "MerchantRotateWebhookSecret",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Webhook endpoint id, from `GET /api/v1/merchant/webhooks` or the create response.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "description": "A unique key for this attempt, ideally a UUID v4, reused unchanged when you retry it. A retry with the same key and body returns the original response, with `Idempotent-Replay: true`, instead of acting twice. The same key with a different body is `422 IDEMPOTENCY_KEY_REUSED`; a retry while the first attempt is still running is `409 REQUEST_IN_PROGRESS`. Error answers are replayed too, so fix the request and send it with a new key. Keys are scoped to your merchant, the method and the path, and are kept for 24 hours. Optional, but recommended on every POST, PUT and DELETE.",
            "schema": {
              "type": "string"
            },
            "example": "5f0c7a52-3e1b-4c8e-9a2d-6b7f1e0d4c3a"
          }
        ],
        "responses": {
          "200": {
            "description": "Rotated. Code `SECRET_ROTATED`. `data.secret` is the new secret, shown only now; `previousSecretExpiresAt` is when the old one stops signing.",
            "headers": {
              "Idempotent-Replay": {
                "description": "`true` when this response is the stored answer to an earlier request with the same `Idempotency-Key`. Absent otherwise.",
                "schema": {
                  "type": "string",
                  "example": "true"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MerchantWebhookSecretDtoApiResponse"
                },
                "examples": {
                  "rotated": {
                    "summary": "New secret",
                    "value": {
                      "success": true,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "SECRET_ROTATED",
                      "message": "Webhook secret rotated",
                      "data": {
                        "id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
                        "url": "https://shop.example/webhooks/qasimha",
                        "description": "Production store",
                        "events": [
                          "order.confirmed",
                          "order.refunded"
                        ],
                        "isActive": true,
                        "secret": "whsec_4c1f0e2a9b7d6c5e3f1a0b9c8d7e6f5a4b3c2d1e0f9a3a9f",
                        "secretHint": "3a9f",
                        "previousSecretExpiresAt": "2026-10-21T09:00:00Z",
                        "createdAt": "2026-10-20T09:00:00Z"
                      },
                      "errors": null
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: no credential, or an unknown, expired or revoked API key. A publishable `pk_` key sent as `X-Api-Key` is refused the same way. The answer has no body and carries a `WWW-Authenticate` header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden: the API key lacks `webhooks:manage`, or the Merchant Portal user's role does not allow this action. Code `FORBIDDEN`. A token that is not a merchant credential at all is refused with no body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "missingScope": {
                    "summary": "API key without webhooks:manage",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "FORBIDDEN",
                      "message": "This API key is missing the required scope: webhooks:manage",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND`: no webhook endpoint with this id belongs to you.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "notFound": {
                    "summary": "Unknown endpoint",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "NOT_FOUND",
                      "message": "Webhook endpoint not found",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "`REQUEST_IN_PROGRESS`: the first attempt with this `Idempotency-Key` is still running; retry shortly.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "requestInProgress": {
                    "summary": "Same Idempotency-Key still running",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "REQUEST_IN_PROGRESS",
                      "message": "A request with this Idempotency-Key is still being processed. Wait a moment and retry with the same key.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "`IDEMPOTENCY_KEY_REUSED`: this `Idempotency-Key` was already used with a different body. Use a new key for a new request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "idempotencyKeyReused": {
                    "summary": "Same Idempotency-Key, different body",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "IDEMPOTENCY_KEY_REUSED",
                      "message": "This Idempotency-Key was already used for a different request. Send a new key for a new request.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: more than the allowance for your API key in the current one-minute window (100 requests a minute by default, shared by every merchant endpoint the key calls). Wait for `Retry-After` seconds, then retry with the same `Idempotency-Key`.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "rateLimited": {
                    "summary": "Rate limited",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "TOO_MANY_REQUESTS",
                      "message": "Too many requests. Try again in 42 second(s).",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/merchant/webhooks/{id}/ping": {
      "post": {
        "tags": [
          "Merchant Webhooks"
        ],
        "summary": "Send a test event",
        "description": "Queues a signed `ping` event to this endpoint, even when it is paused or does not subscribe to every event.\nWatch it arrive in your server's logs and in `GET /api/v1/merchant/webhooks/{id}/deliveries`.\n\n**When to call it.** To prove your receiver is reachable and verifies signatures before you go live, and after\nyou rotate a secret.\n\n**Scope.** API key with `webhooks:manage`, or a Merchant Portal session with the owner or admin role. Every change is written to your account's audit log.\n\n**Idempotency.** Send an `Idempotency-Key` header (a fresh UUID v4 per attempt) and reuse it unchanged\non retries: a retry with the same key and body replays the original answer with `Idempotent-Replay: true`\ninstead of acting twice. Same key with a different body: `422 IDEMPOTENCY_KEY_REUSED`. First attempt still\nrunning: `409 REQUEST_IN_PROGRESS`, retry shortly.\n\n**Errors.**\n\n| HTTP | Code | Meaning |\n| --- | --- | --- |\n| `401` | — | No credential, or an unknown, expired or revoked API key. No body. |\n| `403` | `FORBIDDEN` | The key lacks `webhooks:manage`, or the portal role is not owner or admin. |\n| `404` | `NOT_FOUND` | No webhook endpoint with this id belongs to you. |\n| `409` | `REQUEST_IN_PROGRESS` | The first attempt with this `Idempotency-Key` is still running. |\n| `422` | `IDEMPOTENCY_KEY_REUSED` | This `Idempotency-Key` was used with a different body. |\n| `429` | `TOO_MANY_REQUESTS` | Rate limited; wait `Retry-After` seconds. |",
        "operationId": "MerchantPingWebhook",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Webhook endpoint id, from `GET /api/v1/merchant/webhooks` or the create response.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "description": "A unique key for this attempt, ideally a UUID v4, reused unchanged when you retry it. A retry with the same key and body returns the original response, with `Idempotent-Replay: true`, instead of acting twice. The same key with a different body is `422 IDEMPOTENCY_KEY_REUSED`; a retry while the first attempt is still running is `409 REQUEST_IN_PROGRESS`. Error answers are replayed too, so fix the request and send it with a new key. Keys are scoped to your merchant, the method and the path, and are kept for 24 hours. Optional, but recommended on every POST, PUT and DELETE.",
            "schema": {
              "type": "string"
            },
            "example": "5f0c7a52-3e1b-4c8e-9a2d-6b7f1e0d4c3a"
          }
        ],
        "responses": {
          "202": {
            "description": "Queued. Code `PING_QUEUED`. The `ping` is delivered within seconds and appears in the delivery log.",
            "headers": {
              "Idempotent-Replay": {
                "description": "`true` when this response is the stored answer to an earlier request with the same `Idempotency-Key`. Absent otherwise.",
                "schema": {
                  "type": "string",
                  "example": "true"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MerchantWebhookPingResultDtoApiResponse"
                },
                "examples": {
                  "queued": {
                    "summary": "Ping queued",
                    "value": {
                      "success": true,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "PING_QUEUED",
                      "message": "Test event queued",
                      "data": {
                        "eventId": "evt_9f8c1d2e3a4b5c6d7e8f90a1b2c3d4e5",
                        "endpointId": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"
                      },
                      "errors": null
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: no credential, or an unknown, expired or revoked API key. A publishable `pk_` key sent as `X-Api-Key` is refused the same way. The answer has no body and carries a `WWW-Authenticate` header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden: the API key lacks `webhooks:manage`, or the Merchant Portal user's role does not allow this action. Code `FORBIDDEN`. A token that is not a merchant credential at all is refused with no body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "missingScope": {
                    "summary": "API key without webhooks:manage",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "FORBIDDEN",
                      "message": "This API key is missing the required scope: webhooks:manage",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND`: no webhook endpoint with this id belongs to you.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "notFound": {
                    "summary": "Unknown endpoint",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "NOT_FOUND",
                      "message": "Webhook endpoint not found",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "`REQUEST_IN_PROGRESS`: the first attempt with this `Idempotency-Key` is still running; retry shortly.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "requestInProgress": {
                    "summary": "Same Idempotency-Key still running",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "REQUEST_IN_PROGRESS",
                      "message": "A request with this Idempotency-Key is still being processed. Wait a moment and retry with the same key.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "`IDEMPOTENCY_KEY_REUSED`: this `Idempotency-Key` was already used with a different body. Use a new key for a new request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "idempotencyKeyReused": {
                    "summary": "Same Idempotency-Key, different body",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "IDEMPOTENCY_KEY_REUSED",
                      "message": "This Idempotency-Key was already used for a different request. Send a new key for a new request.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: more than the allowance for your API key in the current one-minute window (100 requests a minute by default, shared by every merchant endpoint the key calls). Wait for `Retry-After` seconds, then retry with the same `Idempotency-Key`.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "rateLimited": {
                    "summary": "Rate limited",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "TOO_MANY_REQUESTS",
                      "message": "Too many requests. Try again in 42 second(s).",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/merchant/webhooks/{id}/deliveries": {
      "get": {
        "tags": [
          "Merchant Webhooks"
        ],
        "summary": "List an endpoint's deliveries",
        "description": "Returns the delivery log of one endpoint, newest first: one row per event, with the number of attempts,\nthe last HTTP status and error, the first 1000 characters of your server's last response, and when the next\nretry is due. Rows are kept for 30 days.\n\n**When to call it.** To debug a receiver, or to find failed deliveries to replay.\n\n**Scope.** API key with `webhooks:manage`, or a Merchant Portal session with the owner or admin role. Every change is written to your account's audit log.\n\n**Filters.** `status` is `Pending`, `Succeeded` or `Failed`; `page` starts at 1; `pageSize` defaults to 20 and\nis capped at 100.\n\n**Errors.**\n\n| HTTP | Code | Meaning |\n| --- | --- | --- |\n| `400` | `INVALID_STATUS` | `status` is not `Pending`, `Succeeded` or `Failed`. |\n| `400` | `VALIDATION_ERROR` | `page` below 1, or a negative `pageSize`. |\n| `401` | — | No credential, or an unknown, expired or revoked API key. No body. |\n| `403` | `FORBIDDEN` | The key lacks `webhooks:manage`, or the portal role is not owner or admin. |\n| `404` | `NOT_FOUND` | No webhook endpoint with this id belongs to you. |\n| `429` | `TOO_MANY_REQUESTS` | Rate limited; wait `Retry-After` seconds. |",
        "operationId": "MerchantListWebhookDeliveries",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Webhook endpoint id.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"
          },
          {
            "name": "status",
            "in": "query",
            "description": "Only deliveries in this state: `Pending`, `Succeeded` or `Failed` (case-insensitive). Anything else is `400 INVALID_STATUS`. Leave it out for all.",
            "schema": {
              "type": "string"
            },
            "example": "Failed"
          },
          {
            "name": "page",
            "in": "query",
            "description": "Page number, starting at 1 (the default). 0 or less is `400 VALIDATION_ERROR`.",
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 1
            },
            "example": 1
          },
          {
            "name": "pageSize",
            "in": "query",
            "description": "Rows per page, default 20, capped at 100. A negative value is `400 VALIDATION_ERROR`.",
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 20
            },
            "example": 20
          }
        ],
        "responses": {
          "200": {
            "description": "One page of the delivery log, newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MerchantWebhookDeliveryPageDtoApiResponse"
                },
                "examples": {
                  "page": {
                    "summary": "One delivery",
                    "value": {
                      "success": true,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "SUCCESS",
                      "message": "Deliveries retrieved",
                      "data": {
                        "data": [
                          {
                            "id": "f0e1d2c3-b4a5-4968-8776-655443322110",
                            "eventId": "9f8c1d2e-3a4b-5c6d-7e8f-90a1b2c3d4e5",
                            "eventType": "order.confirmed",
                            "orderId": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61",
                            "orderReferenceId": "MERCHANT-ORDER-12345",
                            "status": "Succeeded",
                            "attempts": 1,
                            "maxAttempts": 6,
                            "nextAttemptAt": null,
                            "lastAttemptAt": "2026-10-20T09:25:02Z",
                            "lastStatusCode": 200,
                            "lastError": null,
                            "responseSnippet": "{\"received\":true}",
                            "durationMs": 184,
                            "succeededAt": "2026-10-20T09:25:02Z",
                            "replayOfId": null,
                            "createdAt": "2026-10-20T09:25:01Z"
                          }
                        ],
                        "pagination": {
                          "totalItems": 42,
                          "page": 1,
                          "pageSize": 1,
                          "totalPages": 42
                        }
                      },
                      "errors": null
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request. `INVALID_STATUS`: not `Pending`, `Succeeded` or `Failed`. `VALIDATION_ERROR`: `page` below 1 or a negative `pageSize`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "invalidStatus": {
                    "summary": "Unknown status",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "INVALID_STATUS",
                      "message": "Unknown delivery status 'Done'. Use Pending, Succeeded or Failed.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: no credential, or an unknown, expired or revoked API key. A publishable `pk_` key sent as `X-Api-Key` is refused the same way. The answer has no body and carries a `WWW-Authenticate` header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden: the API key lacks `webhooks:manage`, or the Merchant Portal user's role does not allow this action. Code `FORBIDDEN`. A token that is not a merchant credential at all is refused with no body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "missingScope": {
                    "summary": "API key without webhooks:manage",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "FORBIDDEN",
                      "message": "This API key is missing the required scope: webhooks:manage",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND`: no webhook endpoint with this id belongs to you.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "notFound": {
                    "summary": "Unknown endpoint",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "NOT_FOUND",
                      "message": "Webhook endpoint not found",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: more than the allowance for your API key in the current one-minute window (100 requests a minute by default, shared by every merchant endpoint the key calls). Wait for `Retry-After` seconds, then retry with the same `Idempotency-Key`.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "rateLimited": {
                    "summary": "Rate limited",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "TOO_MANY_REQUESTS",
                      "message": "Too many requests. Try again in 42 second(s).",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/merchant/webhooks/{id}/deliveries/{deliveryId}/replay": {
      "post": {
        "tags": [
          "Merchant Webhooks"
        ],
        "summary": "Replay a delivery",
        "description": "Queues the same event to the endpoint again as a new delivery, with the same event id (so your\nde-duplication still works) and a fresh signature. The original row is kept; the new one has `replayOfId`\nset. The event goes to the endpoint's current URL.\n\n**When to call it.** After you fixed a receiver that failed, for each `Failed` delivery you need.\n\n**Scope.** API key with `webhooks:manage`, or a Merchant Portal session with the owner or admin role. Every change is written to your account's audit log.\n\n**Idempotency.** Send an `Idempotency-Key` header (a fresh UUID v4 per attempt) and reuse it unchanged\non retries: a retry with the same key and body replays the original answer with `Idempotent-Replay: true`\ninstead of acting twice. Same key with a different body: `422 IDEMPOTENCY_KEY_REUSED`. First attempt still\nrunning: `409 REQUEST_IN_PROGRESS`, retry shortly.\n\n**Errors.**\n\n| HTTP | Code | Meaning |\n| --- | --- | --- |\n| `401` | — | No credential, or an unknown, expired or revoked API key. No body. |\n| `403` | `FORBIDDEN` | The key lacks `webhooks:manage`, or the portal role is not owner or admin. |\n| `404` | `NOT_FOUND` | The endpoint is not yours, or the delivery does not belong to this endpoint. |\n| `409` | `REQUEST_IN_PROGRESS` | The first attempt with this `Idempotency-Key` is still running. |\n| `422` | `IDEMPOTENCY_KEY_REUSED` | This `Idempotency-Key` was used with a different body. |\n| `429` | `TOO_MANY_REQUESTS` | Rate limited; wait `Retry-After` seconds. |",
        "operationId": "MerchantReplayWebhookDelivery",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Webhook endpoint id.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"
          },
          {
            "name": "deliveryId",
            "in": "path",
            "description": "The delivery to send again, from the delivery log of this endpoint.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "example": "f0e1d2c3-b4a5-4968-8776-655443322110"
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "description": "A unique key for this attempt, ideally a UUID v4, reused unchanged when you retry it. A retry with the same key and body returns the original response, with `Idempotent-Replay: true`, instead of acting twice. The same key with a different body is `422 IDEMPOTENCY_KEY_REUSED`; a retry while the first attempt is still running is `409 REQUEST_IN_PROGRESS`. Error answers are replayed too, so fix the request and send it with a new key. Keys are scoped to your merchant, the method and the path, and are kept for 24 hours. Optional, but recommended on every POST, PUT and DELETE.",
            "schema": {
              "type": "string"
            },
            "example": "5f0c7a52-3e1b-4c8e-9a2d-6b7f1e0d4c3a"
          }
        ],
        "responses": {
          "202": {
            "description": "Queued. Code `DELIVERY_REPLAYED`; `data` is the new delivery (`status: \"Pending\"`, `replayOfId` = the original).",
            "headers": {
              "Idempotent-Replay": {
                "description": "`true` when this response is the stored answer to an earlier request with the same `Idempotency-Key`. Absent otherwise.",
                "schema": {
                  "type": "string",
                  "example": "true"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MerchantWebhookDeliveryDtoApiResponse"
                },
                "examples": {
                  "replayed": {
                    "summary": "New delivery",
                    "value": {
                      "success": true,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "DELIVERY_REPLAYED",
                      "message": "Delivery queued again",
                      "data": {
                        "id": "0a9b8c7d-6e5f-4a3b-9c2d-1e0f9a8b7c6d",
                        "eventId": "9f8c1d2e-3a4b-5c6d-7e8f-90a1b2c3d4e5",
                        "eventType": "order.confirmed",
                        "orderId": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61",
                        "orderReferenceId": "MERCHANT-ORDER-12345",
                        "status": "Pending",
                        "attempts": 0,
                        "maxAttempts": 6,
                        "nextAttemptAt": "2026-10-21T08:00:00Z",
                        "lastAttemptAt": null,
                        "lastStatusCode": null,
                        "lastError": null,
                        "responseSnippet": null,
                        "durationMs": null,
                        "succeededAt": null,
                        "replayOfId": "f0e1d2c3-b4a5-4968-8776-655443322110",
                        "createdAt": "2026-10-21T08:00:00Z"
                      },
                      "errors": null
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: no credential, or an unknown, expired or revoked API key. A publishable `pk_` key sent as `X-Api-Key` is refused the same way. The answer has no body and carries a `WWW-Authenticate` header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden: the API key lacks `webhooks:manage`, or the Merchant Portal user's role does not allow this action. Code `FORBIDDEN`. A token that is not a merchant credential at all is refused with no body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "missingScope": {
                    "summary": "API key without webhooks:manage",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "FORBIDDEN",
                      "message": "This API key is missing the required scope: webhooks:manage",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND`: the endpoint is not yours, or the delivery does not belong to this endpoint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "endpointNotFound": {
                    "summary": "Unknown endpoint",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "NOT_FOUND",
                      "message": "Webhook endpoint not found",
                      "data": null,
                      "errors": [ ]
                    }
                  },
                  "deliveryNotFound": {
                    "summary": "Unknown delivery",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "NOT_FOUND",
                      "message": "Delivery not found",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "`REQUEST_IN_PROGRESS`: the first attempt with this `Idempotency-Key` is still running; retry shortly.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "requestInProgress": {
                    "summary": "Same Idempotency-Key still running",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "REQUEST_IN_PROGRESS",
                      "message": "A request with this Idempotency-Key is still being processed. Wait a moment and retry with the same key.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "`IDEMPOTENCY_KEY_REUSED`: this `Idempotency-Key` was already used with a different body. Use a new key for a new request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "idempotencyKeyReused": {
                    "summary": "Same Idempotency-Key, different body",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "IDEMPOTENCY_KEY_REUSED",
                      "message": "This Idempotency-Key was already used for a different request. Send a new key for a new request.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: more than the allowance for your API key in the current one-minute window (100 requests a minute by default, shared by every merchant endpoint the key calls). Wait for `Retry-After` seconds, then retry with the same `Idempotency-Key`.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "rateLimited": {
                    "summary": "Rate limited",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "TOO_MANY_REQUESTS",
                      "message": "Too many requests. Try again in 42 second(s).",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/pay/session/create": {
      "post": {
        "tags": [
          "Payment Sessions"
        ],
        "summary": "Create an online payment session",
        "description": "Creates an instalment order for your customer's basket and returns `paymentUrl`, the hosted checkout page\nto redirect them to. This is the first call of every online checkout.\n\n**When to call it.** When the customer picks Qasimha at your checkout. Store the returned `installmentId`\n(the Qasimha order id) against your `orderReferenceId`: every later order call takes it as `{id}`.\n\n**Scope.** API key with `orders:write`, or a Merchant Portal session with any role except viewer.\n\n**What happens next.** The order is created as `AwaitingCheckout`; nothing is decided at this call. On the\nhosted page the customer verifies their phone with a one-time code, verifies their identity (Nafath), is\nassessed, chooses a plan, accepts the contract and pays the first instalment. The page is valid for two\nhours.\n\n| Outcome | Order status | Webhook |\n| --- | --- | --- |\n| The customer finished checkout | `Confirmed` | `order.confirmed` |\n| The credit decision declined the customer | `Declined` | `order.declined` |\n| Not finished within two hours | `Expired` | `order.expired` |\n\nFrom `Confirmed` on, the order counts toward the not-paid-yet balance of\n`GET /api/v1/merchant/settlements/overview`: you are paid the order total less your MDR fee and VAT on it.\n\nThe page opens in `language`, else the `Accept-Language` header, else Arabic.\n\n**The basket must add up**, to within 0.01:\n\n- `items[].totalAmount = quantity × unitPrice − items[].discountAmount`\n- `totalAmount = Σ items[].totalAmount + taxAmount + shippingAmount − discountAmount`\n\n`quantity` is at least 1, no amount is negative, `currencyCode` is `SAR`. Every line field is stored as sent.\n\n**Fields you can leave out.** `merchantId` (ignored for API-key and portal callers: the merchant comes from\nyour credential), `decisionFeatures` (always discarded), `checkoutBaseUrl` (set by Qasimha), `riskAssessment`\nand `paymentType` (accepted for compatibility, not used). Of the addresses, `firstName`, `lastName`, `line1`,\n`city` and `countryCode` are stored.\n\n**Return URLs (optional).** `merchantUrl { success, failure, cancel }` is where the hosted page sends the\ncustomer at the end, with `orderId`, `orderReferenceId` and `status` appended to the URL's query string\n(`confirmed` on success; `declined`, `expired` or `failed` on failure; `cancelled`). Absolute URLs, at most\n1024 characters, no `#fragment`; `https`, `http` only on `localhost` / `127.0.0.1`, or a custom app scheme\n(`myshop://…`). `success` and `failure` go together; `cancel` defaults to `failure`; `notification` is\nreserved and refused. A return is a navigation, not a proof: confirm with `GET /api/v1/merchant/orders/{id}`\nbefore you treat the order as paid.\n\n**Idempotency.** Send an `Idempotency-Key` header (a fresh UUID v4 per attempt) and reuse it unchanged\non retries: a retry with the same key and body replays the original answer with `Idempotent-Replay: true`\ninstead of acting twice. Same key with a different body: `422 IDEMPOTENCY_KEY_REUSED`. First attempt still\nrunning: `409 REQUEST_IN_PROGRESS`, retry shortly. Without a key, a double-submitted cart answers `409 DUPLICATE_ORDER_REFERENCE` with the open checkout\nin `data`.\n\n**Errors.**\n\n| HTTP | Code | Meaning |\n| --- | --- | --- |\n| `400` | `VALIDATION_ERROR` | One or more fields failed; `errors` lists each one (the basket does not add up, a bad line, a currency other than SAR, a malformed `nationalId` or return URL). |\n| `400` | `INVALID_REQUEST` | No request body. |\n| `400` | `MERCHANT_REQUIRED` | A back-office token that named no merchant. Never returned to an API key. |\n| `401` | — | No credential, or an unknown, expired or revoked API key. No body. |\n| `403` | `FORBIDDEN` | The key lacks `orders:write`, or the portal user is a viewer. |\n| `409` | `DUPLICATE_ORDER_REFERENCE` | `orderReferenceId` is already used. When the order is yours, `data` has its `installmentId` and `paymentUrl`: send the customer back to that checkout. |\n| `409` | `MERCHANT_SUSPENDED` | Your account is suspended and cannot create orders. |\n| `409` | `REQUEST_IN_PROGRESS` | The first attempt with this `Idempotency-Key` is still running. |\n| `422` | `IDEMPOTENCY_KEY_REUSED` | This `Idempotency-Key` was used with a different body. |\n| `422` | `ORDER_DECLINED` | Not returned by the hosted checkout. Only a deployment on the earlier provider journey decides at creation; `data` then carries the declined order. |\n| `429` | `TOO_MANY_REQUESTS` | Rate limited; wait `Retry-After` seconds. |",
        "operationId": "CreateOnlinePaymentSession",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "description": "A unique key for this attempt, ideally a UUID v4, reused unchanged when you retry it. A retry with the same key and body returns the original response, with `Idempotent-Replay: true`, instead of acting twice. The same key with a different body is `422 IDEMPOTENCY_KEY_REUSED`; a retry while the first attempt is still running is `409 REQUEST_IN_PROGRESS`. Error answers are replayed too, so fix the request and send it with a new key. Keys are scoped to your merchant, the method and the path, and are kept for 24 hours. Optional, but recommended on every POST, PUT and DELETE.",
            "schema": {
              "type": "string"
            },
            "example": "5f0c7a52-3e1b-4c8e-9a2d-6b7f1e0d4c3a"
          },
          {
            "name": "Accept-Language",
            "in": "header",
            "description": "Fallback language for the customer (`ar` or `en`) when the body has no `language`; Arabic when neither says otherwise.",
            "schema": {
              "type": "string"
            },
            "example": "ar"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateOnlineSessionCommand"
              },
              "examples": {
                "fullBasket": {
                  "summary": "Basket with tax, shipping and return URLs",
                  "value": {
                    "orderReferenceId": "MERCHANT-ORDER-12345",
                    "totalAmount": 1278.5,
                    "taxAmount": 163.5,
                    "shippingAmount": 25,
                    "discountAmount": 0,
                    "currencyCode": "SAR",
                    "countryCode": "SA",
                    "description": "Order #12345 from the web store",
                    "instalments": 4,
                    "items": [
                      {
                        "referenceId": "LINE-1",
                        "name": "Wireless Headphones",
                        "type": "physical",
                        "sku": "SKU-1001",
                        "quantity": 1,
                        "unitPrice": 1000,
                        "totalAmount": 1000,
                        "taxAmount": 150,
                        "discountAmount": 0,
                        "imageUrl": "https://shop.example/images/sku-1001.jpg"
                      },
                      {
                        "referenceId": "LINE-2",
                        "name": "Protective Case",
                        "type": "physical",
                        "sku": "SKU-2002",
                        "quantity": 2,
                        "unitPrice": 50,
                        "totalAmount": 90,
                        "taxAmount": 13.5,
                        "discountAmount": 10,
                        "imageUrl": null
                      }
                    ],
                    "consumer": {
                      "firstName": "Noura",
                      "lastName": "Al-Shammari",
                      "phoneNumber": "+966512345678",
                      "email": "noura@example.com"
                    },
                    "shippingAddress": {
                      "firstName": "Noura",
                      "lastName": "Al-Shammari",
                      "line1": "King Fahd Road 1234",
                      "line2": "Building 7, Apartment 12",
                      "city": "Riyadh",
                      "region": "Riyadh Province",
                      "postalCode": "12271",
                      "countryCode": "SA",
                      "phoneNumber": "+966512345678"
                    },
                    "merchantUrl": {
                      "success": "https://shop.example/checkout/qasimha/success",
                      "failure": "https://shop.example/checkout/qasimha/failure",
                      "cancel": "https://shop.example/checkout/qasimha/cancel"
                    },
                    "language": "ar"
                  }
                },
                "minimal": {
                  "summary": "Required fields only",
                  "value": {
                    "orderReferenceId": "MERCHANT-ORDER-12346",
                    "totalAmount": 250,
                    "taxAmount": 0,
                    "shippingAmount": 0,
                    "discountAmount": 0,
                    "currencyCode": "SAR",
                    "countryCode": "SA",
                    "items": [
                      {
                        "referenceId": "SKU-3003",
                        "name": "Smart Watch Strap",
                        "type": "physical",
                        "quantity": 1,
                        "unitPrice": 250,
                        "totalAmount": 250,
                        "taxAmount": 0,
                        "discountAmount": 0
                      }
                    ],
                    "consumer": {
                      "firstName": "Faisal",
                      "lastName": "Al-Harbi",
                      "phoneNumber": "+966598765432"
                    },
                    "shippingAddress": {
                      "firstName": "Faisal",
                      "lastName": "Al-Harbi",
                      "line1": "Prince Sultan Street 88",
                      "city": "Jeddah",
                      "countryCode": "SA"
                    }
                  }
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created. The order is `AwaitingCheckout`; redirect the customer to `paymentUrl`.",
            "headers": {
              "Idempotent-Replay": {
                "description": "`true` when this response is the stored answer to an earlier request with the same `Idempotency-Key`. Absent otherwise.",
                "schema": {
                  "type": "string",
                  "example": "true"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateOnlineSessionResultApiResponse"
                },
                "examples": {
                  "created": {
                    "summary": "Session created",
                    "value": {
                      "success": true,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "SESSION_CREATED",
                      "message": "SESSION_CREATED",
                      "data": {
                        "installmentId": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61",
                        "providerSessionId": "",
                        "paymentUrl": "https://.../pay/3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61?lang=ar",
                        "status": "AwaitingCheckout"
                      },
                      "errors": null
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request. `VALIDATION_ERROR` lists every failed field in `errors` (the basket does not add up, a line is invalid, a currency other than SAR, a malformed `nationalId` or return URL). `INVALID_REQUEST`: no body. `MERCHANT_REQUIRED`: a back-office token that named no merchant.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "basketDoesNotAddUp": {
                    "summary": "totalAmount does not match the basket",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "VALIDATION_ERROR",
                      "message": "One or more validation errors occurred.",
                      "data": null,
                      "errors": [
                        {
                          "field": "TotalAmount",
                          "message": "totalAmount 1300.00 does not equal the basket: items 1090.00 + tax 163.50 + shipping 25.00 − discount 0.00 = 1278.50."
                        }
                      ]
                    }
                  },
                  "invalidFields": {
                    "summary": "Invalid line and currency",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "VALIDATION_ERROR",
                      "message": "One or more validation errors occurred.",
                      "data": null,
                      "errors": [
                        {
                          "field": "Items[1].Quantity",
                          "message": "Item quantity must be greater than zero."
                        },
                        {
                          "field": "CurrencyCode",
                          "message": "Only SAR is supported."
                        }
                      ]
                    }
                  },
                  "missingBody": {
                    "summary": "No request body",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "INVALID_REQUEST",
                      "message": "Request body is required.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: no credential, or an unknown, expired or revoked API key. A publishable `pk_` key sent as `X-Api-Key` is refused the same way. The answer has no body and carries a `WWW-Authenticate` header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden: the API key lacks `orders:write`, or the Merchant Portal user's role does not allow this action. Code `FORBIDDEN`. A token that is not a merchant credential at all is refused with no body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "missingScope": {
                    "summary": "API key without orders:write",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "FORBIDDEN",
                      "message": "This API key lacks the orders:write scope.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "Conflict. `DUPLICATE_ORDER_REFERENCE`: the `orderReferenceId` is already used; when the existing order is yours, `data` carries its `installmentId` and `paymentUrl` so you can send the customer back to the open checkout. `MERCHANT_SUSPENDED`: your account is suspended and cannot create orders. `REQUEST_IN_PROGRESS`: the first attempt with this `Idempotency-Key` is still running; retry shortly.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "duplicateReference": {
                    "summary": "Reference already used by one of your orders",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "DUPLICATE_ORDER_REFERENCE",
                      "message": "You already created an order with reference 'MERCHANT-ORDER-12345'.",
                      "data": {
                        "installmentId": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61",
                        "paymentUrl": "https://.../pay/3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61?lang=ar"
                      },
                      "errors": [ ]
                    }
                  },
                  "merchantSuspended": {
                    "summary": "Account suspended",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "MERCHANT_SUSPENDED",
                      "message": "This merchant is currently suspended and cannot create new orders.",
                      "data": null,
                      "errors": [ ]
                    }
                  },
                  "requestInProgress": {
                    "summary": "Same Idempotency-Key still running",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "REQUEST_IN_PROGRESS",
                      "message": "A request with this Idempotency-Key is still being processed. Wait a moment and retry with the same key.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "Unprocessable. `IDEMPOTENCY_KEY_REUSED`: the `Idempotency-Key` was used with a different body. `ORDER_DECLINED` is not returned by the hosted checkout, where the credit decision is made on the page; it exists only for deployments on the earlier provider journey, with the created order in `data`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateOnlineSessionResultApiResponse"
                },
                "examples": {
                  "idempotencyKeyReused": {
                    "summary": "Same Idempotency-Key, different body",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "IDEMPOTENCY_KEY_REUSED",
                      "message": "This Idempotency-Key was already used for a different request. Send a new key for a new request.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: more than the allowance for your API key in the current one-minute window (100 requests a minute by default, shared by every merchant endpoint the key calls). Wait for `Retry-After` seconds, then retry with the same `Idempotency-Key`.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "rateLimited": {
                    "summary": "Rate limited",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "TOO_MANY_REQUESTS",
                      "message": "Too many requests. Try again in 42 second(s).",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/pay/pos/create": {
      "post": {
        "tags": [
          "Payment Sessions"
        ],
        "summary": "Create an in-store (POS) checkout order",
        "description": "Creates an order for a sale at your till and returns `checkoutUrl`, the hosted checkout page the customer\ncompletes on their own phone. Show it as a QR code on the terminal or send it to them.\n\n**When to call it.** At the point of sale, when the customer chooses to pay with Qasimha. Store the\nreturned `installmentId` against your `orderReferenceId`.\n\n**Scope.** API key with `orders:write`, or a Merchant Portal session with any role except viewer.\n\n**What happens next.** The order is created as `AwaitingCheckout`; nothing is decided at this call. On the\nhosted page the customer verifies their phone with a one-time code, verifies their identity (Nafath), is\nassessed, chooses a plan, accepts the contract and pays the first instalment. The page is valid for two\nhours.\n\n| Outcome | Order status | Webhook |\n| --- | --- | --- |\n| The customer finished checkout | `Confirmed` | `order.confirmed` |\n| The credit decision declined the customer | `Declined` | `order.declined` |\n| Not finished within two hours | `Expired` | `order.expired` |\n\nFrom `Confirmed` on, the order counts toward the not-paid-yet balance of\n`GET /api/v1/merchant/settlements/overview`: you are paid the order total less your MDR fee and VAT on it.\n\n**The basket must add up**, to within 0.01: `items[].totalAmount = quantity × unitPrice − discountAmount`\nand `totalAmount = Σ items[].totalAmount`. There are no order-level tax, shipping or discount fields; the\norder's tax is the sum of the lines' `taxAmount`. Send `currencyCode: \"SAR\"`.\n\n**Customer.** `consumer.email` is required (`400 VALIDATION_ERROR` without it): a returning customer is\nrecognised by it. The phone pre-fills the hosted page.\n\n**Fields you can leave out.** `merchantId` is ignored for API-key and portal callers.\n\n**Unique reference.** `orderReferenceId` must be unique across your orders. A reused reference answers\n`409 DUPLICATE_ORDER_REFERENCE`; when the order is yours, `data` has its `installmentId` and `checkoutUrl`.\n\n**Return URLs (optional).** `merchantUrl { success, failure, cancel }` is where the hosted page sends the\ncustomer at the end, with `orderId`, `orderReferenceId` and `status` appended to the URL's query string\n(`confirmed` on success; `declined`, `expired` or `failed` on failure; `cancelled`). Absolute URLs, at most\n1024 characters, no `#fragment`; `https`, `http` only on `localhost` / `127.0.0.1`, or a custom app scheme\n(`myshop://…`). `success` and `failure` go together; `cancel` defaults to `failure`; `notification` is\nreserved and refused. A return is a navigation, not a proof: confirm with `GET /api/v1/merchant/orders/{id}`\nbefore you treat the order as paid.\n\n**Idempotency.** Send an `Idempotency-Key` header (a fresh UUID v4 per attempt) and reuse it unchanged\non retries: a retry with the same key and body replays the original answer with `Idempotent-Replay: true`\ninstead of acting twice. Same key with a different body: `422 IDEMPOTENCY_KEY_REUSED`. First attempt still\nrunning: `409 REQUEST_IN_PROGRESS`, retry shortly.\n\n**Errors.**\n\n| HTTP | Code | Meaning |\n| --- | --- | --- |\n| `400` | `VALIDATION_ERROR` | One or more fields failed; `errors` lists each one (the basket does not add up, a bad line, no `consumer.email`). |\n| `400` | `INVALID_REQUEST` | No request body. |\n| `400` | `MERCHANT_REQUIRED` | A back-office token that named no merchant. Never returned to an API key. |\n| `401` | — | No credential, or an unknown, expired or revoked API key. No body. |\n| `403` | `FORBIDDEN` | The key lacks `orders:write`, or the portal user is a viewer. |\n| `409` | `DUPLICATE_ORDER_REFERENCE` | `orderReferenceId` is already used. When the order is yours, `data` has its `installmentId` and `checkoutUrl`. |\n| `409` | `MERCHANT_SUSPENDED` | Your account is suspended and cannot create orders. |\n| `409` | `REQUEST_IN_PROGRESS` | The first attempt with this `Idempotency-Key` is still running. |\n| `422` | `IDEMPOTENCY_KEY_REUSED` | This `Idempotency-Key` was used with a different body. |\n| `429` | `TOO_MANY_REQUESTS` | Rate limited; wait `Retry-After` seconds. |",
        "operationId": "CreatePosSession",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "description": "A unique key for this attempt, ideally a UUID v4, reused unchanged when you retry it. A retry with the same key and body returns the original response, with `Idempotent-Replay: true`, instead of acting twice. The same key with a different body is `422 IDEMPOTENCY_KEY_REUSED`; a retry while the first attempt is still running is `409 REQUEST_IN_PROGRESS`. Error answers are replayed too, so fix the request and send it with a new key. Keys are scoped to your merchant, the method and the path, and are kept for 24 hours. Optional, but recommended on every POST, PUT and DELETE.",
            "schema": {
              "type": "string"
            },
            "example": "5f0c7a52-3e1b-4c8e-9a2d-6b7f1e0d4c3a"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreatePosSessionCommand"
              },
              "examples": {
                "inStore": {
                  "summary": "Two-line basket at the till",
                  "value": {
                    "orderReferenceId": "POS-STORE7-000123",
                    "totalAmount": 1150,
                    "currencyCode": "SAR",
                    "countryCode": "SA",
                    "items": [
                      {
                        "referenceId": "SKU-1001",
                        "name": "Wireless Headphones",
                        "type": "physical",
                        "sku": "SKU-1001",
                        "quantity": 1,
                        "unitPrice": 1000,
                        "totalAmount": 1000,
                        "taxAmount": 150,
                        "discountAmount": 0
                      },
                      {
                        "referenceId": "SKU-2002",
                        "name": "Protective Case",
                        "type": "physical",
                        "sku": "SKU-2002",
                        "quantity": 3,
                        "unitPrice": 50,
                        "totalAmount": 150,
                        "taxAmount": 22.5,
                        "discountAmount": 0
                      }
                    ],
                    "consumer": {
                      "firstName": "Noura",
                      "lastName": "Al-Shammari",
                      "phoneNumber": "+966512345678",
                      "email": "noura@example.com"
                    },
                    "merchantUrl": {
                      "success": "myshop://checkout/success",
                      "failure": "myshop://checkout/failure"
                    }
                  }
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created. The order is `AwaitingCheckout`; show `checkoutUrl` to the customer as a QR code or send it to them.",
            "headers": {
              "Idempotent-Replay": {
                "description": "`true` when this response is the stored answer to an earlier request with the same `Idempotency-Key`. Absent otherwise.",
                "schema": {
                  "type": "string",
                  "example": "true"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreatePosSessionResultApiResponse"
                },
                "examples": {
                  "created": {
                    "summary": "POS order created",
                    "value": {
                      "success": true,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "SESSION_CREATED",
                      "message": "SESSION_CREATED",
                      "data": {
                        "installmentId": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61",
                        "providerSessionId": "",
                        "checkoutUrl": "https://.../pay/3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61",
                        "status": "AwaitingCheckout"
                      },
                      "errors": null
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request. `VALIDATION_ERROR` (field details in `errors`: the basket does not add up, a bad line, no `consumer.email`), `INVALID_REQUEST` (no body), `MERCHANT_REQUIRED` (a back-office token that named no merchant).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "validation": {
                    "summary": "Basket does not add up",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "VALIDATION_ERROR",
                      "message": "One or more validation errors occurred.",
                      "data": null,
                      "errors": [
                        {
                          "field": "TotalAmount",
                          "message": "totalAmount 1200.00 does not equal the basket: items 1150.00 + tax 0.00 + shipping 0.00 − discount 0.00 = 1150.00."
                        }
                      ]
                    }
                  },
                  "missingEmail": {
                    "summary": "No consumer.email",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "VALIDATION_ERROR",
                      "message": "One or more validation errors occurred.",
                      "data": null,
                      "errors": [
                        {
                          "field": "Consumer.Email",
                          "message": "Consumer email is required"
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: no credential, or an unknown, expired or revoked API key. A publishable `pk_` key sent as `X-Api-Key` is refused the same way. The answer has no body and carries a `WWW-Authenticate` header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden: the API key lacks `orders:write`, or the Merchant Portal user's role does not allow this action. Code `FORBIDDEN`. A token that is not a merchant credential at all is refused with no body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "missingScope": {
                    "summary": "API key without orders:write",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "FORBIDDEN",
                      "message": "This API key lacks the orders:write scope.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "Conflict. `DUPLICATE_ORDER_REFERENCE`: the `orderReferenceId` is already used; when the existing order is yours, `data` carries its `installmentId` and `checkoutUrl` so the customer can finish the open checkout. `MERCHANT_SUSPENDED`: your account is suspended. `REQUEST_IN_PROGRESS`: the first attempt with this `Idempotency-Key` is still running.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "duplicateReference": {
                    "summary": "Reference already used by one of your orders",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "DUPLICATE_ORDER_REFERENCE",
                      "message": "You already created an order with reference 'POS-STORE7-000123'.",
                      "data": {
                        "installmentId": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61",
                        "checkoutUrl": "https://.../pay/3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61"
                      },
                      "errors": [ ]
                    }
                  },
                  "merchantSuspended": {
                    "summary": "Account suspended",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "MERCHANT_SUSPENDED",
                      "message": "This merchant is currently suspended and cannot create new orders.",
                      "data": null,
                      "errors": [ ]
                    }
                  },
                  "requestInProgress": {
                    "summary": "Same Idempotency-Key still running",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "REQUEST_IN_PROGRESS",
                      "message": "A request with this Idempotency-Key is still being processed. Wait a moment and retry with the same key.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "`IDEMPOTENCY_KEY_REUSED`: this `Idempotency-Key` was already used with a different body. Use a new key for a new request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "idempotencyKeyReused": {
                    "summary": "Same Idempotency-Key, different body",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "IDEMPOTENCY_KEY_REUSED",
                      "message": "This Idempotency-Key was already used for a different request. Send a new key for a new request.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: more than the allowance for your API key in the current one-minute window (100 requests a minute by default, shared by every merchant endpoint the key calls). Wait for `Retry-After` seconds, then retry with the same `Idempotency-Key`.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "rateLimited": {
                    "summary": "Rate limited",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "TOO_MANY_REQUESTS",
                      "message": "Too many requests. Try again in 42 second(s).",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/pay/qr/create": {
      "post": {
        "tags": [
          "Payment Sessions"
        ],
        "summary": "Create a QR checkout order",
        "description": "Creates an order for a basket and returns `qrCodeUrl`, the hosted checkout page to encode in a QR code. The\ncustomer scans it and completes checkout on their own phone. No customer details are sent: the customer\nidentifies themselves after scanning.\n\n**When to call it.** For scan-to-pay with full basket data, for example a QR on a printed invoice or a\nscreen. Without basket data, `POST /api/v1/merchant/checkout-links` with `channel: \"Qr\"` is simpler.\n\n**Scope.** API key with `orders:write`, or a Merchant Portal session with any role except viewer.\n\n**What happens next.** The order is created as `AwaitingCheckout`; nothing is decided at this call. On the\nhosted page the customer verifies their phone with a one-time code, verifies their identity (Nafath), is\nassessed, chooses a plan, accepts the contract and pays the first instalment. The page is valid for two\nhours.\n\n| Outcome | Order status | Webhook |\n| --- | --- | --- |\n| The customer finished checkout | `Confirmed` | `order.confirmed` |\n| The credit decision declined the customer | `Declined` | `order.declined` |\n| Not finished within two hours | `Expired` | `order.expired` |\n\nFrom `Confirmed` on, the order counts toward the not-paid-yet balance of\n`GET /api/v1/merchant/settlements/overview`: you are paid the order total less your MDR fee and VAT on it.\n\nThe page opens in `language`, else the `Accept-Language` header, else Arabic.\n\n**The basket must add up**, to within 0.01: `items[].totalAmount = quantity × unitPrice − discountAmount`\nand `totalAmount = Σ items[].totalAmount`. Send `currencyCode: \"SAR\"`.\n\n**Fields you can leave out.** `merchantId` (ignored for API-key and portal callers) and `checkoutBaseUrl`\n(set by Qasimha).\n\n**Unique reference.** `orderReferenceId` must be unique across your orders. A reused reference answers\n`409 DUPLICATE_ORDER_REFERENCE`; when the order is yours, `data` has its `installmentId` and `qrCodeUrl`.\n\n**Return URLs (optional).** `merchantUrl { success, failure, cancel }` is where the hosted page sends the\ncustomer at the end, with `orderId`, `orderReferenceId` and `status` appended to the URL's query string\n(`confirmed` on success; `declined`, `expired` or `failed` on failure; `cancelled`). Absolute URLs, at most\n1024 characters, no `#fragment`; `https`, `http` only on `localhost` / `127.0.0.1`, or a custom app scheme\n(`myshop://…`). `success` and `failure` go together; `cancel` defaults to `failure`; `notification` is\nreserved and refused. A return is a navigation, not a proof: confirm with `GET /api/v1/merchant/orders/{id}`\nbefore you treat the order as paid.\n\n**Idempotency.** Send an `Idempotency-Key` header (a fresh UUID v4 per attempt) and reuse it unchanged\non retries: a retry with the same key and body replays the original answer with `Idempotent-Replay: true`\ninstead of acting twice. Same key with a different body: `422 IDEMPOTENCY_KEY_REUSED`. First attempt still\nrunning: `409 REQUEST_IN_PROGRESS`, retry shortly.\n\n**Errors.**\n\n| HTTP | Code | Meaning |\n| --- | --- | --- |\n| `400` | `VALIDATION_ERROR` | One or more fields failed; `errors` lists each one. |\n| `400` | `INVALID_REQUEST` | No request body. |\n| `400` | `MERCHANT_REQUIRED` | A back-office token that named no merchant. Never returned to an API key. |\n| `401` | — | No credential, or an unknown, expired or revoked API key. No body. |\n| `403` | `FORBIDDEN` | The key lacks `orders:write`, or the portal user is a viewer. |\n| `409` | `DUPLICATE_ORDER_REFERENCE` | `orderReferenceId` is already used. When the order is yours, `data` has its `installmentId` and `qrCodeUrl`. |\n| `409` | `MERCHANT_SUSPENDED` | Your account is suspended and cannot create orders. |\n| `409` | `REQUEST_IN_PROGRESS` | The first attempt with this `Idempotency-Key` is still running. |\n| `422` | `IDEMPOTENCY_KEY_REUSED` | This `Idempotency-Key` was used with a different body. |\n| `429` | `TOO_MANY_REQUESTS` | Rate limited; wait `Retry-After` seconds. |",
        "operationId": "CreateQrSession",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "description": "A unique key for this attempt, ideally a UUID v4, reused unchanged when you retry it. A retry with the same key and body returns the original response, with `Idempotent-Replay: true`, instead of acting twice. The same key with a different body is `422 IDEMPOTENCY_KEY_REUSED`; a retry while the first attempt is still running is `409 REQUEST_IN_PROGRESS`. Error answers are replayed too, so fix the request and send it with a new key. Keys are scoped to your merchant, the method and the path, and are kept for 24 hours. Optional, but recommended on every POST, PUT and DELETE.",
            "schema": {
              "type": "string"
            },
            "example": "5f0c7a52-3e1b-4c8e-9a2d-6b7f1e0d4c3a"
          },
          {
            "name": "Accept-Language",
            "in": "header",
            "description": "Fallback language for the customer (`ar` or `en`) when the body has no `language`; Arabic when neither says otherwise.",
            "schema": {
              "type": "string"
            },
            "example": "ar"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateQrSessionCommand"
              },
              "examples": {
                "scanToPay": {
                  "summary": "One-line basket, English page",
                  "value": {
                    "orderReferenceId": "QR-STORE7-000456",
                    "totalAmount": 450,
                    "currencyCode": "SAR",
                    "countryCode": "SA",
                    "items": [
                      {
                        "referenceId": "SKU-4004",
                        "name": "Bluetooth Speaker",
                        "type": "physical",
                        "sku": "SKU-4004",
                        "quantity": 1,
                        "unitPrice": 450,
                        "totalAmount": 450,
                        "taxAmount": 58.7,
                        "discountAmount": 0
                      }
                    ],
                    "language": "en"
                  }
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created. The order is `AwaitingCheckout`; encode `qrCodeUrl` in a QR code.",
            "headers": {
              "Idempotent-Replay": {
                "description": "`true` when this response is the stored answer to an earlier request with the same `Idempotency-Key`. Absent otherwise.",
                "schema": {
                  "type": "string",
                  "example": "true"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateQrSessionResultApiResponse"
                },
                "examples": {
                  "created": {
                    "summary": "QR order created",
                    "value": {
                      "success": true,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "SESSION_CREATED",
                      "message": "SESSION_CREATED",
                      "data": {
                        "installmentId": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61",
                        "providerSessionId": "",
                        "qrCodeUrl": "https://.../pay/3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61?lang=en",
                        "status": "AwaitingCheckout"
                      },
                      "errors": null
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request. `VALIDATION_ERROR` (field details in `errors`), `INVALID_REQUEST` (no body), `MERCHANT_REQUIRED` (a back-office token that named no merchant).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "validation": {
                    "summary": "Missing items",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "VALIDATION_ERROR",
                      "message": "One or more validation errors occurred.",
                      "data": null,
                      "errors": [
                        {
                          "field": "Items",
                          "message": "At least one item is required"
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized: no credential, or an unknown, expired or revoked API key. A publishable `pk_` key sent as `X-Api-Key` is refused the same way. The answer has no body and carries a `WWW-Authenticate` header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden: the API key lacks `orders:write`, or the Merchant Portal user's role does not allow this action. Code `FORBIDDEN`. A token that is not a merchant credential at all is refused with no body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "missingScope": {
                    "summary": "API key without orders:write",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "FORBIDDEN",
                      "message": "This API key lacks the orders:write scope.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "Conflict. `DUPLICATE_ORDER_REFERENCE`: the `orderReferenceId` is already used; when the existing order is yours, `data` carries its `installmentId` and `qrCodeUrl` so you can show the open checkout again. `MERCHANT_SUSPENDED`: your account is suspended. `REQUEST_IN_PROGRESS`: the first attempt with this `Idempotency-Key` is still running.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "duplicateReference": {
                    "summary": "Reference already used by one of your orders",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "DUPLICATE_ORDER_REFERENCE",
                      "message": "You already created an order with reference 'QR-STORE7-000456'.",
                      "data": {
                        "installmentId": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61",
                        "qrCodeUrl": "https://.../pay/3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61?lang=en"
                      },
                      "errors": [ ]
                    }
                  },
                  "merchantSuspended": {
                    "summary": "Account suspended",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "MERCHANT_SUSPENDED",
                      "message": "This merchant is currently suspended and cannot create new orders.",
                      "data": null,
                      "errors": [ ]
                    }
                  },
                  "requestInProgress": {
                    "summary": "Same Idempotency-Key still running",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "REQUEST_IN_PROGRESS",
                      "message": "A request with this Idempotency-Key is still being processed. Wait a moment and retry with the same key.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "`IDEMPOTENCY_KEY_REUSED`: this `Idempotency-Key` was already used with a different body. Use a new key for a new request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "idempotencyKeyReused": {
                    "summary": "Same Idempotency-Key, different body",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "IDEMPOTENCY_KEY_REUSED",
                      "message": "This Idempotency-Key was already used for a different request. Send a new key for a new request.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: more than the allowance for your API key in the current one-minute window (100 requests a minute by default, shared by every merchant endpoint the key calls). Wait for `Retry-After` seconds, then retry with the same `Idempotency-Key`.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "rateLimited": {
                    "summary": "Rate limited",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "TOO_MANY_REQUESTS",
                      "message": "Too many requests. Try again in 42 second(s).",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/widget/plans": {
      "get": {
        "tags": [
          "Widget"
        ],
        "summary": "Quote instalment plans for an amount (publishable key)",
        "description": "Returns the plans your policies offer for `amount`, as the on-site widget and the mobile SDK promo views show\nthem under a price: for each instalment count the regular, first and last instalment, the fees including\nVAT, the APR and the total payable. Quoted from your price list with no customer data; the customer's own\nterms are set at checkout.\n\n**When to call it.** From a product or cart page, directly from the browser or app. The `<qasimha-widget>`\nelement and the mobile SDKs call it for you.\n\n**Authentication.** A **publishable** key (`pk_test_…` / `pk_live_…`) in the `X-Public-Key` header, never your\nsecret API key. A publishable key opens this endpoint only. Create one in the Merchant Portal (API Keys, type\npublishable). Requests from any origin are allowed.\n\n**Caching.** Qasimha keeps a merchant's quote for an amount for one minute. The response itself is marked\nuncacheable; cache on your side per page.\n\n**Empty answer.** `plans` is empty when the amount is outside `[minAmount, maxAmount]` or your account has no\nactive policy: show nothing in that case. `shariaCompliant` is `true` for a plan with no finance charge.\n\n**Errors.**\n\n| HTTP | Code | Meaning |\n| --- | --- | --- |\n| `400` | `INVALID_AMOUNT` | `amount` is missing, 0 or less, has more than two decimals, or is too large. |\n| `400` | `UNSUPPORTED_CURRENCY` | `currency` is not `SAR`. |\n| `401` | `PUBLIC_KEY_REQUIRED` | No `X-Public-Key` header. |\n| `401` | `INVALID_PUBLIC_KEY` | The key is unknown, revoked or expired. |\n| `429` | `TOO_MANY_REQUESTS` | More than the per-key allowance (600 a minute by default); wait `Retry-After` seconds. |",
        "operationId": "WidgetPlans",
        "parameters": [
          {
            "name": "amount",
            "in": "query",
            "description": "The price to quote: greater than 0, at most 9999999999.99, at most two decimals. Required.",
            "schema": {
              "type": "number",
              "format": "double"
            },
            "example": 1200
          },
          {
            "name": "currency",
            "in": "query",
            "description": "ISO 4217 currency. Only `SAR`, the default when left out; anything else is `400 UNSUPPORTED_CURRENCY`.",
            "schema": {
              "type": "string"
            },
            "example": "SAR"
          },
          {
            "name": "lang",
            "in": "query",
            "description": "Language the widget renders in, `ar` or `en`. Accepted for the widget's own use; the quote is the same in every language.",
            "schema": {
              "type": "string"
            },
            "example": "ar"
          }
        ],
        "responses": {
          "200": {
            "description": "The quote. Code `OK`. `plans` is empty when the amount is outside `[minAmount, maxAmount]` or no policy is active.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WidgetPlansDtoApiResponse"
                },
                "examples": {
                  "plans": {
                    "summary": "Three plans",
                    "value": {
                      "success": true,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "OK",
                      "message": "Plans quoted",
                      "data": {
                        "merchantName": "Example Store",
                        "currency": "SAR",
                        "minAmount": 100,
                        "maxAmount": 4000,
                        "plans": [
                          {
                            "instalments": 2,
                            "frequency": "monthly",
                            "instalmentAmount": 614.38,
                            "firstInstalmentAmount": 614.38,
                            "lastInstalmentAmount": 614.37,
                            "feesIncludingVat": 28.75,
                            "totalPayable": 1228.75,
                            "shariaCompliant": true,
                            "apr": 0,
                            "processingFeeIncludingVat": 28.75,
                            "profitIncludingVat": 0
                          },
                          {
                            "instalments": 4,
                            "frequency": "monthly",
                            "instalmentAmount": 307.19,
                            "firstInstalmentAmount": 307.19,
                            "lastInstalmentAmount": 307.18,
                            "feesIncludingVat": 28.75,
                            "totalPayable": 1228.75,
                            "shariaCompliant": true,
                            "apr": 0,
                            "processingFeeIncludingVat": 28.75,
                            "profitIncludingVat": 0
                          }
                        ],
                        "products": [
                          {
                            "code": "BNPL",
                            "minAmount": 100,
                            "maxAmount": 4000,
                            "plans": [
                              {
                                "instalments": 2,
                                "frequency": "monthly",
                                "instalmentAmount": 614.38,
                                "firstInstalmentAmount": 614.38,
                                "lastInstalmentAmount": 614.37,
                                "feesIncludingVat": 28.75,
                                "totalPayable": 1228.75,
                                "shariaCompliant": true,
                                "apr": 0,
                                "processingFeeIncludingVat": 28.75,
                                "profitIncludingVat": 0
                              },
                              {
                                "instalments": 4,
                                "frequency": "monthly",
                                "instalmentAmount": 307.19,
                                "firstInstalmentAmount": 307.19,
                                "lastInstalmentAmount": 307.18,
                                "feesIncludingVat": 28.75,
                                "totalPayable": 1228.75,
                                "shariaCompliant": true,
                                "apr": 0,
                                "processingFeeIncludingVat": 28.75,
                                "profitIncludingVat": 0
                              }
                            ]
                          }
                        ]
                      },
                      "errors": null
                    }
                  },
                  "outOfRange": {
                    "summary": "Amount outside every range",
                    "value": {
                      "success": true,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "OK",
                      "message": "Plans quoted",
                      "data": {
                        "merchantName": "Example Store",
                        "currency": "SAR",
                        "minAmount": 100,
                        "maxAmount": 4000,
                        "plans": [ ],
                        "products": [ ]
                      },
                      "errors": null
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad request. `INVALID_AMOUNT`: missing, 0 or less, more than two decimals, or too large. `UNSUPPORTED_CURRENCY`: not SAR.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "invalidAmount": {
                    "summary": "Bad amount",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "INVALID_AMOUNT",
                      "message": "amount must be a positive number with at most two decimals.",
                      "data": null,
                      "errors": [ ]
                    }
                  },
                  "currency": {
                    "summary": "Not SAR",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "UNSUPPORTED_CURRENCY",
                      "message": "Only SAR is supported.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized. `PUBLIC_KEY_REQUIRED`: no `X-Public-Key` header. `INVALID_PUBLIC_KEY`: the key is unknown, revoked or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "missingKey": {
                    "summary": "No key",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "PUBLIC_KEY_REQUIRED",
                      "message": "Send your publishable key in the X-Public-Key header.",
                      "data": null,
                      "errors": [ ]
                    }
                  },
                  "invalidKey": {
                    "summary": "Unknown key",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "INVALID_PUBLIC_KEY",
                      "message": "The publishable key is unknown, revoked or expired.",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited: more than the per-key allowance (600 requests a minute by default; per IP address when no key is sent). Wait for `Retry-After` seconds.",
            "headers": {
              "Retry-After": {
                "description": "Seconds to wait before retrying.",
                "schema": {
                  "type": "integer",
                  "example": 42
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ObjectApiResponse"
                },
                "examples": {
                  "rateLimited": {
                    "summary": "Rate limited",
                    "value": {
                      "success": false,
                      "uuid": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f",
                      "code": "TOO_MANY_REQUESTS",
                      "message": "Too many requests. Try again in 12 second(s).",
                      "data": null,
                      "errors": [ ]
                    }
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "PublicKey": [ ]
          }
        ]
      }
    }
  },
  "components": {
    "schemas": {
      "ApiError": {
        "type": "object",
        "properties": {
          "field": {
            "type": "string",
            "description": "The request field that failed, as a path in the server's casing, for example `TotalAmount`, `Items[0].Quantity` or `MerchantUrl.Success`. Empty or `null` when the problem is not tied to one field.",
            "nullable": true,
            "example": "TotalAmount"
          },
          "message": {
            "type": "string",
            "description": "What is wrong with the field.",
            "nullable": true,
            "example": "Total amount must be greater than zero"
          }
        },
        "additionalProperties": false,
        "description": "One field-level problem in a `400 VALIDATION_ERROR` answer."
      },
      "CheckoutLinkItem": {
        "required": [
          "name",
          "quantity",
          "unitPrice"
        ],
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Product name, up to 500 characters.",
            "nullable": true,
            "example": "Wireless Headphones"
          },
          "sku": {
            "type": "string",
            "description": "Your SKU, up to 100 characters. Optional. The stored line's `referenceId` is the SKU, or the name when there is no SKU (at most 255 characters).",
            "nullable": true,
            "example": "SKU-1001"
          },
          "quantity": {
            "type": "integer",
            "description": "Number of units, greater than 0 (`400 INVALID_ITEM` otherwise).",
            "format": "int32",
            "example": 1
          },
          "unitPrice": {
            "type": "number",
            "description": "Price of one unit, 0 or more.",
            "format": "double",
            "example": 250
          },
          "imageUrl": {
            "type": "string",
            "description": "Product image shown on the hosted page, up to 500 characters. Optional.",
            "nullable": true,
            "example": "https://shop.example/images/sku-1001.jpg"
          }
        },
        "additionalProperties": false,
        "description": "A product line shown on the hosted page."
      },
      "CreateCheckoutLinkRequest": {
        "type": "object",
        "properties": {
          "amount": {
            "type": "number",
            "description": "Amount to finance, greater than 0 and at most 9999999999.99. May be 0 or left out when `items` are sent: the amount is then `Σ quantity × unitPrice`. When both are sent they must be equal, or the answer is `400 AMOUNT_ITEMS_MISMATCH`.",
            "format": "double",
            "example": 250
          },
          "channel": {
            "type": "string",
            "description": "`Link` (the default) sends the link to `phone` by SMS. `Qr` only returns the URL, for you to show as a QR code. Case-insensitive; any other value is treated as `Link`.",
            "nullable": true,
            "example": "Link"
          },
          "phone": {
            "type": "string",
            "description": "Customer mobile in E.164 form. Required for `Link` (`400 PHONE_REQUIRED`), optional for `Qr`. Pre-fills the hosted page.",
            "nullable": true,
            "example": "+966512345678"
          },
          "orderReferenceId": {
            "type": "string",
            "description": "Your order number, up to 255 characters, unique across all your orders (`409 DUPLICATE_ORDER_REFERENCE`). Leave it out and one is generated as `YYDDDNNNNN`: the UTC year, the day of the year, then five digits, for example `2628041980`.",
            "nullable": true,
            "example": "MERCHANT-ORDER-12345"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CheckoutLinkItem"
            },
            "description": "Optional product lines shown on the hosted page.",
            "nullable": true,
            "example": [
              {
                "name": "Wireless Headphones",
                "sku": "SKU-1001",
                "quantity": 1,
                "unitPrice": 250,
                "imageUrl": "https://shop.example/images/sku-1001.jpg"
              }
            ]
          },
          "ejarContractNumber": {
            "type": "string",
            "description": "Ejar platform contract number the order finances, at most 50 characters. Required for a real-estate merchant (`400 EJAR_CONTRACT_NUMBER_REQUIRED`); ignored and not stored for any other merchant.",
            "nullable": true,
            "example": "EJ-2026-000123"
          },
          "language": {
            "type": "string",
            "description": "Language the customer is contacted in, `ar` or `en`: the SMS, the push notification and the hosted page. When omitted, the `Accept-Language` header decides, then Arabic.",
            "nullable": true,
            "example": "ar"
          },
          "merchantUrl": {
            "allOf": [
              {
                "$ref": "#/components/schemas/MerchantUrlDto"
              }
            ],
            "description": "Optional return URLs for the end of the hosted checkout.",
            "example": {
              "success": "https://shop.example/checkout/qasimha/success",
              "failure": "https://shop.example/checkout/qasimha/failure",
              "cancel": "https://shop.example/checkout/qasimha/cancel",
              "notification": null
            }
          }
        },
        "additionalProperties": false,
        "description": "Request body of `POST /api/v1/merchant/checkout-links`. Send `amount`, `items`, or both (they must then agree)."
      },
      "CreateMerchantWebhookRequest": {
        "required": [
          "url"
        ],
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "description": "Where events are posted: `https`, a public host (no private or local address), at most 1024 characters, no credentials and no `#fragment`.",
            "nullable": true,
            "example": "https://shop.example/webhooks/qasimha"
          },
          "description": {
            "type": "string",
            "description": "Optional label, at most 200 characters.",
            "nullable": true,
            "example": "Production store"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Event types to receive; leave it out or send `[]` for every event. Known types: `order.confirmed`, `order.declined`, `order.fulfilled`, `order.partially_fulfilled`, `order.refunded`, `order.partially_refunded`, `order.voided`, `order.cancelled`, `order.expired`, `checkout_link.expired`. `order.completed` is accepted as an alias of `order.confirmed`. Case-insensitive; duplicates are dropped.",
            "nullable": true,
            "example": [
              "order.confirmed",
              "order.refunded"
            ]
          }
        },
        "additionalProperties": false,
        "description": "Request body of `POST /api/v1/merchant/webhooks`."
      },
      "CreateOnlineSessionAddressDto": {
        "type": "object",
        "properties": {
          "firstName": {
            "type": "string",
            "description": "Recipient first name. Stored.",
            "nullable": true,
            "example": "Noura"
          },
          "lastName": {
            "type": "string",
            "description": "Recipient last name. Stored.",
            "nullable": true,
            "example": "Al-Shammari"
          },
          "line1": {
            "type": "string",
            "description": "Street address. Stored.",
            "nullable": true,
            "example": "King Fahd Road 1234"
          },
          "line2": {
            "type": "string",
            "description": "Building, floor or unit. Accepted, not stored.",
            "nullable": true,
            "example": "Building 7, Apartment 12"
          },
          "city": {
            "type": "string",
            "description": "City. Stored.",
            "nullable": true,
            "example": "Riyadh"
          },
          "region": {
            "type": "string",
            "description": "Region or province. Accepted, not stored.",
            "nullable": true,
            "example": "Riyadh Province"
          },
          "postalCode": {
            "type": "string",
            "description": "Postal code. Accepted, not stored.",
            "nullable": true,
            "example": "12271"
          },
          "countryCode": {
            "type": "string",
            "description": "ISO 3166-1 alpha-2 country. Stored.",
            "nullable": true,
            "example": "SA"
          },
          "phoneNumber": {
            "type": "string",
            "description": "Contact number at the address. Accepted, not stored.",
            "nullable": true,
            "example": "+966512345678"
          }
        },
        "additionalProperties": false,
        "description": "A postal address. `firstName`, `lastName`, `line1`, `city` and `countryCode` are stored on the order."
      },
      "CreateOnlineSessionCommand": {
        "required": [
          "consumer",
          "countryCode",
          "currencyCode",
          "items",
          "orderReferenceId",
          "shippingAddress",
          "totalAmount"
        ],
        "type": "object",
        "properties": {
          "orderReferenceId": {
            "type": "string",
            "description": "Your own order number, 1 to 255 characters, unique across all your orders. It is echoed in webhooks and return URLs and finds the order with `GET /api/v1/merchant/orders/by-reference/{orderReferenceId}`. A reference already in use answers `409 DUPLICATE_ORDER_REFERENCE`.",
            "nullable": true,
            "example": "MERCHANT-ORDER-12345"
          },
          "totalAmount": {
            "type": "number",
            "description": "Amount to finance, greater than 0, two decimals. Must equal the basket total (see the model description) to within 0.01.",
            "format": "double",
            "example": 1278.5
          },
          "taxAmount": {
            "type": "number",
            "description": "Order-level tax (VAT) included in `totalAmount`. 0 or more; send 0 when there is none.",
            "format": "double",
            "example": 163.5
          },
          "shippingAmount": {
            "type": "number",
            "description": "Shipping charged to the customer, included in `totalAmount`. 0 or more.",
            "format": "double",
            "example": 25
          },
          "discountAmount": {
            "type": "number",
            "description": "Order-level discount, subtracted in `totalAmount`. 0 or more. Put line discounts on `items[].discountAmount`.",
            "format": "double",
            "example": 0
          },
          "currencyCode": {
            "type": "string",
            "description": "ISO 4217 currency code. Only `SAR` is accepted.",
            "nullable": true,
            "example": "SAR"
          },
          "countryCode": {
            "type": "string",
            "description": "ISO 3166-1 alpha-2 country of the sale, exactly two letters.",
            "nullable": true,
            "example": "SA"
          },
          "description": {
            "type": "string",
            "description": "Optional free text stored on the order and shown in the Merchant Portal.",
            "nullable": true,
            "example": "Order #12345 from the web store"
          },
          "paymentType": {
            "type": "string",
            "description": "Accepted for compatibility and not used: every order created here is an instalment order (`paymentType: \"PayByInstalments\"` on the order).",
            "nullable": true,
            "example": "pay_by_instalments"
          },
          "instalments": {
            "type": "integer",
            "description": "Your preferred number of instalments, stored on the order. The customer picks the plan on the hosted page from what your policy offers. 0 or omitted stores 4.",
            "format": "int32",
            "example": 4
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CreateOnlineSessionItemDto"
            },
            "description": "The basket, at least one line. Every field is stored as you send it.",
            "nullable": true,
            "example": [
              {
                "referenceId": "LINE-1",
                "name": "Wireless Headphones",
                "type": "physical",
                "sku": "SKU-1001",
                "quantity": 1,
                "unitPrice": 1000,
                "totalAmount": 1000,
                "taxAmount": 0,
                "discountAmount": 0,
                "imageUrl": "https://shop.example/images/sku-1001.jpg"
              }
            ]
          },
          "consumer": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CreateOnlineSessionConsumerDto"
              }
            ],
            "description": "The customer as you know them. Used to pre-fill the hosted page and to recognise a returning customer; the page still verifies the phone and the identity itself.",
            "example": {
              "firstName": "Noura",
              "lastName": "Al-Shammari",
              "phoneNumber": "+966512345678",
              "email": "noura@example.com",
              "nationalId": "1012345678"
            }
          },
          "shippingAddress": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CreateOnlineSessionAddressDto"
              }
            ],
            "description": "Delivery address. Required.",
            "example": {
              "firstName": "Noura",
              "lastName": "Al-Shammari",
              "line1": "King Fahd Road 1234",
              "line2": "Building 7, Apartment 12",
              "city": "Riyadh",
              "region": "Riyadh Province",
              "postalCode": "12271",
              "countryCode": "SA",
              "phoneNumber": "+966512345678"
            }
          },
          "billingAddress": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CreateOnlineSessionAddressDto"
              }
            ],
            "description": "Billing address. Optional.",
            "example": {
              "firstName": "Noura",
              "lastName": "Al-Shammari",
              "line1": "King Fahd Road 1234",
              "line2": "Building 7, Apartment 12",
              "city": "Riyadh",
              "region": "Riyadh Province",
              "postalCode": "12271",
              "countryCode": "SA",
              "phoneNumber": "+966512345678"
            }
          },
          "riskAssessment": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CreateOnlineSessionRiskAssessmentDto"
              }
            ],
            "description": "Accepted for compatibility and ignored by the hosted checkout: the credit decision uses only data Qasimha collects itself.",
            "example": {
              "customerDob": "1990-05-14",
              "customerGender": "female",
              "isExistingCustomer": true,
              "orderIsDigital": false,
              "totalOrderCount": 7,
              "dateOfFirstTransaction": "2023-01-20"
            }
          },
          "merchantId": {
            "type": "string",
            "description": "Ignored for API-key and Merchant Portal callers: the merchant always comes from your credential, so leave it out. Only a back-office administrator token, which carries no merchant, has to name one.",
            "format": "uuid",
            "nullable": true,
            "example": null
          },
          "decisionFeatures": {
            "type": "object",
            "additionalProperties": { },
            "description": "Ignored for every caller and discarded before the order is created: risk inputs are resolved by Qasimha only. Leave it out; sending it changes nothing and is logged.",
            "nullable": true,
            "example": null
          },
          "merchantUrl": {
            "allOf": [
              {
                "$ref": "#/components/schemas/MerchantUrlDto"
              }
            ],
            "description": "Optional return URLs for the end of the hosted checkout.",
            "example": {
              "success": "https://shop.example/checkout/qasimha/success",
              "failure": "https://shop.example/checkout/qasimha/failure",
              "cancel": "https://shop.example/checkout/qasimha/cancel",
              "notification": null
            }
          },
          "language": {
            "type": "string",
            "description": "Language the hosted checkout opens in: `ar` or `en` (a value starting with `en` is English, anything else Arabic). When omitted, the `Accept-Language` header decides, then Arabic.",
            "nullable": true,
            "example": "ar"
          },
          "checkoutBaseUrl": {
            "type": "string",
            "description": "Ignored: set by Qasimha. The hosted page always lives on the platform's own checkout host. Leave it out.",
            "nullable": true,
            "example": null
          }
        },
        "additionalProperties": false,
        "description": "Request body of `POST /api/v1/pay/session/create`: the basket, the customer, and where to send them afterwards. The amounts must add up: `totalAmount = Σ items[].totalAmount + taxAmount + shippingAmount − discountAmount`."
      },
      "CreateOnlineSessionConsumerDto": {
        "type": "object",
        "properties": {
          "firstName": {
            "type": "string",
            "description": "First name. Stored with `lastName` as the customer's name until the hosted checkout verifies the identity.",
            "nullable": true,
            "example": "Noura"
          },
          "lastName": {
            "type": "string",
            "description": "Last name.",
            "nullable": true,
            "example": "Al-Shammari"
          },
          "phoneNumber": {
            "type": "string",
            "description": "Mobile number in E.164 form (`+9665XXXXXXXX`). Pre-fills the hosted page, which verifies it with a one-time code, and recognises a returning customer.",
            "nullable": true,
            "example": "+966512345678"
          },
          "email": {
            "type": "string",
            "description": "Email address. Optional; used to recognise a returning customer when given.",
            "nullable": true,
            "example": "noura@example.com"
          },
          "nationalId": {
            "type": "string",
            "description": "Saudi national ID or Iqama number, 10 digits starting with 1 or 2. Optional; when sent it must be well-formed. The customer still verifies their identity on the hosted page.",
            "nullable": true,
            "example": "1012345678"
          }
        },
        "additionalProperties": false,
        "description": "The customer placing the order."
      },
      "CreateOnlineSessionItemDto": {
        "required": [
          "name",
          "quantity",
          "referenceId",
          "totalAmount",
          "unitPrice"
        ],
        "type": "object",
        "properties": {
          "referenceId": {
            "type": "string",
            "description": "Your identifier for the line, 1 to 255 characters. Returned on the order's `items[]`.",
            "nullable": true,
            "example": "LINE-1"
          },
          "name": {
            "type": "string",
            "description": "Product name shown to the customer, 1 to 500 characters.",
            "nullable": true,
            "example": "Wireless Headphones"
          },
          "type": {
            "type": "string",
            "description": "Line type, free text up to 50 characters, for example `physical`, `digital` or `service`.",
            "nullable": true,
            "example": "physical"
          },
          "sku": {
            "type": "string",
            "description": "Your SKU, up to 100 characters. Optional.",
            "nullable": true,
            "example": "SKU-1001"
          },
          "quantity": {
            "type": "integer",
            "description": "Number of units, 1 or more.",
            "format": "int32",
            "example": 1
          },
          "unitPrice": {
            "type": "number",
            "description": "Price of one unit, 0 or more, two decimals.",
            "format": "double",
            "example": 1000
          },
          "totalAmount": {
            "type": "number",
            "description": "Line total: `quantity × unitPrice − discountAmount`, to within 0.01.",
            "format": "double",
            "example": 1000
          },
          "taxAmount": {
            "type": "number",
            "description": "Tax on this line, 0 or more. Stored on the line; it is not part of the line's `totalAmount`.",
            "format": "double",
            "example": 0
          },
          "discountAmount": {
            "type": "number",
            "description": "Discount on this line, 0 or more, already subtracted in `totalAmount`.",
            "format": "double",
            "example": 0
          },
          "imageUrl": {
            "type": "string",
            "description": "Product image shown on the hosted page, up to 500 characters. Optional.",
            "nullable": true,
            "example": "https://shop.example/images/sku-1001.jpg"
          }
        },
        "additionalProperties": false,
        "description": "One basket line."
      },
      "CreateOnlineSessionResult": {
        "type": "object",
        "properties": {
          "installmentId": {
            "type": "string",
            "description": "The Qasimha order id. Store it against your `orderReferenceId`: every order endpoint takes it as `{id}`.",
            "format": "uuid",
            "example": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61"
          },
          "providerSessionId": {
            "type": "string",
            "description": "Always an empty string on the hosted checkout. Kept for compatibility.",
            "nullable": true,
            "example": ""
          },
          "paymentUrl": {
            "type": "string",
            "description": "The hosted checkout page, `<checkout host>/pay/{installmentId}?lang=ar|en`. Redirect the customer here. Valid for two hours.",
            "nullable": true,
            "example": "https://.../pay/3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61?lang=ar"
          },
          "status": {
            "type": "string",
            "description": "Order status at creation: `AwaitingCheckout`.",
            "nullable": true,
            "example": "AwaitingCheckout"
          }
        },
        "additionalProperties": false,
        "description": "The created order and the hosted page to send the customer to."
      },
      "CreateOnlineSessionResultApiResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "`true` when the request succeeded, `false` on every error.",
            "example": true
          },
          "uuid": {
            "type": "string",
            "description": "Identifier of this response. Quote it when you contact support about a request.",
            "nullable": true,
            "example": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f"
          },
          "code": {
            "type": "string",
            "description": "Machine-readable outcome, for example `SUCCESS`, `SESSION_CREATED` or `VALIDATION_ERROR`. Branch on this, never on `message`.",
            "nullable": true,
            "example": "SUCCESS"
          },
          "message": {
            "type": "string",
            "description": "Human-readable text for the outcome. It may change and may be localised; do not parse it.",
            "nullable": true,
            "example": "Operation completed successfully"
          },
          "data": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CreateOnlineSessionResult"
              }
            ],
            "description": "The result. Its shape is documented for each operation; `null` on most errors.",
            "example": {
              "installmentId": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61",
              "providerSessionId": "",
              "paymentUrl": "https://.../pay/3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61?lang=ar",
              "status": "AwaitingCheckout"
            }
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiError"
            },
            "description": "Field-level problems. `null` on success; on failure an array, often empty, of `{ field, message }`.",
            "nullable": true,
            "example": null
          }
        },
        "additionalProperties": false,
        "description": "The envelope every response uses. `success` and `code` tell you the outcome; `data` carries the result."
      },
      "CreateOnlineSessionRiskAssessmentDto": {
        "type": "object",
        "properties": {
          "customerDob": {
            "type": "string",
            "description": "Date of birth, `YYYY-MM-DD`. Ignored.",
            "nullable": true,
            "example": "1990-05-14"
          },
          "customerGender": {
            "type": "string",
            "description": "Gender. Ignored.",
            "nullable": true,
            "example": "female"
          },
          "isExistingCustomer": {
            "type": "boolean",
            "description": "Whether the shopper has bought from you before. Ignored.",
            "example": true
          },
          "orderIsDigital": {
            "type": "boolean",
            "description": "Whether the order is for digital goods. Ignored.",
            "example": false
          },
          "totalOrderCount": {
            "type": "integer",
            "description": "Orders the shopper has placed with you. Ignored.",
            "format": "int32",
            "example": 7
          },
          "dateOfFirstTransaction": {
            "type": "string",
            "description": "Date of the shopper's first order with you, `YYYY-MM-DD`. Ignored.",
            "nullable": true,
            "example": "2023-01-20"
          }
        },
        "additionalProperties": false,
        "description": "Shopper history you may hold. Accepted for compatibility and ignored by the hosted checkout."
      },
      "CreatePosSessionCommand": {
        "required": [
          "consumer",
          "countryCode",
          "currencyCode",
          "items",
          "orderReferenceId",
          "totalAmount"
        ],
        "type": "object",
        "properties": {
          "orderReferenceId": {
            "type": "string",
            "description": "Your order number, 1 to 255 characters, unique across all your orders. A reference already in use answers `409 DUPLICATE_ORDER_REFERENCE`.",
            "nullable": true,
            "example": "POS-STORE7-000123"
          },
          "totalAmount": {
            "type": "number",
            "description": "Amount to finance, greater than 0. Must equal `Σ items[].totalAmount` to within 0.01.",
            "format": "double",
            "example": 1150
          },
          "currencyCode": {
            "type": "string",
            "description": "ISO 4217 currency code, three letters. Send `SAR`, the only currency financed.",
            "nullable": true,
            "example": "SAR"
          },
          "countryCode": {
            "type": "string",
            "description": "ISO 3166-1 alpha-2 country of the sale, exactly two letters.",
            "nullable": true,
            "example": "SA"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CreatePosSessionItemDto"
            },
            "description": "The basket, at least one line. The order's `taxAmount` is the sum of the lines' `taxAmount`.",
            "nullable": true,
            "example": [
              {
                "referenceId": "LINE-1",
                "name": "Wireless Headphones",
                "type": "physical",
                "sku": "SKU-1001",
                "quantity": 1,
                "unitPrice": 1000,
                "totalAmount": 1000,
                "taxAmount": 0,
                "discountAmount": 0,
                "imageUrl": "https://shop.example/images/sku-1001.jpg"
              }
            ]
          },
          "consumer": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CreatePosSessionConsumerDto"
              }
            ],
            "description": "The customer at the till. Required.",
            "example": {
              "firstName": "Noura",
              "lastName": "Al-Shammari",
              "phoneNumber": "+966512345678",
              "email": "noura@example.com",
              "nationalId": "1012345678"
            }
          },
          "merchantId": {
            "type": "string",
            "description": "Ignored for API-key and Merchant Portal callers: the merchant always comes from your credential, so leave it out. Only a back-office administrator token, which carries no merchant, has to name one.",
            "format": "uuid",
            "nullable": true,
            "example": null
          },
          "merchantUrl": {
            "allOf": [
              {
                "$ref": "#/components/schemas/MerchantUrlDto"
              }
            ],
            "description": "Optional return URLs for the end of the hosted checkout.",
            "example": {
              "success": "https://shop.example/checkout/qasimha/success",
              "failure": "https://shop.example/checkout/qasimha/failure",
              "cancel": "https://shop.example/checkout/qasimha/cancel",
              "notification": null
            }
          }
        },
        "additionalProperties": false,
        "description": "Request body of `POST /api/v1/pay/pos/create`. There are no order-level tax, shipping or discount fields: `totalAmount` must equal `Σ items[].totalAmount`."
      },
      "CreatePosSessionConsumerDto": {
        "required": [
          "email",
          "firstName",
          "lastName",
          "phoneNumber"
        ],
        "type": "object",
        "properties": {
          "firstName": {
            "type": "string",
            "description": "First name.",
            "nullable": true,
            "example": "Noura"
          },
          "lastName": {
            "type": "string",
            "description": "Last name.",
            "nullable": true,
            "example": "Al-Shammari"
          },
          "phoneNumber": {
            "type": "string",
            "description": "Mobile number in E.164 form. Stored on the order and pre-fills the hosted page.",
            "nullable": true,
            "example": "+966512345678"
          },
          "email": {
            "type": "string",
            "description": "Email address. Required (`400 VALIDATION_ERROR` without it): a returning customer is recognised by it.",
            "nullable": true,
            "example": "noura@example.com"
          },
          "nationalId": {
            "type": "string",
            "description": "Saudi national ID or Iqama number, 10 digits. Optional.",
            "nullable": true,
            "example": "1012345678"
          }
        },
        "additionalProperties": false,
        "description": "The customer at the till."
      },
      "CreatePosSessionItemDto": {
        "required": [
          "name",
          "quantity",
          "referenceId",
          "totalAmount",
          "unitPrice"
        ],
        "type": "object",
        "properties": {
          "referenceId": {
            "type": "string",
            "description": "Your identifier for the line, 1 to 255 characters. Returned on the order's `items[]`.",
            "nullable": true,
            "example": "LINE-1"
          },
          "name": {
            "type": "string",
            "description": "Product name shown to the customer, 1 to 500 characters.",
            "nullable": true,
            "example": "Wireless Headphones"
          },
          "type": {
            "type": "string",
            "description": "Line type, free text up to 50 characters, for example `physical`, `digital` or `service`.",
            "nullable": true,
            "example": "physical"
          },
          "sku": {
            "type": "string",
            "description": "Your SKU, up to 100 characters. Optional.",
            "nullable": true,
            "example": "SKU-1001"
          },
          "quantity": {
            "type": "integer",
            "description": "Number of units, 1 or more.",
            "format": "int32",
            "example": 1
          },
          "unitPrice": {
            "type": "number",
            "description": "Price of one unit, 0 or more, two decimals.",
            "format": "double",
            "example": 1000
          },
          "totalAmount": {
            "type": "number",
            "description": "Line total: `quantity × unitPrice − discountAmount`, to within 0.01.",
            "format": "double",
            "example": 1000
          },
          "taxAmount": {
            "type": "number",
            "description": "Tax on this line, 0 or more. Stored on the line; it is not part of the line's `totalAmount`.",
            "format": "double",
            "example": 0
          },
          "discountAmount": {
            "type": "number",
            "description": "Discount on this line, 0 or more, already subtracted in `totalAmount`.",
            "format": "double",
            "example": 0
          },
          "imageUrl": {
            "type": "string",
            "description": "Product image shown on the hosted page, up to 500 characters. Optional.",
            "nullable": true,
            "example": "https://shop.example/images/sku-1001.jpg"
          }
        },
        "additionalProperties": false,
        "description": "One basket line."
      },
      "CreatePosSessionResult": {
        "type": "object",
        "properties": {
          "installmentId": {
            "type": "string",
            "description": "The Qasimha order id.",
            "format": "uuid",
            "example": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61"
          },
          "providerSessionId": {
            "type": "string",
            "description": "Always an empty string. Kept for compatibility.",
            "nullable": true,
            "example": ""
          },
          "checkoutUrl": {
            "type": "string",
            "description": "The hosted checkout page, `<checkout host>/pay/{installmentId}`. Show it as a QR code on the terminal or hand it to the customer. Valid for two hours.",
            "nullable": true,
            "example": "https://.../pay/3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61"
          },
          "status": {
            "type": "string",
            "description": "Order status at creation: `AwaitingCheckout`.",
            "nullable": true,
            "example": "AwaitingCheckout"
          }
        },
        "additionalProperties": false,
        "description": "The created in-store order and its hosted checkout page."
      },
      "CreatePosSessionResultApiResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "`true` when the request succeeded, `false` on every error.",
            "example": true
          },
          "uuid": {
            "type": "string",
            "description": "Identifier of this response. Quote it when you contact support about a request.",
            "nullable": true,
            "example": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f"
          },
          "code": {
            "type": "string",
            "description": "Machine-readable outcome, for example `SUCCESS`, `SESSION_CREATED` or `VALIDATION_ERROR`. Branch on this, never on `message`.",
            "nullable": true,
            "example": "SUCCESS"
          },
          "message": {
            "type": "string",
            "description": "Human-readable text for the outcome. It may change and may be localised; do not parse it.",
            "nullable": true,
            "example": "Operation completed successfully"
          },
          "data": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CreatePosSessionResult"
              }
            ],
            "description": "The result. Its shape is documented for each operation; `null` on most errors.",
            "example": {
              "installmentId": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61",
              "providerSessionId": "",
              "checkoutUrl": "https://.../pay/3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61",
              "status": "AwaitingCheckout"
            }
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiError"
            },
            "description": "Field-level problems. `null` on success; on failure an array, often empty, of `{ field, message }`.",
            "nullable": true,
            "example": null
          }
        },
        "additionalProperties": false,
        "description": "The envelope every response uses. `success` and `code` tell you the outcome; `data` carries the result."
      },
      "CreateQrSessionCommand": {
        "required": [
          "countryCode",
          "currencyCode",
          "items",
          "orderReferenceId",
          "totalAmount"
        ],
        "type": "object",
        "properties": {
          "orderReferenceId": {
            "type": "string",
            "description": "Your order number, 1 to 255 characters, unique across all your orders. A reference already in use answers `409 DUPLICATE_ORDER_REFERENCE`.",
            "nullable": true,
            "example": "QR-STORE7-000456"
          },
          "totalAmount": {
            "type": "number",
            "description": "Amount to finance, greater than 0. Must equal `Σ items[].totalAmount` to within 0.01.",
            "format": "double",
            "example": 450
          },
          "currencyCode": {
            "type": "string",
            "description": "ISO 4217 currency code, three letters. Send `SAR`, the only currency financed.",
            "nullable": true,
            "example": "SAR"
          },
          "countryCode": {
            "type": "string",
            "description": "ISO 3166-1 alpha-2 country of the sale, exactly two letters.",
            "nullable": true,
            "example": "SA"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CreateQrSessionItemDto"
            },
            "description": "The basket, at least one line.",
            "nullable": true,
            "example": [
              {
                "referenceId": "LINE-1",
                "name": "Wireless Headphones",
                "type": "physical",
                "sku": "SKU-1001",
                "quantity": 1,
                "unitPrice": 1000,
                "totalAmount": 1000,
                "taxAmount": 0,
                "discountAmount": 0,
                "imageUrl": "https://shop.example/images/sku-1001.jpg"
              }
            ]
          },
          "merchantId": {
            "type": "string",
            "description": "Ignored for API-key and Merchant Portal callers: the merchant always comes from your credential, so leave it out. Only a back-office administrator token, which carries no merchant, has to name one.",
            "format": "uuid",
            "nullable": true,
            "example": null
          },
          "merchantUrl": {
            "allOf": [
              {
                "$ref": "#/components/schemas/MerchantUrlDto"
              }
            ],
            "description": "Optional return URLs for the end of the hosted checkout.",
            "example": {
              "success": "https://shop.example/checkout/qasimha/success",
              "failure": "https://shop.example/checkout/qasimha/failure",
              "cancel": "https://shop.example/checkout/qasimha/cancel",
              "notification": null
            }
          },
          "language": {
            "type": "string",
            "description": "Language the hosted checkout opens in: `ar` or `en` (a value starting with `en` is English, anything else Arabic). When omitted, the `Accept-Language` header decides, then Arabic.",
            "nullable": true,
            "example": "ar"
          },
          "checkoutBaseUrl": {
            "type": "string",
            "description": "Ignored: set by Qasimha. The hosted page always lives on the platform's own checkout host. Leave it out.",
            "nullable": true,
            "example": null
          }
        },
        "additionalProperties": false,
        "description": "Request body of `POST /api/v1/pay/qr/create`. No customer details: the customer identifies themselves after scanning. `totalAmount` must equal `Σ items[].totalAmount`."
      },
      "CreateQrSessionItemDto": {
        "required": [
          "name",
          "quantity",
          "referenceId",
          "totalAmount",
          "unitPrice"
        ],
        "type": "object",
        "properties": {
          "referenceId": {
            "type": "string",
            "description": "Your identifier for the line, 1 to 255 characters. Returned on the order's `items[]`.",
            "nullable": true,
            "example": "LINE-1"
          },
          "name": {
            "type": "string",
            "description": "Product name shown to the customer, 1 to 500 characters.",
            "nullable": true,
            "example": "Wireless Headphones"
          },
          "type": {
            "type": "string",
            "description": "Line type, free text up to 50 characters, for example `physical`, `digital` or `service`.",
            "nullable": true,
            "example": "physical"
          },
          "sku": {
            "type": "string",
            "description": "Your SKU, up to 100 characters. Optional.",
            "nullable": true,
            "example": "SKU-1001"
          },
          "quantity": {
            "type": "integer",
            "description": "Number of units, 1 or more.",
            "format": "int32",
            "example": 1
          },
          "unitPrice": {
            "type": "number",
            "description": "Price of one unit, 0 or more, two decimals.",
            "format": "double",
            "example": 1000
          },
          "totalAmount": {
            "type": "number",
            "description": "Line total: `quantity × unitPrice − discountAmount`, to within 0.01.",
            "format": "double",
            "example": 1000
          },
          "taxAmount": {
            "type": "number",
            "description": "Tax on this line, 0 or more. Stored on the line; it is not part of the line's `totalAmount`.",
            "format": "double",
            "example": 0
          },
          "discountAmount": {
            "type": "number",
            "description": "Discount on this line, 0 or more, already subtracted in `totalAmount`.",
            "format": "double",
            "example": 0
          },
          "imageUrl": {
            "type": "string",
            "description": "Product image shown on the hosted page, up to 500 characters. Optional.",
            "nullable": true,
            "example": "https://shop.example/images/sku-1001.jpg"
          }
        },
        "additionalProperties": false,
        "description": "One basket line."
      },
      "CreateQrSessionResult": {
        "type": "object",
        "properties": {
          "installmentId": {
            "type": "string",
            "description": "The Qasimha order id.",
            "format": "uuid",
            "example": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61"
          },
          "providerSessionId": {
            "type": "string",
            "description": "Always an empty string on the hosted checkout. Kept for compatibility.",
            "nullable": true,
            "example": ""
          },
          "qrCodeUrl": {
            "type": "string",
            "description": "The hosted checkout page to encode in your QR code, `<checkout host>/pay/{installmentId}?lang=ar|en`. Valid for two hours.",
            "nullable": true,
            "example": "https://.../pay/3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61?lang=ar"
          },
          "status": {
            "type": "string",
            "description": "Order status at creation: `AwaitingCheckout`.",
            "nullable": true,
            "example": "AwaitingCheckout"
          }
        },
        "additionalProperties": false,
        "description": "The created QR order and the URL to encode in the QR code."
      },
      "CreateQrSessionResultApiResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "`true` when the request succeeded, `false` on every error.",
            "example": true
          },
          "uuid": {
            "type": "string",
            "description": "Identifier of this response. Quote it when you contact support about a request.",
            "nullable": true,
            "example": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f"
          },
          "code": {
            "type": "string",
            "description": "Machine-readable outcome, for example `SUCCESS`, `SESSION_CREATED` or `VALIDATION_ERROR`. Branch on this, never on `message`.",
            "nullable": true,
            "example": "SUCCESS"
          },
          "message": {
            "type": "string",
            "description": "Human-readable text for the outcome. It may change and may be localised; do not parse it.",
            "nullable": true,
            "example": "Operation completed successfully"
          },
          "data": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CreateQrSessionResult"
              }
            ],
            "description": "The result. Its shape is documented for each operation; `null` on most errors.",
            "example": {
              "installmentId": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61",
              "providerSessionId": "",
              "qrCodeUrl": "https://.../pay/3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61?lang=ar",
              "status": "AwaitingCheckout"
            }
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiError"
            },
            "description": "Field-level problems. `null` on success; on failure an array, often empty, of `{ field, message }`.",
            "nullable": true,
            "example": null
          }
        },
        "additionalProperties": false,
        "description": "The envelope every response uses. `success` and `code` tell you the outcome; `data` carries the result."
      },
      "MerchantCheckoutLinkCreatedDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The Qasimha order id. Use it with `/merchant/checkout-links/{id}` and `/merchant/orders/{id}`.",
            "format": "uuid",
            "example": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61"
          },
          "url": {
            "type": "string",
            "description": "The hosted checkout page, `<checkout host>/pay/{id}?lang=ar|en`. For `Link` it has also been sent to the customer; for `Qr`, encode it in your QR code.",
            "nullable": true,
            "example": "https://.../pay/3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61?lang=ar"
          },
          "channel": {
            "type": "string",
            "description": "`Link` or `Qr`.",
            "nullable": true,
            "example": "Link"
          },
          "amount": {
            "type": "number",
            "description": "Amount to finance.",
            "format": "double",
            "example": 250
          },
          "currency": {
            "type": "string",
            "description": "Currency of the amount.",
            "nullable": true,
            "example": "SAR"
          },
          "phone": {
            "type": "string",
            "description": "The phone you sent, unmasked; `null` when none.",
            "nullable": true,
            "example": "+966512345678"
          },
          "language": {
            "type": "string",
            "description": "Language the customer is contacted in: `ar` or `en`.",
            "nullable": true,
            "example": "ar"
          },
          "orderReferenceId": {
            "type": "string",
            "description": "Your reference, or the generated one.",
            "nullable": true,
            "example": "2628041980"
          },
          "ejarContractNumber": {
            "type": "string",
            "description": "The Ejar contract number for a real-estate merchant; `null` otherwise.",
            "nullable": true,
            "example": null
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MerchantCheckoutLinkCreatedItemDto"
            },
            "description": "The lines you sent, each with its `total`; `null` when you sent none.",
            "nullable": true,
            "example": [
              {
                "name": "Wireless Headphones",
                "sku": "SKU-1001",
                "quantity": 1,
                "unitPrice": 250,
                "total": 250,
                "imageUrl": "https://shop.example/images/sku-1001.jpg"
              }
            ]
          },
          "status": {
            "type": "string",
            "description": "Order status: `AwaitingCheckout`.",
            "nullable": true,
            "example": "AwaitingCheckout"
          },
          "expiresAt": {
            "type": "string",
            "description": "When the link expires (UTC), two hours after creation.",
            "format": "date-time",
            "example": "2026-10-20T11:00:00Z"
          }
        },
        "additionalProperties": false,
        "description": "The created payment-link or QR order."
      },
      "MerchantCheckoutLinkCreatedDtoApiResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "`true` when the request succeeded, `false` on every error.",
            "example": true
          },
          "uuid": {
            "type": "string",
            "description": "Identifier of this response. Quote it when you contact support about a request.",
            "nullable": true,
            "example": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f"
          },
          "code": {
            "type": "string",
            "description": "Machine-readable outcome, for example `SUCCESS`, `SESSION_CREATED` or `VALIDATION_ERROR`. Branch on this, never on `message`.",
            "nullable": true,
            "example": "SUCCESS"
          },
          "message": {
            "type": "string",
            "description": "Human-readable text for the outcome. It may change and may be localised; do not parse it.",
            "nullable": true,
            "example": "Operation completed successfully"
          },
          "data": {
            "allOf": [
              {
                "$ref": "#/components/schemas/MerchantCheckoutLinkCreatedDto"
              }
            ],
            "description": "The result. Its shape is documented for each operation; `null` on most errors.",
            "example": {
              "id": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61",
              "url": "https://.../pay/3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61?lang=ar",
              "channel": "Link",
              "amount": 250,
              "currency": "SAR",
              "phone": "+966512345678",
              "language": "ar",
              "orderReferenceId": "2628041980",
              "ejarContractNumber": null,
              "items": [
                {
                  "name": "Wireless Headphones",
                  "sku": "SKU-1001",
                  "quantity": 1,
                  "unitPrice": 250,
                  "total": 250,
                  "imageUrl": "https://shop.example/images/sku-1001.jpg"
                }
              ],
              "status": "AwaitingCheckout",
              "expiresAt": "2026-10-20T11:00:00Z"
            }
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiError"
            },
            "description": "Field-level problems. `null` on success; on failure an array, often empty, of `{ field, message }`.",
            "nullable": true,
            "example": null
          }
        },
        "additionalProperties": false,
        "description": "The envelope every response uses. `success` and `code` tell you the outcome; `data` carries the result."
      },
      "MerchantCheckoutLinkCreatedItemDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Product name.",
            "nullable": true,
            "example": "Wireless Headphones"
          },
          "sku": {
            "type": "string",
            "description": "SKU, or `null`.",
            "nullable": true,
            "example": "SKU-1001"
          },
          "quantity": {
            "type": "integer",
            "description": "Number of units.",
            "format": "int32",
            "example": 1
          },
          "unitPrice": {
            "type": "number",
            "description": "Price of one unit.",
            "format": "double",
            "example": 250
          },
          "total": {
            "type": "number",
            "description": "`quantity × unitPrice`.",
            "format": "double",
            "example": 250
          },
          "imageUrl": {
            "type": "string",
            "description": "Product image, or `null`.",
            "nullable": true,
            "example": "https://shop.example/images/sku-1001.jpg"
          }
        },
        "additionalProperties": false,
        "description": "A line as stored on the link."
      },
      "MerchantCheckoutLinkListDto": {
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MerchantCheckoutLinkRowDto"
            },
            "description": "The orders. No paging: only the latest 50 are returned.",
            "nullable": true,
            "example": [
              {
                "id": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61",
                "orderReferenceId": "MERCHANT-ORDER-12345",
                "ejarContractNumber": null,
                "amount": 250,
                "currency": "SAR",
                "phone": "*****678",
                "channel": "Link",
                "status": "AwaitingCheckout",
                "decisionStatus": null,
                "expiresAt": "2026-10-20T11:00:00Z",
                "createdAt": "2026-10-20T09:00:00Z",
                "waiting": true
              }
            ]
          }
        },
        "additionalProperties": false,
        "description": "Your 50 most recent hosted-checkout orders, newest first."
      },
      "MerchantCheckoutLinkListDtoApiResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "`true` when the request succeeded, `false` on every error.",
            "example": true
          },
          "uuid": {
            "type": "string",
            "description": "Identifier of this response. Quote it when you contact support about a request.",
            "nullable": true,
            "example": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f"
          },
          "code": {
            "type": "string",
            "description": "Machine-readable outcome, for example `SUCCESS`, `SESSION_CREATED` or `VALIDATION_ERROR`. Branch on this, never on `message`.",
            "nullable": true,
            "example": "SUCCESS"
          },
          "message": {
            "type": "string",
            "description": "Human-readable text for the outcome. It may change and may be localised; do not parse it.",
            "nullable": true,
            "example": "Operation completed successfully"
          },
          "data": {
            "allOf": [
              {
                "$ref": "#/components/schemas/MerchantCheckoutLinkListDto"
              }
            ],
            "description": "The result. Its shape is documented for each operation; `null` on most errors.",
            "example": {
              "items": [
                {
                  "id": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61",
                  "orderReferenceId": "MERCHANT-ORDER-12345",
                  "ejarContractNumber": null,
                  "amount": 250,
                  "currency": "SAR",
                  "phone": "*****678",
                  "channel": "Link",
                  "status": "AwaitingCheckout",
                  "decisionStatus": null,
                  "expiresAt": "2026-10-20T11:00:00Z",
                  "createdAt": "2026-10-20T09:00:00Z",
                  "waiting": true
                }
              ]
            }
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiError"
            },
            "description": "Field-level problems. `null` on success; on failure an array, often empty, of `{ field, message }`.",
            "nullable": true,
            "example": null
          }
        },
        "additionalProperties": false,
        "description": "The envelope every response uses. `success` and `code` tell you the outcome; `data` carries the result."
      },
      "MerchantCheckoutLinkRowDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The Qasimha order id.",
            "format": "uuid",
            "example": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61"
          },
          "orderReferenceId": {
            "type": "string",
            "description": "Your order reference.",
            "nullable": true,
            "example": "MERCHANT-ORDER-12345"
          },
          "ejarContractNumber": {
            "type": "string",
            "description": "The Ejar contract number for a real-estate merchant; `null` otherwise.",
            "nullable": true,
            "example": null
          },
          "amount": {
            "type": "number",
            "description": "Amount financed.",
            "format": "double",
            "example": 250
          },
          "currency": {
            "type": "string",
            "description": "Currency of the amount.",
            "nullable": true,
            "example": "SAR"
          },
          "phone": {
            "type": "string",
            "description": "Customer phone, masked to its last three digits; `null` when none.",
            "nullable": true,
            "example": "*****678"
          },
          "channel": {
            "type": "string",
            "description": "How the hosted checkout was started: `Link`, `Qr`, `Online` (a payment session) or `Pos`.",
            "nullable": true,
            "example": "Link"
          },
          "status": {
            "type": "string",
            "description": "Order status name. A link past its deadline shows `Expired`. Values: Draft, New, Approved, Declined, Expired, Confirmed, FullyFulfilled, PartiallyFulfilled, FullyReversed, PartiallyReversed, Voided, Updated, AwaitingCheckout, Cancelled, Settled, WrittenOff.",
            "nullable": true,
            "example": "AwaitingCheckout"
          },
          "decisionStatus": {
            "type": "string",
            "description": "Outcome of the credit decision as recorded by the engine, for example `APPROVED` or `DECLINED`; `null` until the customer has been assessed.",
            "nullable": true,
            "example": null
          },
          "expiresAt": {
            "type": "string",
            "description": "Checkout deadline (UTC).",
            "format": "date-time",
            "nullable": true,
            "example": "2026-10-20T11:00:00Z"
          },
          "createdAt": {
            "type": "string",
            "description": "When the order was created (UTC).",
            "format": "date-time",
            "example": "2026-10-20T09:00:00Z"
          },
          "waiting": {
            "type": "boolean",
            "description": "`true` while the checkout is still open: `AwaitingCheckout` and not past its deadline.",
            "example": true
          }
        },
        "additionalProperties": false,
        "description": "One hosted-checkout order in the list."
      },
      "MerchantCheckoutLinkStatusDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The Qasimha order id.",
            "format": "uuid",
            "example": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61"
          },
          "orderReferenceId": {
            "type": "string",
            "description": "Your order reference.",
            "nullable": true,
            "example": "MERCHANT-ORDER-12345"
          },
          "ejarContractNumber": {
            "type": "string",
            "description": "The Ejar contract number for a real-estate merchant; `null` otherwise.",
            "nullable": true,
            "example": null
          },
          "amount": {
            "type": "number",
            "description": "Amount financed.",
            "format": "double",
            "example": 250
          },
          "currency": {
            "type": "string",
            "description": "Currency of the amount.",
            "nullable": true,
            "example": "SAR"
          },
          "phone": {
            "type": "string",
            "description": "Customer phone, masked to its last three digits; `null` when none.",
            "nullable": true,
            "example": "*****678"
          },
          "channel": {
            "type": "string",
            "description": "How the hosted checkout was started: `Link`, `Qr`, `Online` (a payment session) or `Pos`.",
            "nullable": true,
            "example": "Link"
          },
          "status": {
            "type": "string",
            "description": "Order status name. A link past its deadline shows `Expired`. Values: Draft, New, Approved, Declined, Expired, Confirmed, FullyFulfilled, PartiallyFulfilled, FullyReversed, PartiallyReversed, Voided, Updated, AwaitingCheckout, Cancelled, Settled, WrittenOff.",
            "nullable": true,
            "example": "AwaitingCheckout"
          },
          "decisionStatus": {
            "type": "string",
            "description": "Outcome of the credit decision as recorded by the engine, for example `APPROVED` or `DECLINED`; `null` until the customer has been assessed.",
            "nullable": true,
            "example": null
          },
          "expiresAt": {
            "type": "string",
            "description": "Checkout deadline (UTC).",
            "format": "date-time",
            "nullable": true,
            "example": "2026-10-20T11:00:00Z"
          },
          "createdAt": {
            "type": "string",
            "description": "When the order was created (UTC).",
            "format": "date-time",
            "example": "2026-10-20T09:00:00Z"
          },
          "completed": {
            "type": "boolean",
            "description": "`true` once the customer finished checkout: status `Confirmed`, `PartiallyFulfilled`, `FullyFulfilled`, `New` or `Approved`. It reads `false` again after a refund, void or settlement; read the order for those.",
            "example": false
          },
          "declined": {
            "type": "boolean",
            "description": "`true` when the credit decision declined the customer.",
            "example": false
          }
        },
        "additionalProperties": false,
        "description": "Status of one hosted-checkout order."
      },
      "MerchantCheckoutLinkStatusDtoApiResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "`true` when the request succeeded, `false` on every error.",
            "example": true
          },
          "uuid": {
            "type": "string",
            "description": "Identifier of this response. Quote it when you contact support about a request.",
            "nullable": true,
            "example": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f"
          },
          "code": {
            "type": "string",
            "description": "Machine-readable outcome, for example `SUCCESS`, `SESSION_CREATED` or `VALIDATION_ERROR`. Branch on this, never on `message`.",
            "nullable": true,
            "example": "SUCCESS"
          },
          "message": {
            "type": "string",
            "description": "Human-readable text for the outcome. It may change and may be localised; do not parse it.",
            "nullable": true,
            "example": "Operation completed successfully"
          },
          "data": {
            "allOf": [
              {
                "$ref": "#/components/schemas/MerchantCheckoutLinkStatusDto"
              }
            ],
            "description": "The result. Its shape is documented for each operation; `null` on most errors.",
            "example": {
              "id": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61",
              "orderReferenceId": "MERCHANT-ORDER-12345",
              "ejarContractNumber": null,
              "amount": 250,
              "currency": "SAR",
              "phone": "*****678",
              "channel": "Link",
              "status": "AwaitingCheckout",
              "decisionStatus": null,
              "expiresAt": "2026-10-20T11:00:00Z",
              "createdAt": "2026-10-20T09:00:00Z",
              "completed": false,
              "declined": false
            }
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiError"
            },
            "description": "Field-level problems. `null` on success; on failure an array, often empty, of `{ field, message }`.",
            "nullable": true,
            "example": null
          }
        },
        "additionalProperties": false,
        "description": "The envelope every response uses. `success` and `code` tell you the outcome; `data` carries the result."
      },
      "MerchantFulfillRequest": {
        "type": "object",
        "properties": {
          "shippedAmount": {
            "type": "number",
            "description": "Amount shipped in this fulfilment. Leave it out (or `null`) to fulfil everything still outstanding (`totalAmount − fulfilledAmount`). Otherwise greater than 0 and not above the outstanding amount; rounded to two decimals.",
            "format": "double",
            "nullable": true,
            "example": 1278.5
          },
          "shippingCompany": {
            "type": "string",
            "description": "Carrier name. Optional; stored on the fulfilment.",
            "nullable": true,
            "example": "Aramex"
          },
          "trackingNumber": {
            "type": "string",
            "description": "Tracking number. Optional.",
            "nullable": true,
            "example": "ARX123456789"
          },
          "trackingUrl": {
            "type": "string",
            "description": "Tracking URL. Optional.",
            "nullable": true,
            "example": "https://shop.example/track/ARX123456789"
          }
        },
        "additionalProperties": false,
        "description": "Request body of `POST /api/v1/merchant/orders/{id}/fulfill`. Send `{}` to fulfil everything still outstanding."
      },
      "MerchantFulfillmentDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Fulfilment id.",
            "format": "uuid",
            "example": "b7c8d9e0-1f2a-4b3c-8d4e-5f6a7b8c9d0e"
          },
          "amount": {
            "type": "number",
            "description": "Amount shipped in this fulfilment.",
            "format": "double",
            "example": 1278.5
          },
          "currencyCode": {
            "type": "string",
            "description": "Currency.",
            "nullable": true,
            "example": "SAR"
          },
          "shippingCompany": {
            "type": "string",
            "description": "Carrier, as sent.",
            "nullable": true,
            "example": "Aramex"
          },
          "trackingNumber": {
            "type": "string",
            "description": "Tracking number, as sent.",
            "nullable": true,
            "example": "ARX123456789"
          },
          "trackingUrl": {
            "type": "string",
            "description": "Tracking URL, as sent.",
            "nullable": true,
            "example": "https://shop.example/track/ARX123456789"
          },
          "providerCaptureId": {
            "type": "string",
            "description": "Payment-provider capture reference; `null` on the hosted checkout.",
            "nullable": true,
            "example": null
          },
          "createdAt": {
            "type": "string",
            "description": "When the fulfilment was recorded (UTC).",
            "format": "date-time",
            "example": "2026-10-21T08:00:00Z"
          }
        },
        "additionalProperties": false,
        "description": "One fulfilment (shipment) you recorded."
      },
      "MerchantInstallmentDto": {
        "type": "object",
        "properties": {
          "sequence": {
            "type": "integer",
            "description": "Position in the schedule, starting at 1.",
            "format": "int32",
            "example": 1
          },
          "dueDate": {
            "type": "string",
            "description": "Due date (UTC).",
            "format": "date-time",
            "example": "2026-10-20T00:00:00Z"
          },
          "amount": {
            "type": "number",
            "description": "Amount due for this instalment.",
            "format": "double",
            "example": 319.63
          },
          "principal": {
            "type": "number",
            "description": "Principal part of `amount`.",
            "format": "double",
            "example": 319.63
          },
          "profit": {
            "type": "number",
            "description": "Profit (finance charge) part of `amount`; 0 on an interest-free plan.",
            "format": "double",
            "example": 0
          },
          "status": {
            "type": "string",
            "description": "`Scheduled`, `Paid`, `Late`, `Reversed` (cancelled by a refund or void) or `Waived`.",
            "nullable": true,
            "example": "Paid"
          },
          "paidAmount": {
            "type": "number",
            "description": "Amount paid against this instalment.",
            "format": "double",
            "example": 319.63
          },
          "paidAt": {
            "type": "string",
            "description": "When it was paid (UTC), or `null`.",
            "format": "date-time",
            "nullable": true,
            "example": "2026-10-20T09:25:00Z"
          },
          "providerRef": {
            "type": "string",
            "description": "Payment reference of the collection, or `null`.",
            "nullable": true,
            "example": "chg_TS02A1520261020x8Y91234567"
          }
        },
        "additionalProperties": false,
        "description": "One instalment of the customer's repayment schedule."
      },
      "MerchantOrderCancelResultDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The Qasimha order id.",
            "format": "uuid",
            "example": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61"
          },
          "status": {
            "type": "integer",
            "description": "Order status after the call, as a number (not a name, unlike the order reads): 0 Draft, 1 New, 2 Approved, 3 Declined, 4 Expired, 5 Confirmed, 6 FullyFulfilled, 7 PartiallyFulfilled, 8 FullyReversed, 9 PartiallyReversed, 10 Voided, 11 Updated, 12 AwaitingCheckout, 13 Cancelled, 14 Settled, 15 WrittenOff. `10` (Voided) for a confirmed order, `13` (Cancelled) for an open checkout.",
            "format": "int32",
            "example": 10
          }
        },
        "additionalProperties": false,
        "description": "The order after a cancellation."
      },
      "MerchantOrderCancelResultDtoApiResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "`true` when the request succeeded, `false` on every error.",
            "example": true
          },
          "uuid": {
            "type": "string",
            "description": "Identifier of this response. Quote it when you contact support about a request.",
            "nullable": true,
            "example": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f"
          },
          "code": {
            "type": "string",
            "description": "Machine-readable outcome, for example `SUCCESS`, `SESSION_CREATED` or `VALIDATION_ERROR`. Branch on this, never on `message`.",
            "nullable": true,
            "example": "SUCCESS"
          },
          "message": {
            "type": "string",
            "description": "Human-readable text for the outcome. It may change and may be localised; do not parse it.",
            "nullable": true,
            "example": "Operation completed successfully"
          },
          "data": {
            "allOf": [
              {
                "$ref": "#/components/schemas/MerchantOrderCancelResultDto"
              }
            ],
            "description": "The result. Its shape is documented for each operation; `null` on most errors.",
            "example": {
              "id": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61",
              "status": 10
            }
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiError"
            },
            "description": "Field-level problems. `null` on success; on failure an array, often empty, of `{ field, message }`.",
            "nullable": true,
            "example": null
          }
        },
        "additionalProperties": false,
        "description": "The envelope every response uses. `success` and `code` tell you the outcome; `data` carries the result."
      },
      "MerchantOrderCaptureResultDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The Qasimha order id.",
            "format": "uuid",
            "example": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61"
          },
          "status": {
            "type": "integer",
            "description": "Order status after the call, as a number (not a name, unlike the order reads): 0 Draft, 1 New, 2 Approved, 3 Declined, 4 Expired, 5 Confirmed, 6 FullyFulfilled, 7 PartiallyFulfilled, 8 FullyReversed, 9 PartiallyReversed, 10 Voided, 11 Updated, 12 AwaitingCheckout, 13 Cancelled, 14 Settled, 15 WrittenOff. `5` (Confirmed) after a capture.",
            "format": "int32",
            "example": 5
          },
          "confirmedAt": {
            "type": "string",
            "description": "When the order was confirmed (UTC).",
            "format": "date-time",
            "nullable": true,
            "example": "2026-10-20T09:30:00Z"
          }
        },
        "additionalProperties": false,
        "description": "The order after a capture."
      },
      "MerchantOrderCaptureResultDtoApiResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "`true` when the request succeeded, `false` on every error.",
            "example": true
          },
          "uuid": {
            "type": "string",
            "description": "Identifier of this response. Quote it when you contact support about a request.",
            "nullable": true,
            "example": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f"
          },
          "code": {
            "type": "string",
            "description": "Machine-readable outcome, for example `SUCCESS`, `SESSION_CREATED` or `VALIDATION_ERROR`. Branch on this, never on `message`.",
            "nullable": true,
            "example": "SUCCESS"
          },
          "message": {
            "type": "string",
            "description": "Human-readable text for the outcome. It may change and may be localised; do not parse it.",
            "nullable": true,
            "example": "Operation completed successfully"
          },
          "data": {
            "allOf": [
              {
                "$ref": "#/components/schemas/MerchantOrderCaptureResultDto"
              }
            ],
            "description": "The result. Its shape is documented for each operation; `null` on most errors.",
            "example": {
              "id": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61",
              "status": 5,
              "confirmedAt": "2026-10-20T09:30:00Z"
            }
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiError"
            },
            "description": "Field-level problems. `null` on success; on failure an array, often empty, of `{ field, message }`.",
            "nullable": true,
            "example": null
          }
        },
        "additionalProperties": false,
        "description": "The envelope every response uses. `success` and `code` tell you the outcome; `data` carries the result."
      },
      "MerchantOrderCountsDto": {
        "type": "object",
        "properties": {
          "all": {
            "type": "integer",
            "description": "All your orders.",
            "format": "int32",
            "example": 57
          },
          "approved": {
            "type": "integer",
            "description": "Orders in `New` or `Approved`.",
            "format": "int32",
            "example": 2
          },
          "authorised": {
            "type": "integer",
            "description": "Orders in `Confirmed`: checkout complete, nothing shipped yet.",
            "format": "int32",
            "example": 18
          },
          "captured": {
            "type": "integer",
            "description": "Orders in `FullyFulfilled` or `PartiallyFulfilled`.",
            "format": "int32",
            "example": 25
          },
          "refunded": {
            "type": "integer",
            "description": "Orders in `FullyReversed` or `PartiallyReversed`.",
            "format": "int32",
            "example": 3
          },
          "cancelled": {
            "type": "integer",
            "description": "Orders in `Voided` or `Cancelled`.",
            "format": "int32",
            "example": 4
          }
        },
        "additionalProperties": false,
        "description": "Number of your orders per Merchant Portal tab."
      },
      "MerchantOrderCountsDtoApiResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "`true` when the request succeeded, `false` on every error.",
            "example": true
          },
          "uuid": {
            "type": "string",
            "description": "Identifier of this response. Quote it when you contact support about a request.",
            "nullable": true,
            "example": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f"
          },
          "code": {
            "type": "string",
            "description": "Machine-readable outcome, for example `SUCCESS`, `SESSION_CREATED` or `VALIDATION_ERROR`. Branch on this, never on `message`.",
            "nullable": true,
            "example": "SUCCESS"
          },
          "message": {
            "type": "string",
            "description": "Human-readable text for the outcome. It may change and may be localised; do not parse it.",
            "nullable": true,
            "example": "Operation completed successfully"
          },
          "data": {
            "allOf": [
              {
                "$ref": "#/components/schemas/MerchantOrderCountsDto"
              }
            ],
            "description": "The result. Its shape is documented for each operation; `null` on most errors.",
            "example": {
              "all": 57,
              "approved": 2,
              "authorised": 18,
              "captured": 25,
              "refunded": 3,
              "cancelled": 4
            }
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiError"
            },
            "description": "Field-level problems. `null` on success; on failure an array, often empty, of `{ field, message }`.",
            "nullable": true,
            "example": null
          }
        },
        "additionalProperties": false,
        "description": "The envelope every response uses. `success` and `code` tell you the outcome; `data` carries the result."
      },
      "MerchantOrderCustomerDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Qasimha customer id.",
            "format": "uuid",
            "example": "8d2e4f60-7a1b-4c3d-9e5f-6a7b8c9d0e1f"
          },
          "name": {
            "type": "string",
            "description": "Customer name.",
            "nullable": true,
            "example": "Noura Al-Shammari"
          },
          "email": {
            "type": "string",
            "description": "Email, or `null`.",
            "nullable": true,
            "example": "noura@example.com"
          },
          "phone": {
            "type": "string",
            "description": "Mobile, or `null`.",
            "nullable": true,
            "example": "+966512345678"
          }
        },
        "additionalProperties": false,
        "description": "The customer as you know them. No national ID is returned. Before the customer completes a payment-link or QR checkout, this is a placeholder customer."
      },
      "MerchantOrderDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The Qasimha order id.",
            "format": "uuid",
            "example": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61"
          },
          "orderReferenceId": {
            "type": "string",
            "description": "Your order reference.",
            "nullable": true,
            "example": "MERCHANT-ORDER-12345"
          },
          "status": {
            "type": "string",
            "description": "Order status name. Values: Draft, New, Approved, Declined, Expired, Confirmed, FullyFulfilled, PartiallyFulfilled, FullyReversed, PartiallyReversed, Voided, Updated, AwaitingCheckout, Cancelled, Settled, WrittenOff.",
            "nullable": true,
            "example": "Confirmed"
          },
          "paymentType": {
            "type": "string",
            "description": "`PayByInstalments` for every order created through this API.",
            "nullable": true,
            "example": "PayByInstalments"
          },
          "channel": {
            "type": "string",
            "description": "`Online`, `InStore` or `QrCode`.",
            "nullable": true,
            "example": "Online"
          },
          "checkoutChannel": {
            "type": "string",
            "description": "How the hosted checkout was started: `Online` (payment session), `Pos`, `Qr` or `Link`; `null` for an order created outside the hosted checkout.",
            "nullable": true,
            "example": "Online"
          },
          "totalAmount": {
            "type": "number",
            "description": "Amount financed.",
            "format": "double",
            "example": 1278.5
          },
          "taxAmount": {
            "type": "number",
            "description": "Order-level tax.",
            "format": "double",
            "example": 163.5
          },
          "shippingAmount": {
            "type": "number",
            "description": "Shipping.",
            "format": "double",
            "example": 25
          },
          "discountAmount": {
            "type": "number",
            "description": "Order-level discount.",
            "format": "double",
            "example": 0
          },
          "currencyCode": {
            "type": "string",
            "description": "Currency of every amount.",
            "nullable": true,
            "example": "SAR"
          },
          "countryCode": {
            "type": "string",
            "description": "Country of the sale.",
            "nullable": true,
            "example": "SA"
          },
          "description": {
            "type": "string",
            "description": "Description you sent, or `null`.",
            "nullable": true,
            "example": "Order #12345 from the web store"
          },
          "instalments": {
            "type": "integer",
            "description": "Number of instalments in the plan. Before the customer picks a plan, your preferred number (4 by default).",
            "format": "int32",
            "example": 4
          },
          "downPaymentAmount": {
            "type": "number",
            "description": "Down payment taken at signing; 0 when the plan has none.",
            "format": "double",
            "example": 0
          },
          "approvedAmount": {
            "type": "number",
            "description": "Amount the credit decision approved; `null` until decided.",
            "format": "double",
            "nullable": true,
            "example": 1278.5
          },
          "capturedAmount": {
            "type": "number",
            "description": "What the customer has paid so far: the sum of paid instalments (same as `collectedAmount`).",
            "format": "double",
            "example": 319.63
          },
          "fulfilledAmount": {
            "type": "number",
            "description": "What you have shipped so far (sum of fulfilments).",
            "format": "double",
            "example": 0
          },
          "refundedAmount": {
            "type": "number",
            "description": "Total refunded to the customer (same as `reversedAmount`).",
            "format": "double",
            "example": 0
          },
          "refundableAmount": {
            "type": "number",
            "description": "What you can still refund: `collectedAmount − reversedAmount` when a repayment schedule exists, otherwise `fulfilledAmount − reversedAmount`.",
            "format": "double",
            "example": 319.63
          },
          "collectedAmount": {
            "type": "number",
            "description": "Sum of paid instalments.",
            "format": "double",
            "example": 319.63
          },
          "voidedAmount": {
            "type": "number",
            "description": "Outstanding balance released when the order was voided; 0 otherwise.",
            "format": "double",
            "example": 0
          },
          "reversedAmount": {
            "type": "number",
            "description": "Total refunded.",
            "format": "double",
            "example": 0
          },
          "paymentUrl": {
            "type": "string",
            "description": "The hosted checkout page of the order, or `null`.",
            "nullable": true,
            "example": "https://.../pay/3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61?lang=ar"
          },
          "providerSessionId": {
            "type": "string",
            "description": "Payment-provider session reference; `null` or empty on the hosted checkout.",
            "nullable": true,
            "example": null
          },
          "providerOrderId": {
            "type": "string",
            "description": "Payment-provider order reference; `null` on the hosted checkout.",
            "nullable": true,
            "example": null
          },
          "createdAt": {
            "type": "string",
            "description": "When the order was created (UTC).",
            "format": "date-time",
            "example": "2026-10-20T09:00:00Z"
          },
          "confirmedAt": {
            "type": "string",
            "description": "When the order became `Confirmed` (UTC), or `null`.",
            "format": "date-time",
            "nullable": true,
            "example": "2026-10-20T09:25:00Z"
          },
          "fulfilledAt": {
            "type": "string",
            "description": "When the order became `FullyFulfilled` (UTC), or `null`.",
            "format": "date-time",
            "nullable": true,
            "example": null
          },
          "voidedAt": {
            "type": "string",
            "description": "When the order was voided (UTC), or `null`.",
            "format": "date-time",
            "nullable": true,
            "example": null
          },
          "reversedAt": {
            "type": "string",
            "description": "When the order became `FullyReversed` (UTC), or `null`.",
            "format": "date-time",
            "nullable": true,
            "example": null
          },
          "settledAt": {
            "type": "string",
            "description": "When the customer finished repaying (UTC), or `null`.",
            "format": "date-time",
            "nullable": true,
            "example": null
          },
          "checkoutExpiresAt": {
            "type": "string",
            "description": "Deadline of the hosted checkout (UTC), two hours after creation; `null` for an order created outside it.",
            "format": "date-time",
            "nullable": true,
            "example": "2026-10-20T11:00:00Z"
          },
          "expiresAt": {
            "type": "string",
            "description": "When the order expired (UTC); `null` unless its status is `Expired`.",
            "format": "date-time",
            "nullable": true,
            "example": null
          },
          "ejarContractNumber": {
            "type": "string",
            "description": "Ejar contract number for a real-estate merchant; `null` otherwise.",
            "nullable": true,
            "example": null
          },
          "notificationLanguage": {
            "type": "string",
            "description": "Language the customer is messaged in, `ar` or `en`; `null` when not set.",
            "nullable": true,
            "example": "ar"
          },
          "contractNumber": {
            "type": "string",
            "description": "Number printed on the financing contract; `null` until the contract is issued.",
            "nullable": true,
            "example": "01-26293-0012345"
          },
          "customer": {
            "allOf": [
              {
                "$ref": "#/components/schemas/MerchantOrderCustomerDto"
              }
            ],
            "description": "The customer.",
            "example": {
              "id": "8d2e4f60-7a1b-4c3d-9e5f-6a7b8c9d0e1f",
              "name": "Noura Al-Shammari",
              "email": "noura@example.com",
              "phone": "+966512345678"
            }
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MerchantOrderItemDto"
            },
            "description": "Basket lines.",
            "nullable": true,
            "example": [
              {
                "referenceId": "LINE-1",
                "name": "Wireless Headphones",
                "type": "physical",
                "sku": "SKU-1001",
                "quantity": 1,
                "unitPrice": 1000,
                "totalAmount": 1000,
                "taxAmount": 0,
                "discountAmount": 0,
                "imageUrl": "https://shop.example/images/sku-1001.jpg"
              }
            ]
          },
          "installments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MerchantInstallmentDto"
            },
            "description": "The repayment schedule, in order. Empty until the customer has chosen a plan.",
            "nullable": true,
            "example": [
              {
                "sequence": 1,
                "dueDate": "2026-10-20T00:00:00Z",
                "amount": 319.63,
                "principal": 319.63,
                "profit": 0,
                "status": "Paid",
                "paidAmount": 319.63,
                "paidAt": "2026-10-20T09:25:00Z",
                "providerRef": "chg_TS02A1520261020x8Y91234567"
              }
            ]
          },
          "fulfillments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MerchantFulfillmentDto"
            },
            "description": "Fulfilments you recorded, oldest first.",
            "nullable": true,
            "example": [
              {
                "id": "b7c8d9e0-1f2a-4b3c-8d4e-5f6a7b8c9d0e",
                "amount": 1278.5,
                "currencyCode": "SAR",
                "shippingCompany": "Aramex",
                "trackingNumber": "ARX123456789",
                "trackingUrl": "https://shop.example/track/ARX123456789",
                "providerCaptureId": null,
                "createdAt": "2026-10-21T08:00:00Z"
              }
            ]
          },
          "reversals": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MerchantReversalDto"
            },
            "description": "Refunds you recorded, oldest first.",
            "nullable": true,
            "example": [
              {
                "id": "c1d2e3f4-5a6b-4c7d-8e9f-0a1b2c3d4e5f",
                "amount": 300,
                "currencyCode": "SAR",
                "comment": "Customer return",
                "fulfillmentId": null,
                "providerRefundId": null,
                "createdAt": "2026-10-25T10:00:00Z"
              }
            ]
          },
          "voids": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MerchantVoidDto"
            },
            "description": "Void records, oldest first.",
            "nullable": true,
            "example": [
              {
                "id": "d4e5f6a7-b8c9-4d0e-9f1a-2b3c4d5e6f7a",
                "amount": 958.87,
                "currencyCode": "SAR",
                "reason": null,
                "providerCancelId": null,
                "createdAt": "2026-10-22T12:00:00Z"
              }
            ]
          },
          "statusHistory": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MerchantStatusChangeDto"
            },
            "description": "Every status transition, oldest first.",
            "nullable": true,
            "example": [
              {
                "fromStatus": "AwaitingCheckout",
                "toStatus": "Confirmed",
                "trigger": "CheckoutCompleted",
                "createdAt": "2026-10-20T09:25:00Z"
              }
            ]
          }
        },
        "additionalProperties": false,
        "description": "Your view of one order: what was bought, what the customer owes and has paid, what you have shipped and refunded, and how the order got where it is. Enum values are names, never numbers."
      },
      "MerchantOrderDtoApiResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "`true` when the request succeeded, `false` on every error.",
            "example": true
          },
          "uuid": {
            "type": "string",
            "description": "Identifier of this response. Quote it when you contact support about a request.",
            "nullable": true,
            "example": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f"
          },
          "code": {
            "type": "string",
            "description": "Machine-readable outcome, for example `SUCCESS`, `SESSION_CREATED` or `VALIDATION_ERROR`. Branch on this, never on `message`.",
            "nullable": true,
            "example": "SUCCESS"
          },
          "message": {
            "type": "string",
            "description": "Human-readable text for the outcome. It may change and may be localised; do not parse it.",
            "nullable": true,
            "example": "Operation completed successfully"
          },
          "data": {
            "allOf": [
              {
                "$ref": "#/components/schemas/MerchantOrderDto"
              }
            ],
            "description": "The result. Its shape is documented for each operation; `null` on most errors.",
            "example": {
              "id": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61",
              "orderReferenceId": "MERCHANT-ORDER-12345",
              "status": "Confirmed",
              "paymentType": "PayByInstalments",
              "channel": "Online",
              "checkoutChannel": "Online",
              "totalAmount": 1278.5,
              "taxAmount": 163.5,
              "shippingAmount": 25,
              "discountAmount": 0,
              "currencyCode": "SAR",
              "countryCode": "SA",
              "description": "Order #12345 from the web store",
              "instalments": 4,
              "downPaymentAmount": 0,
              "approvedAmount": 1278.5,
              "capturedAmount": 319.63,
              "fulfilledAmount": 0,
              "refundedAmount": 0,
              "refundableAmount": 319.63,
              "collectedAmount": 319.63,
              "voidedAmount": 0,
              "reversedAmount": 0,
              "paymentUrl": "https://.../pay/3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61?lang=ar",
              "providerSessionId": null,
              "providerOrderId": null,
              "createdAt": "2026-10-20T09:00:00Z",
              "confirmedAt": "2026-10-20T09:25:00Z",
              "fulfilledAt": null,
              "voidedAt": null,
              "reversedAt": null,
              "settledAt": null,
              "checkoutExpiresAt": "2026-10-20T11:00:00Z",
              "expiresAt": null,
              "ejarContractNumber": null,
              "notificationLanguage": "ar",
              "contractNumber": "01-26293-0012345",
              "customer": {
                "id": "8d2e4f60-7a1b-4c3d-9e5f-6a7b8c9d0e1f",
                "name": "Noura Al-Shammari",
                "email": "noura@example.com",
                "phone": "+966512345678"
              },
              "items": [
                {
                  "referenceId": "LINE-1",
                  "name": "Wireless Headphones",
                  "type": "physical",
                  "sku": "SKU-1001",
                  "quantity": 1,
                  "unitPrice": 1000,
                  "totalAmount": 1000,
                  "taxAmount": 0,
                  "discountAmount": 0,
                  "imageUrl": "https://shop.example/images/sku-1001.jpg"
                }
              ],
              "installments": [
                {
                  "sequence": 1,
                  "dueDate": "2026-10-20T00:00:00Z",
                  "amount": 319.63,
                  "principal": 319.63,
                  "profit": 0,
                  "status": "Paid",
                  "paidAmount": 319.63,
                  "paidAt": "2026-10-20T09:25:00Z",
                  "providerRef": "chg_TS02A1520261020x8Y91234567"
                }
              ],
              "fulfillments": [
                {
                  "id": "b7c8d9e0-1f2a-4b3c-8d4e-5f6a7b8c9d0e",
                  "amount": 1278.5,
                  "currencyCode": "SAR",
                  "shippingCompany": "Aramex",
                  "trackingNumber": "ARX123456789",
                  "trackingUrl": "https://shop.example/track/ARX123456789",
                  "providerCaptureId": null,
                  "createdAt": "2026-10-21T08:00:00Z"
                }
              ],
              "reversals": [
                {
                  "id": "c1d2e3f4-5a6b-4c7d-8e9f-0a1b2c3d4e5f",
                  "amount": 300,
                  "currencyCode": "SAR",
                  "comment": "Customer return",
                  "fulfillmentId": null,
                  "providerRefundId": null,
                  "createdAt": "2026-10-25T10:00:00Z"
                }
              ],
              "voids": [
                {
                  "id": "d4e5f6a7-b8c9-4d0e-9f1a-2b3c4d5e6f7a",
                  "amount": 958.87,
                  "currencyCode": "SAR",
                  "reason": null,
                  "providerCancelId": null,
                  "createdAt": "2026-10-22T12:00:00Z"
                }
              ],
              "statusHistory": [
                {
                  "fromStatus": "AwaitingCheckout",
                  "toStatus": "Confirmed",
                  "trigger": "CheckoutCompleted",
                  "createdAt": "2026-10-20T09:25:00Z"
                }
              ]
            }
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiError"
            },
            "description": "Field-level problems. `null` on success; on failure an array, often empty, of `{ field, message }`.",
            "nullable": true,
            "example": null
          }
        },
        "additionalProperties": false,
        "description": "The envelope every response uses. `success` and `code` tell you the outcome; `data` carries the result."
      },
      "MerchantOrderFulfilmentResultDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The Qasimha order id.",
            "format": "uuid",
            "example": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61"
          },
          "status": {
            "type": "integer",
            "description": "Order status after the call, as a number (not a name, unlike the order reads): 0 Draft, 1 New, 2 Approved, 3 Declined, 4 Expired, 5 Confirmed, 6 FullyFulfilled, 7 PartiallyFulfilled, 8 FullyReversed, 9 PartiallyReversed, 10 Voided, 11 Updated, 12 AwaitingCheckout, 13 Cancelled, 14 Settled, 15 WrittenOff. `6` (FullyFulfilled) or `7` (PartiallyFulfilled).",
            "format": "int32",
            "example": 6
          },
          "fulfilledAmount": {
            "type": "number",
            "description": "Total fulfilled so far, this fulfilment included.",
            "format": "double",
            "example": 1278.5
          }
        },
        "additionalProperties": false,
        "description": "The order after a fulfilment."
      },
      "MerchantOrderFulfilmentResultDtoApiResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "`true` when the request succeeded, `false` on every error.",
            "example": true
          },
          "uuid": {
            "type": "string",
            "description": "Identifier of this response. Quote it when you contact support about a request.",
            "nullable": true,
            "example": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f"
          },
          "code": {
            "type": "string",
            "description": "Machine-readable outcome, for example `SUCCESS`, `SESSION_CREATED` or `VALIDATION_ERROR`. Branch on this, never on `message`.",
            "nullable": true,
            "example": "SUCCESS"
          },
          "message": {
            "type": "string",
            "description": "Human-readable text for the outcome. It may change and may be localised; do not parse it.",
            "nullable": true,
            "example": "Operation completed successfully"
          },
          "data": {
            "allOf": [
              {
                "$ref": "#/components/schemas/MerchantOrderFulfilmentResultDto"
              }
            ],
            "description": "The result. Its shape is documented for each operation; `null` on most errors.",
            "example": {
              "id": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61",
              "status": 6,
              "fulfilledAmount": 1278.5
            }
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiError"
            },
            "description": "Field-level problems. `null` on success; on failure an array, often empty, of `{ field, message }`.",
            "nullable": true,
            "example": null
          }
        },
        "additionalProperties": false,
        "description": "The envelope every response uses. `success` and `code` tell you the outcome; `data` carries the result."
      },
      "MerchantOrderItemDto": {
        "type": "object",
        "properties": {
          "referenceId": {
            "type": "string",
            "description": "Your line identifier (for a payment link: the SKU, or the name).",
            "nullable": true,
            "example": "LINE-1"
          },
          "name": {
            "type": "string",
            "description": "Product name.",
            "nullable": true,
            "example": "Wireless Headphones"
          },
          "type": {
            "type": "string",
            "description": "Line type as sent, for example `physical`.",
            "nullable": true,
            "example": "physical"
          },
          "sku": {
            "type": "string",
            "description": "SKU, or `null`.",
            "nullable": true,
            "example": "SKU-1001"
          },
          "quantity": {
            "type": "integer",
            "description": "Number of units.",
            "format": "int32",
            "example": 1
          },
          "unitPrice": {
            "type": "number",
            "description": "Price of one unit.",
            "format": "double",
            "example": 1000
          },
          "totalAmount": {
            "type": "number",
            "description": "Line total.",
            "format": "double",
            "example": 1000
          },
          "taxAmount": {
            "type": "number",
            "description": "Tax on the line.",
            "format": "double",
            "example": 0
          },
          "discountAmount": {
            "type": "number",
            "description": "Discount on the line.",
            "format": "double",
            "example": 0
          },
          "imageUrl": {
            "type": "string",
            "description": "Product image, or `null`.",
            "nullable": true,
            "example": "https://shop.example/images/sku-1001.jpg"
          }
        },
        "additionalProperties": false,
        "description": "A basket line as stored on the order."
      },
      "MerchantOrderPageDto": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MerchantOrderSummaryDto"
            },
            "description": "The orders on this page, newest first.",
            "nullable": true,
            "example": [
              {
                "id": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61",
                "orderReferenceId": "MERCHANT-ORDER-12345",
                "status": "Confirmed",
                "policyCode": "BNPL_POLICY_V41",
                "policyType": "BNPL",
                "totalAmount": 1278.5,
                "currencyCode": "SAR",
                "createdAt": "2026-10-20T09:00:00Z",
                "customerId": "8d2e4f60-7a1b-4c3d-9e5f-6a7b8c9d0e1f",
                "customerName": "Noura Al-Shammari",
                "customerEmail": "noura@example.com",
                "customerPhone": "+966512345678",
                "instalments": 4,
                "channel": "Online",
                "capturedAmount": 0,
                "refundedAmount": 0,
                "ejarContractNumber": null
              }
            ]
          },
          "pagination": {
            "allOf": [
              {
                "$ref": "#/components/schemas/MerchantPaginationDto"
              }
            ],
            "description": "Paging information.",
            "example": {
              "totalItems": 57,
              "page": 1,
              "pageSize": 20,
              "totalPages": 3
            }
          }
        },
        "additionalProperties": false,
        "description": "One page of orders."
      },
      "MerchantOrderPageDtoApiResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "`true` when the request succeeded, `false` on every error.",
            "example": true
          },
          "uuid": {
            "type": "string",
            "description": "Identifier of this response. Quote it when you contact support about a request.",
            "nullable": true,
            "example": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f"
          },
          "code": {
            "type": "string",
            "description": "Machine-readable outcome, for example `SUCCESS`, `SESSION_CREATED` or `VALIDATION_ERROR`. Branch on this, never on `message`.",
            "nullable": true,
            "example": "SUCCESS"
          },
          "message": {
            "type": "string",
            "description": "Human-readable text for the outcome. It may change and may be localised; do not parse it.",
            "nullable": true,
            "example": "Operation completed successfully"
          },
          "data": {
            "allOf": [
              {
                "$ref": "#/components/schemas/MerchantOrderPageDto"
              }
            ],
            "description": "The result. Its shape is documented for each operation; `null` on most errors.",
            "example": {
              "data": [
                {
                  "id": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61",
                  "orderReferenceId": "MERCHANT-ORDER-12345",
                  "status": "Confirmed",
                  "policyCode": "BNPL_POLICY_V41",
                  "policyType": "BNPL",
                  "totalAmount": 1278.5,
                  "currencyCode": "SAR",
                  "createdAt": "2026-10-20T09:00:00Z",
                  "customerId": "8d2e4f60-7a1b-4c3d-9e5f-6a7b8c9d0e1f",
                  "customerName": "Noura Al-Shammari",
                  "customerEmail": "noura@example.com",
                  "customerPhone": "+966512345678",
                  "instalments": 4,
                  "channel": "Online",
                  "capturedAmount": 0,
                  "refundedAmount": 0,
                  "ejarContractNumber": null
                }
              ],
              "pagination": {
                "totalItems": 57,
                "page": 1,
                "pageSize": 20,
                "totalPages": 3
              }
            }
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiError"
            },
            "description": "Field-level problems. `null` on success; on failure an array, often empty, of `{ field, message }`.",
            "nullable": true,
            "example": null
          }
        },
        "additionalProperties": false,
        "description": "The envelope every response uses. `success` and `code` tell you the outcome; `data` carries the result."
      },
      "MerchantOrderRefundResultDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The Qasimha order id.",
            "format": "uuid",
            "example": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61"
          },
          "status": {
            "type": "integer",
            "description": "Order status after the call, as a number (not a name, unlike the order reads): 0 Draft, 1 New, 2 Approved, 3 Declined, 4 Expired, 5 Confirmed, 6 FullyFulfilled, 7 PartiallyFulfilled, 8 FullyReversed, 9 PartiallyReversed, 10 Voided, 11 Updated, 12 AwaitingCheckout, 13 Cancelled, 14 Settled, 15 WrittenOff. `8` (FullyReversed) or `9` (PartiallyReversed).",
            "format": "int32",
            "example": 9
          },
          "refundedAmount": {
            "type": "number",
            "description": "Total refunded so far, this refund included.",
            "format": "double",
            "example": 300
          }
        },
        "additionalProperties": false,
        "description": "The order after a refund."
      },
      "MerchantOrderRefundResultDtoApiResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "`true` when the request succeeded, `false` on every error.",
            "example": true
          },
          "uuid": {
            "type": "string",
            "description": "Identifier of this response. Quote it when you contact support about a request.",
            "nullable": true,
            "example": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f"
          },
          "code": {
            "type": "string",
            "description": "Machine-readable outcome, for example `SUCCESS`, `SESSION_CREATED` or `VALIDATION_ERROR`. Branch on this, never on `message`.",
            "nullable": true,
            "example": "SUCCESS"
          },
          "message": {
            "type": "string",
            "description": "Human-readable text for the outcome. It may change and may be localised; do not parse it.",
            "nullable": true,
            "example": "Operation completed successfully"
          },
          "data": {
            "allOf": [
              {
                "$ref": "#/components/schemas/MerchantOrderRefundResultDto"
              }
            ],
            "description": "The result. Its shape is documented for each operation; `null` on most errors.",
            "example": {
              "id": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61",
              "status": 9,
              "refundedAmount": 300
            }
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiError"
            },
            "description": "Field-level problems. `null` on success; on failure an array, often empty, of `{ field, message }`.",
            "nullable": true,
            "example": null
          }
        },
        "additionalProperties": false,
        "description": "The envelope every response uses. `success` and `code` tell you the outcome; `data` carries the result."
      },
      "MerchantOrderSummaryDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The Qasimha order id.",
            "format": "uuid",
            "example": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61"
          },
          "orderReferenceId": {
            "type": "string",
            "description": "Your order reference.",
            "nullable": true,
            "example": "MERCHANT-ORDER-12345"
          },
          "status": {
            "type": "string",
            "description": "Order status name. Values: Draft, New, Approved, Declined, Expired, Confirmed, FullyFulfilled, PartiallyFulfilled, FullyReversed, PartiallyReversed, Voided, Updated, AwaitingCheckout, Cancelled, Settled, WrittenOff.",
            "nullable": true,
            "example": "Confirmed"
          },
          "policyCode": {
            "type": "string",
            "description": "Code of the credit policy that assessed the order; `null` until assessed.",
            "nullable": true,
            "example": "BNPL_POLICY_V41"
          },
          "policyType": {
            "type": "string",
            "description": "`BNPL` or `Microfinance`, from the product the decision chose; `null` until assessed.",
            "nullable": true,
            "example": "BNPL"
          },
          "totalAmount": {
            "type": "number",
            "description": "Amount financed.",
            "format": "double",
            "example": 1278.5
          },
          "currencyCode": {
            "type": "string",
            "description": "Currency of the amounts.",
            "nullable": true,
            "example": "SAR"
          },
          "createdAt": {
            "type": "string",
            "description": "When the order was created (UTC).",
            "format": "date-time",
            "example": "2026-10-20T09:00:00Z"
          },
          "customerId": {
            "type": "string",
            "description": "Qasimha customer id.",
            "format": "uuid",
            "example": "8d2e4f60-7a1b-4c3d-9e5f-6a7b8c9d0e1f"
          },
          "customerName": {
            "type": "string",
            "description": "Customer name.",
            "nullable": true,
            "example": "Noura Al-Shammari"
          },
          "customerEmail": {
            "type": "string",
            "description": "Customer email, or `null`.",
            "nullable": true,
            "example": "noura@example.com"
          },
          "customerPhone": {
            "type": "string",
            "description": "Customer mobile, unmasked, or `null`.",
            "nullable": true,
            "example": "+966512345678"
          },
          "instalments": {
            "type": "integer",
            "description": "Number of instalments in the plan.",
            "format": "int32",
            "example": 4
          },
          "channel": {
            "type": "string",
            "description": "`Online`, `InStore` or `QrCode`.",
            "nullable": true,
            "example": "Online"
          },
          "capturedAmount": {
            "type": "number",
            "description": "In this list, the amount you have fulfilled (shipped) so far: the order detail's `fulfilledAmount`. The order detail's own `capturedAmount` is what the customer has paid.",
            "format": "double",
            "example": 0
          },
          "refundedAmount": {
            "type": "number",
            "description": "Total refunded to the customer.",
            "format": "double",
            "example": 0
          },
          "ejarContractNumber": {
            "type": "string",
            "description": "Ejar contract number for a real-estate merchant; `null` otherwise.",
            "nullable": true,
            "example": null
          }
        },
        "additionalProperties": false,
        "description": "One order in the list."
      },
      "MerchantPaginationDto": {
        "type": "object",
        "properties": {
          "totalItems": {
            "type": "integer",
            "description": "Rows matching the filters, across all pages.",
            "format": "int32",
            "example": 57
          },
          "page": {
            "type": "integer",
            "description": "This page, starting at 1.",
            "format": "int32",
            "example": 1
          },
          "pageSize": {
            "type": "integer",
            "description": "Rows per page, as requested.",
            "format": "int32",
            "example": 20
          },
          "totalPages": {
            "type": "integer",
            "description": "`ceil(totalItems / pageSize)`.",
            "format": "int32",
            "example": 3
          }
        },
        "additionalProperties": false,
        "description": "Paging of a list."
      },
      "MerchantRefundRequest": {
        "required": [
          "amount"
        ],
        "type": "object",
        "properties": {
          "amount": {
            "type": "number",
            "description": "Amount to refund to the customer: greater than 0 and at most the order's `refundableAmount`.",
            "format": "double",
            "example": 300
          },
          "reason": {
            "type": "string",
            "description": "Why you refund. Optional; stored as the refund's `comment`.",
            "nullable": true,
            "example": "Customer return"
          }
        },
        "additionalProperties": false,
        "description": "Request body of `POST /api/v1/merchant/orders/{id}/refund`."
      },
      "MerchantReversalDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Refund id.",
            "format": "uuid",
            "example": "c1d2e3f4-5a6b-4c7d-8e9f-0a1b2c3d4e5f"
          },
          "amount": {
            "type": "number",
            "description": "Amount refunded.",
            "format": "double",
            "example": 300
          },
          "currencyCode": {
            "type": "string",
            "description": "Currency.",
            "nullable": true,
            "example": "SAR"
          },
          "comment": {
            "type": "string",
            "description": "The `reason` you sent, or `null`.",
            "nullable": true,
            "example": "Customer return"
          },
          "fulfillmentId": {
            "type": "string",
            "description": "Fulfilment this refund belongs to; `null` for refunds made through this API.",
            "format": "uuid",
            "nullable": true,
            "example": null
          },
          "providerRefundId": {
            "type": "string",
            "description": "Payment-provider refund reference; `null` on the hosted checkout.",
            "nullable": true,
            "example": null
          },
          "createdAt": {
            "type": "string",
            "description": "When the refund was recorded (UTC).",
            "format": "date-time",
            "example": "2026-10-25T10:00:00Z"
          }
        },
        "additionalProperties": false,
        "description": "One refund you recorded."
      },
      "MerchantSettlementOverviewDto": {
        "type": "object",
        "properties": {
          "currency": {
            "type": "string",
            "description": "Your account currency.",
            "nullable": true,
            "example": "SAR"
          },
          "iban": {
            "type": "string",
            "description": "Payout IBAN on file, or `null`.",
            "nullable": true,
            "example": "SA0380000000608010167519"
          },
          "beneficiaryName": {
            "type": "string",
            "description": "Payout beneficiary name, or `null`.",
            "nullable": true,
            "example": "Example Trading Co."
          },
          "settlementCycle": {
            "type": "string",
            "description": "Payout cadence. Currently always `Once a week`.",
            "nullable": true,
            "example": "Once a week"
          },
          "settlementModel": {
            "type": "string",
            "description": "Payout model. Currently always `Flexible`.",
            "nullable": true,
            "example": "Flexible"
          },
          "minimumSettlementAmount": {
            "type": "number",
            "description": "Smallest balance paid out. Currently 2500.",
            "format": "double",
            "example": 2500
          },
          "notPaidYet": {
            "type": "number",
            "description": "Net amount owed to you for orders in `Confirmed`, `PartiallyFulfilled`, `FullyFulfilled` or `Updated`: per order `(totalAmount − refunds − voids)` less your MDR fee and 15% VAT on that fee.",
            "format": "double",
            "example": 8412.4
          },
          "currentBalance": {
            "type": "number",
            "description": "Same figure as `notPaidYet`.",
            "format": "double",
            "example": 8412.4
          },
          "remainingForNextSettlement": {
            "type": "number",
            "description": "`max(0, minimumSettlementAmount − notPaidYet)`.",
            "format": "double",
            "example": 0
          },
          "belowMinimum": {
            "type": "boolean",
            "description": "`true` when `notPaidYet` is below the minimum.",
            "example": false
          },
          "upcomingPayment": {
            "type": "number",
            "description": "`notPaidYet`, or 0 when it is below the minimum.",
            "format": "double",
            "example": 8412.4
          },
          "expectedBy": {
            "type": "string",
            "description": "Next Wednesday (UTC midnight); a week ahead when today is Wednesday.",
            "format": "date-time",
            "example": "2026-10-21T00:00:00Z"
          },
          "nextCombineDate": {
            "type": "string",
            "description": "Next Saturday (UTC midnight); a week ahead when today is Saturday.",
            "format": "date-time",
            "example": "2026-10-24T00:00:00Z"
          },
          "lastSettledAmount": {
            "type": "number",
            "description": "Amount of your latest settlement; 0 when none.",
            "format": "double",
            "example": 12450.75
          },
          "lastSettlementDate": {
            "type": "string",
            "description": "Date of your latest settlement (UTC), or `null`.",
            "format": "date-time",
            "nullable": true,
            "example": "2026-10-15T06:00:00Z"
          },
          "totalSettled": {
            "type": "number",
            "description": "Sum of all your settlements.",
            "format": "double",
            "example": 96320.1
          },
          "settlementCount": {
            "type": "integer",
            "description": "Number of your settlements.",
            "format": "int32",
            "example": 9
          }
        },
        "additionalProperties": false,
        "description": "Headline settlement figures."
      },
      "MerchantSettlementOverviewDtoApiResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "`true` when the request succeeded, `false` on every error.",
            "example": true
          },
          "uuid": {
            "type": "string",
            "description": "Identifier of this response. Quote it when you contact support about a request.",
            "nullable": true,
            "example": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f"
          },
          "code": {
            "type": "string",
            "description": "Machine-readable outcome, for example `SUCCESS`, `SESSION_CREATED` or `VALIDATION_ERROR`. Branch on this, never on `message`.",
            "nullable": true,
            "example": "SUCCESS"
          },
          "message": {
            "type": "string",
            "description": "Human-readable text for the outcome. It may change and may be localised; do not parse it.",
            "nullable": true,
            "example": "Operation completed successfully"
          },
          "data": {
            "allOf": [
              {
                "$ref": "#/components/schemas/MerchantSettlementOverviewDto"
              }
            ],
            "description": "The result. Its shape is documented for each operation; `null` on most errors.",
            "example": {
              "currency": "SAR",
              "iban": "SA0380000000608010167519",
              "beneficiaryName": "Example Trading Co.",
              "settlementCycle": "Once a week",
              "settlementModel": "Flexible",
              "minimumSettlementAmount": 2500,
              "notPaidYet": 8412.4,
              "currentBalance": 8412.4,
              "remainingForNextSettlement": 0,
              "belowMinimum": false,
              "upcomingPayment": 8412.4,
              "expectedBy": "2026-10-21T00:00:00Z",
              "nextCombineDate": "2026-10-24T00:00:00Z",
              "lastSettledAmount": 12450.75,
              "lastSettlementDate": "2026-10-15T06:00:00Z",
              "totalSettled": 96320.1,
              "settlementCount": 9
            }
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiError"
            },
            "description": "Field-level problems. `null` on success; on failure an array, often empty, of `{ field, message }`.",
            "nullable": true,
            "example": null
          }
        },
        "additionalProperties": false,
        "description": "The envelope every response uses. `success` and `code` tell you the outcome; `data` carries the result."
      },
      "MerchantSettlementPageDto": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MerchantSettlementRowDto"
            },
            "description": "The settlements on this page, newest `settledAt` first.",
            "nullable": true,
            "example": [
              {
                "merchantId": "5a6b7c8d-9e0f-4a1b-8c2d-3e4f5a6b7c8d",
                "providerSettlementId": "STL-2026-10-15-0007",
                "amount": 12450.75,
                "currencyCode": "SAR",
                "settledAt": "2026-10-15T06:00:00Z",
                "status": "SETTLED",
                "orderCount": 14,
                "metadata": null,
                "id": "e1f2a3b4-c5d6-4e7f-8a9b-0c1d2e3f4a5b",
                "createdAt": "2026-10-15T06:00:00Z",
                "createdBy": null,
                "updatedAt": null,
                "updatedBy": null,
                "isDeleted": false,
                "domainEvents": null
              }
            ]
          },
          "pagination": {
            "allOf": [
              {
                "$ref": "#/components/schemas/MerchantPaginationDto"
              }
            ],
            "description": "Paging information.",
            "example": {
              "totalItems": 57,
              "page": 1,
              "pageSize": 20,
              "totalPages": 3
            }
          }
        },
        "additionalProperties": false,
        "description": "One page of settlements."
      },
      "MerchantSettlementPageDtoApiResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "`true` when the request succeeded, `false` on every error.",
            "example": true
          },
          "uuid": {
            "type": "string",
            "description": "Identifier of this response. Quote it when you contact support about a request.",
            "nullable": true,
            "example": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f"
          },
          "code": {
            "type": "string",
            "description": "Machine-readable outcome, for example `SUCCESS`, `SESSION_CREATED` or `VALIDATION_ERROR`. Branch on this, never on `message`.",
            "nullable": true,
            "example": "SUCCESS"
          },
          "message": {
            "type": "string",
            "description": "Human-readable text for the outcome. It may change and may be localised; do not parse it.",
            "nullable": true,
            "example": "Operation completed successfully"
          },
          "data": {
            "allOf": [
              {
                "$ref": "#/components/schemas/MerchantSettlementPageDto"
              }
            ],
            "description": "The result. Its shape is documented for each operation; `null` on most errors.",
            "example": {
              "data": [
                {
                  "merchantId": "5a6b7c8d-9e0f-4a1b-8c2d-3e4f5a6b7c8d",
                  "providerSettlementId": "STL-2026-10-15-0007",
                  "amount": 12450.75,
                  "currencyCode": "SAR",
                  "settledAt": "2026-10-15T06:00:00Z",
                  "status": "SETTLED",
                  "orderCount": 14,
                  "metadata": null,
                  "id": "e1f2a3b4-c5d6-4e7f-8a9b-0c1d2e3f4a5b",
                  "createdAt": "2026-10-15T06:00:00Z",
                  "createdBy": null,
                  "updatedAt": null,
                  "updatedBy": null,
                  "isDeleted": false,
                  "domainEvents": null
                }
              ],
              "pagination": {
                "totalItems": 57,
                "page": 1,
                "pageSize": 20,
                "totalPages": 3
              }
            }
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiError"
            },
            "description": "Field-level problems. `null` on success; on failure an array, often empty, of `{ field, message }`.",
            "nullable": true,
            "example": null
          }
        },
        "additionalProperties": false,
        "description": "The envelope every response uses. `success` and `code` tell you the outcome; `data` carries the result."
      },
      "MerchantSettlementRowDto": {
        "type": "object",
        "properties": {
          "merchantId": {
            "type": "string",
            "description": "Your merchant id.",
            "format": "uuid",
            "nullable": true,
            "example": "5a6b7c8d-9e0f-4a1b-8c2d-3e4f5a6b7c8d"
          },
          "providerSettlementId": {
            "type": "string",
            "description": "Reference of the payout.",
            "nullable": true,
            "example": "STL-2026-10-15-0007"
          },
          "amount": {
            "type": "number",
            "description": "Net amount paid out in this batch.",
            "format": "double",
            "example": 12450.75
          },
          "currencyCode": {
            "type": "string",
            "description": "Currency.",
            "nullable": true,
            "example": "SAR"
          },
          "settledAt": {
            "type": "string",
            "description": "When the batch was paid (UTC).",
            "format": "date-time",
            "example": "2026-10-15T06:00:00Z"
          },
          "status": {
            "type": "string",
            "description": "Status of the batch as recorded, for example `SETTLED`.",
            "nullable": true,
            "example": "SETTLED"
          },
          "orderCount": {
            "type": "integer",
            "description": "Number of orders in the batch.",
            "format": "int32",
            "example": 14
          },
          "metadata": {
            "type": "string",
            "description": "Additional details as a JSON string, or `null`.",
            "nullable": true,
            "example": null
          },
          "id": {
            "type": "string",
            "description": "Settlement id.",
            "format": "uuid",
            "example": "e1f2a3b4-c5d6-4e7f-8a9b-0c1d2e3f4a5b"
          },
          "createdAt": {
            "type": "string",
            "description": "When the record was created (UTC).",
            "format": "date-time",
            "example": "2026-10-15T06:00:00Z"
          },
          "createdBy": {
            "type": "string",
            "description": "Internal bookkeeping; ignore.",
            "nullable": true,
            "example": null
          },
          "updatedAt": {
            "type": "string",
            "description": "When the record last changed (UTC), or `null`.",
            "format": "date-time",
            "nullable": true,
            "example": null
          },
          "updatedBy": {
            "type": "string",
            "description": "Internal bookkeeping; ignore.",
            "nullable": true,
            "example": null
          },
          "isDeleted": {
            "type": "boolean",
            "description": "Internal bookkeeping; always `false` here.",
            "example": false
          },
          "domainEvents": {
            "type": "array",
            "items": { },
            "description": "Internal bookkeeping; ignore it. Always sent as an empty array.",
            "nullable": true,
            "example": null
          }
        },
        "additionalProperties": false,
        "description": "One settlement (payout) batch."
      },
      "MerchantStatusChangeDto": {
        "type": "object",
        "properties": {
          "fromStatus": {
            "type": "string",
            "description": "Status before; `null` on the first row.",
            "nullable": true,
            "example": "AwaitingCheckout"
          },
          "toStatus": {
            "type": "string",
            "description": "Status after.",
            "nullable": true,
            "example": "Confirmed"
          },
          "trigger": {
            "type": "string",
            "description": "What caused it, for example `LinkCreated`, `CheckoutCompleted`, `MerchantConfirmed`, `Fulfilled`, `PartiallyFulfilled`, `Reversed`, `PartiallyReversed`, `Voided`, `LinkCancelled`.",
            "nullable": true,
            "example": "CheckoutCompleted"
          },
          "createdAt": {
            "type": "string",
            "description": "When it happened (UTC).",
            "format": "date-time",
            "example": "2026-10-20T09:25:00Z"
          }
        },
        "additionalProperties": false,
        "description": "One status transition in the order's history."
      },
      "MerchantUrlDto": {
        "type": "object",
        "properties": {
          "success": {
            "type": "string",
            "description": "Where the customer goes after a confirmed checkout. `orderId`, `orderReferenceId` and `status=confirmed` are appended to its query string. Absolute URL, at most 1024 characters, no `#fragment`; `https`, `http` only on `localhost` / `127.0.0.1`, or a custom app scheme such as `myshop://`. Required when `failure` or `cancel` is set.",
            "nullable": true,
            "example": "https://shop.example/checkout/qasimha/success"
          },
          "failure": {
            "type": "string",
            "description": "Where the customer goes when the checkout is declined, expires or fails (`status=declined`, `expired` or `failed` appended). Same URL rules as `success`. Required when `success` or `cancel` is set.",
            "nullable": true,
            "example": "https://shop.example/checkout/qasimha/failure"
          },
          "cancel": {
            "type": "string",
            "description": "Where the customer goes when they cancel (`status=cancelled` appended). Same URL rules. Optional: defaults to `failure`.",
            "nullable": true,
            "example": "https://shop.example/checkout/qasimha/cancel"
          },
          "notification": {
            "type": "string",
            "description": "Reserved. Leave it out or empty: any value is refused with `400 VALIDATION_ERROR`. Use webhooks for server-to-server notifications.",
            "nullable": true,
            "example": null
          }
        },
        "additionalProperties": false,
        "description": "Where the hosted checkout sends the customer when it ends. Every URL is optional, but `success` and `failure` go together. A return is a navigation, not a proof of payment: confirm with `GET /api/v1/merchant/orders/{id}` before you treat the order as paid."
      },
      "MerchantVoidDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Void id.",
            "format": "uuid",
            "example": "d4e5f6a7-b8c9-4d0e-9f1a-2b3c4d5e6f7a"
          },
          "amount": {
            "type": "number",
            "description": "Amount voided.",
            "format": "double",
            "example": 958.87
          },
          "currencyCode": {
            "type": "string",
            "description": "Currency.",
            "nullable": true,
            "example": "SAR"
          },
          "reason": {
            "type": "string",
            "description": "Reason, or `null`.",
            "nullable": true,
            "example": null
          },
          "providerCancelId": {
            "type": "string",
            "description": "Payment-provider reference, or `null`.",
            "nullable": true,
            "example": null
          },
          "createdAt": {
            "type": "string",
            "description": "When it was recorded (UTC).",
            "format": "date-time",
            "example": "2026-10-22T12:00:00Z"
          }
        },
        "additionalProperties": false,
        "description": "A void record. May be empty even for a `Voided` order: `voidedAmount` and `voidedAt` on the order are the authoritative figures."
      },
      "MerchantWebhookDeliveryDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Delivery id; use it with `.../deliveries/{deliveryId}/replay`.",
            "format": "uuid",
            "example": "f0e1d2c3-b4a5-4968-8776-655443322110"
          },
          "eventId": {
            "type": "string",
            "description": "Event id as a bare GUID. The `X-Qasimha-Event-Id` header carries the same id as `evt_` followed by the GUID's 32 hexadecimal digits.",
            "format": "uuid",
            "example": "9f8c1d2e-3a4b-5c6d-7e8f-90a1b2c3d4e5"
          },
          "eventType": {
            "type": "string",
            "description": "Event type, for example `order.confirmed`, or `ping` for a test event.",
            "nullable": true,
            "example": "order.confirmed"
          },
          "orderId": {
            "type": "string",
            "description": "Order the event is about; `null` for `ping`.",
            "format": "uuid",
            "nullable": true,
            "example": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61"
          },
          "orderReferenceId": {
            "type": "string",
            "description": "Your order reference; `null` for `ping`.",
            "nullable": true,
            "example": "MERCHANT-ORDER-12345"
          },
          "status": {
            "type": "string",
            "description": "`Pending` (waiting for its first attempt or a retry), `Succeeded` (your server answered 2xx) or `Failed` (every attempt failed, or the endpoint was deleted).",
            "nullable": true,
            "example": "Succeeded"
          },
          "attempts": {
            "type": "integer",
            "description": "Attempts made so far.",
            "format": "int32",
            "example": 1
          },
          "maxAttempts": {
            "type": "integer",
            "description": "Most attempts that will be made: the first plus five retries.",
            "format": "int32",
            "example": 6
          },
          "nextAttemptAt": {
            "type": "string",
            "description": "When the next attempt is due (UTC); `null` when none is planned.",
            "format": "date-time",
            "nullable": true,
            "example": null
          },
          "lastAttemptAt": {
            "type": "string",
            "description": "When the last attempt was made (UTC), or `null`.",
            "format": "date-time",
            "nullable": true,
            "example": "2026-10-20T09:25:02Z"
          },
          "lastStatusCode": {
            "type": "integer",
            "description": "HTTP status your server answered on the last attempt; `null` when it did not answer.",
            "format": "int32",
            "nullable": true,
            "example": 200
          },
          "lastError": {
            "type": "string",
            "description": "Why the last attempt failed (timeout, connection error, non-2xx), or `null`.",
            "nullable": true,
            "example": null
          },
          "responseSnippet": {
            "type": "string",
            "description": "First 1000 characters of your server's last response body, or `null`.",
            "nullable": true,
            "example": "{\"received\":true}"
          },
          "durationMs": {
            "type": "integer",
            "description": "How long the last attempt took, in milliseconds, or `null`.",
            "format": "int32",
            "nullable": true,
            "example": 184
          },
          "succeededAt": {
            "type": "string",
            "description": "When a 2xx was received (UTC), or `null`.",
            "format": "date-time",
            "nullable": true,
            "example": "2026-10-20T09:25:02Z"
          },
          "replayOfId": {
            "type": "string",
            "description": "The delivery this one replays; `null` for an original delivery.",
            "format": "uuid",
            "nullable": true,
            "example": null
          },
          "createdAt": {
            "type": "string",
            "description": "When the delivery was queued (UTC).",
            "format": "date-time",
            "example": "2026-10-20T09:25:01Z"
          }
        },
        "additionalProperties": false,
        "description": "One event delivered (or being delivered) to one endpoint, with the outcome of its attempts."
      },
      "MerchantWebhookDeliveryDtoApiResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "`true` when the request succeeded, `false` on every error.",
            "example": true
          },
          "uuid": {
            "type": "string",
            "description": "Identifier of this response. Quote it when you contact support about a request.",
            "nullable": true,
            "example": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f"
          },
          "code": {
            "type": "string",
            "description": "Machine-readable outcome, for example `SUCCESS`, `SESSION_CREATED` or `VALIDATION_ERROR`. Branch on this, never on `message`.",
            "nullable": true,
            "example": "SUCCESS"
          },
          "message": {
            "type": "string",
            "description": "Human-readable text for the outcome. It may change and may be localised; do not parse it.",
            "nullable": true,
            "example": "Operation completed successfully"
          },
          "data": {
            "allOf": [
              {
                "$ref": "#/components/schemas/MerchantWebhookDeliveryDto"
              }
            ],
            "description": "The result. Its shape is documented for each operation; `null` on most errors.",
            "example": {
              "id": "f0e1d2c3-b4a5-4968-8776-655443322110",
              "eventId": "9f8c1d2e-3a4b-5c6d-7e8f-90a1b2c3d4e5",
              "eventType": "order.confirmed",
              "orderId": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61",
              "orderReferenceId": "MERCHANT-ORDER-12345",
              "status": "Succeeded",
              "attempts": 1,
              "maxAttempts": 6,
              "nextAttemptAt": null,
              "lastAttemptAt": "2026-10-20T09:25:02Z",
              "lastStatusCode": 200,
              "lastError": null,
              "responseSnippet": "{\"received\":true}",
              "durationMs": 184,
              "succeededAt": "2026-10-20T09:25:02Z",
              "replayOfId": null,
              "createdAt": "2026-10-20T09:25:01Z"
            }
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiError"
            },
            "description": "Field-level problems. `null` on success; on failure an array, often empty, of `{ field, message }`.",
            "nullable": true,
            "example": null
          }
        },
        "additionalProperties": false,
        "description": "The envelope every response uses. `success` and `code` tell you the outcome; `data` carries the result."
      },
      "MerchantWebhookDeliveryPageDto": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MerchantWebhookDeliveryDto"
            },
            "description": "Deliveries on this page, newest first.",
            "nullable": true,
            "example": [
              {
                "id": "f0e1d2c3-b4a5-4968-8776-655443322110",
                "eventId": "9f8c1d2e-3a4b-5c6d-7e8f-90a1b2c3d4e5",
                "eventType": "order.confirmed",
                "orderId": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61",
                "orderReferenceId": "MERCHANT-ORDER-12345",
                "status": "Succeeded",
                "attempts": 1,
                "maxAttempts": 6,
                "nextAttemptAt": null,
                "lastAttemptAt": "2026-10-20T09:25:02Z",
                "lastStatusCode": 200,
                "lastError": null,
                "responseSnippet": "{\"received\":true}",
                "durationMs": 184,
                "succeededAt": "2026-10-20T09:25:02Z",
                "replayOfId": null,
                "createdAt": "2026-10-20T09:25:01Z"
              }
            ]
          },
          "pagination": {
            "allOf": [
              {
                "$ref": "#/components/schemas/MerchantPaginationDto"
              }
            ],
            "description": "Paging information. `pageSize` is capped at 100.",
            "example": {
              "totalItems": 57,
              "page": 1,
              "pageSize": 20,
              "totalPages": 3
            }
          }
        },
        "additionalProperties": false,
        "description": "One page of an endpoint's delivery log."
      },
      "MerchantWebhookDeliveryPageDtoApiResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "`true` when the request succeeded, `false` on every error.",
            "example": true
          },
          "uuid": {
            "type": "string",
            "description": "Identifier of this response. Quote it when you contact support about a request.",
            "nullable": true,
            "example": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f"
          },
          "code": {
            "type": "string",
            "description": "Machine-readable outcome, for example `SUCCESS`, `SESSION_CREATED` or `VALIDATION_ERROR`. Branch on this, never on `message`.",
            "nullable": true,
            "example": "SUCCESS"
          },
          "message": {
            "type": "string",
            "description": "Human-readable text for the outcome. It may change and may be localised; do not parse it.",
            "nullable": true,
            "example": "Operation completed successfully"
          },
          "data": {
            "allOf": [
              {
                "$ref": "#/components/schemas/MerchantWebhookDeliveryPageDto"
              }
            ],
            "description": "The result. Its shape is documented for each operation; `null` on most errors.",
            "example": {
              "data": [
                {
                  "id": "f0e1d2c3-b4a5-4968-8776-655443322110",
                  "eventId": "9f8c1d2e-3a4b-5c6d-7e8f-90a1b2c3d4e5",
                  "eventType": "order.confirmed",
                  "orderId": "3f6c2a9e-8b41-4d7a-9c15-2e7b0d4f8a61",
                  "orderReferenceId": "MERCHANT-ORDER-12345",
                  "status": "Succeeded",
                  "attempts": 1,
                  "maxAttempts": 6,
                  "nextAttemptAt": null,
                  "lastAttemptAt": "2026-10-20T09:25:02Z",
                  "lastStatusCode": 200,
                  "lastError": null,
                  "responseSnippet": "{\"received\":true}",
                  "durationMs": 184,
                  "succeededAt": "2026-10-20T09:25:02Z",
                  "replayOfId": null,
                  "createdAt": "2026-10-20T09:25:01Z"
                }
              ],
              "pagination": {
                "totalItems": 57,
                "page": 1,
                "pageSize": 20,
                "totalPages": 3
              }
            }
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiError"
            },
            "description": "Field-level problems. `null` on success; on failure an array, often empty, of `{ field, message }`.",
            "nullable": true,
            "example": null
          }
        },
        "additionalProperties": false,
        "description": "The envelope every response uses. `success` and `code` tell you the outcome; `data` carries the result."
      },
      "MerchantWebhookEndpointDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Endpoint id.",
            "format": "uuid",
            "example": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"
          },
          "url": {
            "type": "string",
            "description": "Where events are posted.",
            "nullable": true,
            "example": "https://shop.example/webhooks/qasimha"
          },
          "description": {
            "type": "string",
            "description": "Label, or `null`.",
            "nullable": true,
            "example": "Production store"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Subscribed event types; empty means every event.",
            "nullable": true,
            "example": [
              "order.confirmed",
              "order.refunded"
            ]
          },
          "isActive": {
            "type": "boolean",
            "description": "`false` while the endpoint is paused.",
            "example": true
          },
          "secretHint": {
            "type": "string",
            "description": "Last four characters of the current signing secret, to tell which secret a server holds.",
            "nullable": true,
            "example": "9f3a"
          },
          "previousSecretExpiresAt": {
            "type": "string",
            "description": "After a rotation, until when the previous secret still signs deliveries (UTC); `null` otherwise.",
            "format": "date-time",
            "nullable": true,
            "example": null
          },
          "createdAt": {
            "type": "string",
            "description": "When the endpoint was registered (UTC).",
            "format": "date-time",
            "example": "2026-10-20T09:00:00Z"
          },
          "updatedAt": {
            "type": "string",
            "description": "When it last changed (UTC), or `null`.",
            "format": "date-time",
            "nullable": true,
            "example": null
          }
        },
        "additionalProperties": false,
        "description": "A registered webhook endpoint. The secret itself is never returned here."
      },
      "MerchantWebhookEndpointDtoApiResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "`true` when the request succeeded, `false` on every error.",
            "example": true
          },
          "uuid": {
            "type": "string",
            "description": "Identifier of this response. Quote it when you contact support about a request.",
            "nullable": true,
            "example": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f"
          },
          "code": {
            "type": "string",
            "description": "Machine-readable outcome, for example `SUCCESS`, `SESSION_CREATED` or `VALIDATION_ERROR`. Branch on this, never on `message`.",
            "nullable": true,
            "example": "SUCCESS"
          },
          "message": {
            "type": "string",
            "description": "Human-readable text for the outcome. It may change and may be localised; do not parse it.",
            "nullable": true,
            "example": "Operation completed successfully"
          },
          "data": {
            "allOf": [
              {
                "$ref": "#/components/schemas/MerchantWebhookEndpointDto"
              }
            ],
            "description": "The result. Its shape is documented for each operation; `null` on most errors.",
            "example": {
              "id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
              "url": "https://shop.example/webhooks/qasimha",
              "description": "Production store",
              "events": [
                "order.confirmed",
                "order.refunded"
              ],
              "isActive": true,
              "secretHint": "9f3a",
              "previousSecretExpiresAt": null,
              "createdAt": "2026-10-20T09:00:00Z",
              "updatedAt": null
            }
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiError"
            },
            "description": "Field-level problems. `null` on success; on failure an array, often empty, of `{ field, message }`.",
            "nullable": true,
            "example": null
          }
        },
        "additionalProperties": false,
        "description": "The envelope every response uses. `success` and `code` tell you the outcome; `data` carries the result."
      },
      "MerchantWebhookEndpointDtoIReadOnlyListApiResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "`true` when the request succeeded, `false` on every error.",
            "example": true
          },
          "uuid": {
            "type": "string",
            "description": "Identifier of this response. Quote it when you contact support about a request.",
            "nullable": true,
            "example": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f"
          },
          "code": {
            "type": "string",
            "description": "Machine-readable outcome, for example `SUCCESS`, `SESSION_CREATED` or `VALIDATION_ERROR`. Branch on this, never on `message`.",
            "nullable": true,
            "example": "SUCCESS"
          },
          "message": {
            "type": "string",
            "description": "Human-readable text for the outcome. It may change and may be localised; do not parse it.",
            "nullable": true,
            "example": "Operation completed successfully"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MerchantWebhookEndpointDto"
            },
            "description": "The result. Its shape is documented for each operation; `null` on most errors.",
            "nullable": true,
            "example": [
              {
                "id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
                "url": "https://shop.example/webhooks/qasimha",
                "description": "Production store",
                "events": [
                  "order.confirmed",
                  "order.refunded"
                ],
                "isActive": true,
                "secretHint": "9f3a",
                "previousSecretExpiresAt": null,
                "createdAt": "2026-10-20T09:00:00Z",
                "updatedAt": null
              }
            ]
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiError"
            },
            "description": "Field-level problems. `null` on success; on failure an array, often empty, of `{ field, message }`.",
            "nullable": true,
            "example": null
          }
        },
        "additionalProperties": false,
        "description": "The envelope every response uses. `success` and `code` tell you the outcome; `data` carries the result."
      },
      "MerchantWebhookPingResultDto": {
        "type": "object",
        "properties": {
          "eventId": {
            "type": "string",
            "description": "Id of the `ping` event, as it will appear in the `X-Qasimha-Event-Id` header.",
            "nullable": true,
            "example": "evt_9f8c1d2e3a4b5c6d7e8f90a1b2c3d4e5"
          },
          "endpointId": {
            "type": "string",
            "description": "The endpoint it is queued for.",
            "format": "uuid",
            "example": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"
          }
        },
        "additionalProperties": false,
        "description": "The queued test event."
      },
      "MerchantWebhookPingResultDtoApiResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "`true` when the request succeeded, `false` on every error.",
            "example": true
          },
          "uuid": {
            "type": "string",
            "description": "Identifier of this response. Quote it when you contact support about a request.",
            "nullable": true,
            "example": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f"
          },
          "code": {
            "type": "string",
            "description": "Machine-readable outcome, for example `SUCCESS`, `SESSION_CREATED` or `VALIDATION_ERROR`. Branch on this, never on `message`.",
            "nullable": true,
            "example": "SUCCESS"
          },
          "message": {
            "type": "string",
            "description": "Human-readable text for the outcome. It may change and may be localised; do not parse it.",
            "nullable": true,
            "example": "Operation completed successfully"
          },
          "data": {
            "allOf": [
              {
                "$ref": "#/components/schemas/MerchantWebhookPingResultDto"
              }
            ],
            "description": "The result. Its shape is documented for each operation; `null` on most errors.",
            "example": {
              "eventId": "evt_9f8c1d2e3a4b5c6d7e8f90a1b2c3d4e5",
              "endpointId": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"
            }
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiError"
            },
            "description": "Field-level problems. `null` on success; on failure an array, often empty, of `{ field, message }`.",
            "nullable": true,
            "example": null
          }
        },
        "additionalProperties": false,
        "description": "The envelope every response uses. `success` and `code` tell you the outcome; `data` carries the result."
      },
      "MerchantWebhookSecretDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Endpoint id.",
            "format": "uuid",
            "example": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"
          },
          "url": {
            "type": "string",
            "description": "Where events are posted.",
            "nullable": true,
            "example": "https://shop.example/webhooks/qasimha"
          },
          "description": {
            "type": "string",
            "description": "Label, or `null`.",
            "nullable": true,
            "example": "Production store"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Subscribed event types; empty means every event.",
            "nullable": true,
            "example": [
              "order.confirmed",
              "order.refunded"
            ]
          },
          "isActive": {
            "type": "boolean",
            "description": "`false` while the endpoint is paused.",
            "example": true
          },
          "secret": {
            "type": "string",
            "description": "The signing secret: `whsec_` followed by 48 hexadecimal characters. Shown only in this response; store it in a secrets manager now.",
            "nullable": true,
            "example": "whsec_4c1f0e2a9b7d6c5e3f1a0b9c8d7e6f5a4b3c2d1e0f9a3a9f"
          },
          "secretHint": {
            "type": "string",
            "description": "Last four characters of `secret`.",
            "nullable": true,
            "example": "3a9f"
          },
          "previousSecretExpiresAt": {
            "type": "string",
            "description": "After a rotation, until when the previous secret still signs deliveries (UTC), 24 hours from now; `null` on create.",
            "format": "date-time",
            "nullable": true,
            "example": "2026-10-21T09:00:00Z"
          },
          "createdAt": {
            "type": "string",
            "description": "When the endpoint was registered (UTC).",
            "format": "date-time",
            "example": "2026-10-20T09:00:00Z"
          }
        },
        "additionalProperties": false,
        "description": "A webhook endpoint with its signing secret. Returned only on create and on rotation."
      },
      "MerchantWebhookSecretDtoApiResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "`true` when the request succeeded, `false` on every error.",
            "example": true
          },
          "uuid": {
            "type": "string",
            "description": "Identifier of this response. Quote it when you contact support about a request.",
            "nullable": true,
            "example": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f"
          },
          "code": {
            "type": "string",
            "description": "Machine-readable outcome, for example `SUCCESS`, `SESSION_CREATED` or `VALIDATION_ERROR`. Branch on this, never on `message`.",
            "nullable": true,
            "example": "SUCCESS"
          },
          "message": {
            "type": "string",
            "description": "Human-readable text for the outcome. It may change and may be localised; do not parse it.",
            "nullable": true,
            "example": "Operation completed successfully"
          },
          "data": {
            "allOf": [
              {
                "$ref": "#/components/schemas/MerchantWebhookSecretDto"
              }
            ],
            "description": "The result. Its shape is documented for each operation; `null` on most errors.",
            "example": {
              "id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
              "url": "https://shop.example/webhooks/qasimha",
              "description": "Production store",
              "events": [
                "order.confirmed",
                "order.refunded"
              ],
              "isActive": true,
              "secret": "whsec_4c1f0e2a9b7d6c5e3f1a0b9c8d7e6f5a4b3c2d1e0f9a3a9f",
              "secretHint": "3a9f",
              "previousSecretExpiresAt": "2026-10-21T09:00:00Z",
              "createdAt": "2026-10-20T09:00:00Z"
            }
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiError"
            },
            "description": "Field-level problems. `null` on success; on failure an array, often empty, of `{ field, message }`.",
            "nullable": true,
            "example": null
          }
        },
        "additionalProperties": false,
        "description": "The envelope every response uses. `success` and `code` tell you the outcome; `data` carries the result."
      },
      "ObjectApiResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "`true` when the request succeeded, `false` on every error.",
            "example": true
          },
          "uuid": {
            "type": "string",
            "description": "Identifier of this response. Quote it when you contact support about a request.",
            "nullable": true,
            "example": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f"
          },
          "code": {
            "type": "string",
            "description": "Machine-readable outcome, for example `SUCCESS`, `SESSION_CREATED` or `VALIDATION_ERROR`. Branch on this, never on `message`.",
            "nullable": true,
            "example": "SUCCESS"
          },
          "message": {
            "type": "string",
            "description": "Human-readable text for the outcome. It may change and may be localised; do not parse it.",
            "nullable": true,
            "example": "Operation completed successfully"
          },
          "data": {
            "description": "The result. Its shape is documented for each operation; `null` on most errors.",
            "nullable": true,
            "example": null
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiError"
            },
            "description": "Field-level problems. `null` on success; on failure an array, often empty, of `{ field, message }`.",
            "nullable": true,
            "example": null
          }
        },
        "additionalProperties": false,
        "description": "The envelope every response uses. `success` and `code` tell you the outcome; `data` carries the result."
      },
      "UpdateMerchantWebhookRequest": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "description": "New URL, same rules as on create. Leave it out to keep the current one.",
            "nullable": true,
            "example": "https://shop.example/webhooks/qasimha-v2"
          },
          "description": {
            "type": "string",
            "description": "New label, at most 200 characters. Leave it out to keep it; send `\"\"` to clear it.",
            "nullable": true,
            "example": "Production store"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "New subscription. Leave it out to keep it; `[]` means every event. Same names as on create.",
            "nullable": true,
            "example": [
              "order.confirmed",
              "order.declined",
              "order.refunded",
              "order.partially_refunded"
            ]
          },
          "isActive": {
            "type": "boolean",
            "description": "`false` pauses the endpoint: events raised while it is paused are not queued for it. `true` resumes it. Leave it out to keep it.",
            "nullable": true,
            "example": true
          }
        },
        "additionalProperties": false,
        "description": "Request body of `PUT /api/v1/merchant/webhooks/{id}`. Every field is optional; a field left out keeps its value."
      },
      "WidgetPlanDto": {
        "type": "object",
        "properties": {
          "instalments": {
            "type": "integer",
            "description": "Number of monthly instalments.",
            "format": "int32",
            "example": 4
          },
          "frequency": {
            "type": "string",
            "description": "Instalment frequency. Always `monthly`.",
            "nullable": true,
            "example": "monthly"
          },
          "instalmentAmount": {
            "type": "number",
            "description": "The regular instalment.",
            "format": "double",
            "example": 307.19
          },
          "firstInstalmentAmount": {
            "type": "number",
            "description": "The first instalment (it carries any upfront fees).",
            "format": "double",
            "example": 307.19
          },
          "lastInstalmentAmount": {
            "type": "number",
            "description": "The last instalment (it carries the rounding remainder).",
            "format": "double",
            "example": 307.18
          },
          "feesIncludingVat": {
            "type": "number",
            "description": "`totalPayable − amount`: everything the customer pays on top of the amount, VAT included.",
            "format": "double",
            "example": 28.75
          },
          "totalPayable": {
            "type": "number",
            "description": "Total the customer repays.",
            "format": "double",
            "example": 1228.75
          },
          "shariaCompliant": {
            "type": "boolean",
            "description": "`true` when the plan carries no finance charge (no profit and no term cost). The widget shows its interest-free line only then.",
            "example": true
          },
          "apr": {
            "type": "number",
            "description": "Annual percentage rate, in percent, two decimals.",
            "format": "double",
            "example": 0
          },
          "processingFeeIncludingVat": {
            "type": "number",
            "description": "One-off processing fee, VAT included.",
            "format": "double",
            "example": 28.75
          },
          "profitIncludingVat": {
            "type": "number",
            "description": "Profit earned over the term, VAT included.",
            "format": "double",
            "example": 0
          }
        },
        "additionalProperties": false,
        "description": "One plan quoted for the amount, from your price list. No customer data."
      },
      "WidgetPlansDto": {
        "type": "object",
        "properties": {
          "merchantName": {
            "type": "string",
            "description": "Your store name; `null` when unknown.",
            "nullable": true,
            "example": "Example Store"
          },
          "currency": {
            "type": "string",
            "description": "Currency of the quote.",
            "nullable": true,
            "example": "SAR"
          },
          "minAmount": {
            "type": "number",
            "description": "Smallest amount any of your active policies finances; 0 when none.",
            "format": "double",
            "example": 100
          },
          "maxAmount": {
            "type": "number",
            "description": "Largest amount any of your active policies finances; 0 when none.",
            "format": "double",
            "example": 4000
          },
          "plans": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WidgetPlanDto"
            },
            "description": "The cheapest plan for each instalment count across all products, shortest first. Empty when the amount is outside `[minAmount, maxAmount]` or no policy is active.",
            "nullable": true,
            "example": [
              {
                "instalments": 4,
                "frequency": "monthly",
                "instalmentAmount": 307.19,
                "firstInstalmentAmount": 307.19,
                "lastInstalmentAmount": 307.18,
                "feesIncludingVat": 28.75,
                "totalPayable": 1228.75,
                "shariaCompliant": true,
                "apr": 0,
                "processingFeeIncludingVat": 28.75,
                "profitIncludingVat": 0
              }
            ]
          },
          "products": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WidgetProductDto"
            },
            "description": "The same quotes grouped by financing product, each with its own range. Products without a plan for this amount are left out.",
            "nullable": true,
            "example": [
              {
                "code": "BNPL",
                "minAmount": 100,
                "maxAmount": 4000,
                "plans": [
                  {
                    "instalments": 4,
                    "frequency": "monthly",
                    "instalmentAmount": 307.19,
                    "firstInstalmentAmount": 307.19,
                    "lastInstalmentAmount": 307.18,
                    "feesIncludingVat": 28.75,
                    "totalPayable": 1228.75,
                    "shariaCompliant": true,
                    "apr": 0,
                    "processingFeeIncludingVat": 28.75,
                    "profitIncludingVat": 0
                  }
                ]
              }
            ]
          }
        },
        "additionalProperties": false,
        "description": "Plans your policies offer for the amount."
      },
      "WidgetPlansDtoApiResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "`true` when the request succeeded, `false` on every error.",
            "example": true
          },
          "uuid": {
            "type": "string",
            "description": "Identifier of this response. Quote it when you contact support about a request.",
            "nullable": true,
            "example": "6f1c2d3e-4b5a-4c7d-9e8f-0a1b2c3d4e5f"
          },
          "code": {
            "type": "string",
            "description": "Machine-readable outcome, for example `SUCCESS`, `SESSION_CREATED` or `VALIDATION_ERROR`. Branch on this, never on `message`.",
            "nullable": true,
            "example": "SUCCESS"
          },
          "message": {
            "type": "string",
            "description": "Human-readable text for the outcome. It may change and may be localised; do not parse it.",
            "nullable": true,
            "example": "Operation completed successfully"
          },
          "data": {
            "allOf": [
              {
                "$ref": "#/components/schemas/WidgetPlansDto"
              }
            ],
            "description": "The result. Its shape is documented for each operation; `null` on most errors.",
            "example": {
              "merchantName": "Example Store",
              "currency": "SAR",
              "minAmount": 100,
              "maxAmount": 4000,
              "plans": [
                {
                  "instalments": 4,
                  "frequency": "monthly",
                  "instalmentAmount": 307.19,
                  "firstInstalmentAmount": 307.19,
                  "lastInstalmentAmount": 307.18,
                  "feesIncludingVat": 28.75,
                  "totalPayable": 1228.75,
                  "shariaCompliant": true,
                  "apr": 0,
                  "processingFeeIncludingVat": 28.75,
                  "profitIncludingVat": 0
                }
              ],
              "products": [
                {
                  "code": "BNPL",
                  "minAmount": 100,
                  "maxAmount": 4000,
                  "plans": [
                    {
                      "instalments": 4,
                      "frequency": "monthly",
                      "instalmentAmount": 307.19,
                      "firstInstalmentAmount": 307.19,
                      "lastInstalmentAmount": 307.18,
                      "feesIncludingVat": 28.75,
                      "totalPayable": 1228.75,
                      "shariaCompliant": true,
                      "apr": 0,
                      "processingFeeIncludingVat": 28.75,
                      "profitIncludingVat": 0
                    }
                  ]
                }
              ]
            }
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApiError"
            },
            "description": "Field-level problems. `null` on success; on failure an array, often empty, of `{ field, message }`.",
            "nullable": true,
            "example": null
          }
        },
        "additionalProperties": false,
        "description": "The envelope every response uses. `success` and `code` tell you the outcome; `data` carries the result."
      },
      "WidgetProductDto": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string",
            "description": "Product code in upper case, for example `BNPL` or `MF` (micro-finance).",
            "nullable": true,
            "example": "BNPL"
          },
          "minAmount": {
            "type": "number",
            "description": "Smallest amount this product finances.",
            "format": "double",
            "example": 100
          },
          "maxAmount": {
            "type": "number",
            "description": "Largest amount this product finances.",
            "format": "double",
            "example": 4000
          },
          "plans": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WidgetPlanDto"
            },
            "description": "Cheapest plan per instalment count for this product, shortest first.",
            "nullable": true,
            "example": [
              {
                "instalments": 4,
                "frequency": "monthly",
                "instalmentAmount": 307.19,
                "firstInstalmentAmount": 307.19,
                "lastInstalmentAmount": 307.18,
                "feesIncludingVat": 28.75,
                "totalPayable": 1228.75,
                "shariaCompliant": true,
                "apr": 0,
                "processingFeeIncludingVat": 28.75,
                "profitIncludingVat": 0
              }
            ]
          }
        },
        "additionalProperties": false,
        "description": "The plans of one financing product."
      }
    },
    "securitySchemes": {
      "Bearer": {
        "type": "http",
        "description": "JWT Authorization header using the Bearer scheme.",
        "scheme": "Bearer",
        "bearerFormat": "JWT"
      },
      "ApiKey": {
        "type": "apiKey",
        "description": "Merchant API key for server-to-server integration. Generate one in the Merchant Portal (Settings → API Keys) and send it as the X-Api-Key header. Use an sk_test_… key in sandbox and sk_live_… in production.",
        "name": "X-Api-Key",
        "in": "header"
      },
      "PublicKey": {
        "type": "apiKey",
        "description": "Publishable key (`pk_test_…` / `pk_live_…`) for `GET /api/v1/widget/plans` only. Safe to embed in a web page or an app. Create one in the Merchant Portal (API Keys, type publishable).",
        "name": "X-Public-Key",
        "in": "header"
      }
    }
  },
  "security": [
    {
      "Bearer": [ ]
    },
    {
      "ApiKey": [ ]
    }
  ],
  "tags": [
    {
      "name": "Payment Sessions",
      "description": "Create a checkout. A session is the start of every order: it returns a hosted `paymentUrl` to redirect the customer to (online), or a POS / QR session for in-store. Requires scope `orders:write`."
    },
    {
      "name": "Merchant Orders",
      "description": "Track and manage the order lifecycle after checkout: list and read orders (`orders:read`), then capture, fulfil, refund, or cancel (`orders:write`)."
    },
    {
      "name": "Merchant Settlements",
      "description": "Settlement batches due to you, net of fees. Requires scope `settlements:read`."
    },
    {
      "name": "Merchant Webhooks",
      "description": "Endpoints that receive signed order events, their secrets and the delivery log. Requires scope `webhooks:manage` (or an owner/admin portal session)."
    },
    {
      "name": "Widget",
      "description": "Plan quotes for an amount, read with a publishable key (`X-Public-Key`) by the on-site widget and the mobile promo views. No customer data, any origin."
    },
    {
      "name": "Merchant Checkout Links",
      "description": "Create shareable payment links or scan-to-pay QR codes without a server-side checkout integration. The order is created up-front and the link is delivered to the customer by SMS. Authenticate with a merchant API key (`X-Api-Key`, scope `orders:write`) or a Merchant Portal session (JWT)."
    }
  ]
}