{
  "openapi": "3.1.0",
  "info": {
    "title": "Hensa API",
    "version": "2026-11-01",
    "description": "The Hensa Connect API: create dynamic VietQR codes, receive money in real time via webhooks and reconcile automatically. All amounts are integer VND, timestamps are ISO 8601."
  },
  "servers": [
    {
      "url": "https://api.hensa.example/v1"
    }
  ],
  "security": [
    {
      "apiKey": []
    }
  ],
  "tags": [
    {
      "name": "payment_requests",
      "x-displayName": "Payment requests",
      "description": "Create a dynamic VietQR per order. When the customer pays, Hensa matches it automatically and sends `payment_request.paid`."
    },
    {
      "name": "bank_transactions",
      "x-displayName": "Bank transactions",
      "description": "Every incoming transfer to linked accounts, with its reconciliation status."
    },
    {
      "name": "webhook_endpoints",
      "x-displayName": "Webhook endpoints",
      "description": "Register event URLs, rotate secrets, inspect deliveries and redeliver."
    },
    {
      "name": "test",
      "x-displayName": "Test mode",
      "description": "Only with `hk_test_` keys. Simulate incoming money to exercise the whole flow without real transfers."
    }
  ],
  "paths": {
    "/payment_requests": {
      "post": {
        "operationId": "createPaymentRequest",
        "tags": [
          "payment_requests"
        ],
        "summary": "Create a payment request",
        "description": "Creates a dynamic VietQR with its own amount and `referenceCode`. Render `qrPayload` as a QR code for the customer – or redirect them to the hosted Hensa Pay page at `payUrl`. Expires after 15 minutes.\n\n**Possible errors:** `validation_error` (400) · `idempotency_conflict` (409) · `no_active_bank_account` (422) · `merchant_name_required` (422) · `plan_limit` (422)",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "description": "Idempotency key (UUID). Repeating the same key with the same body within 24 hours returns the original result instead of creating a new record.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Hensa-Version",
            "in": "header",
            "required": false,
            "description": "API version (date). Omit to use the version pinned to your key.",
            "schema": {
              "type": "string",
              "example": "2026-11-01"
            }
          }
        ],
        "responses": {
          "201": {
            "description": "The new payment request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentRequest"
                }
              }
            }
          },
          "400": {
            "description": "`validation_error` – Invalid request data – see `message`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`idempotency_conflict` – Same `Idempotency-Key` with a different body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "`no_active_bank_account` – The store has no active bank account.\n`merchant_name_required` – The store has no name yet – set it in the Dashboard before creating QR codes (live mode only).\n`plan_limit` – The current plan's limit is reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "amount": {
                    "type": "integer",
                    "minimum": 1000,
                    "maximum": 9007199254740991,
                    "description": "Amount to collect, integer VND, minimum 1,000.",
                    "examples": [
                      150000
                    ]
                  },
                  "externalOrderRef": {
                    "type": "string",
                    "maxLength": 64,
                    "description": "Your own order id (max 64 chars). Returned as-is in webhooks.",
                    "examples": [
                      "DH-1024"
                    ]
                  }
                },
                "required": [
                  "amount"
                ]
              },
              "example": {
                "amount": 150000,
                "externalOrderRef": "DH-1024"
              }
            }
          }
        }
      },
      "get": {
        "operationId": "listPaymentRequests",
        "tags": [
          "payment_requests"
        ],
        "summary": "List payment requests",
        "description": "Newest first. Cursor-paginated.\n\n**Possible errors:** `validation_error` (400)",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Filter by status.",
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "paid",
                "partially_paid",
                "expired",
                "cancelled"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Items per page (1–100, default 25).",
            "schema": {
              "default": 25,
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "The `nextCursor` value from the previous page.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Hensa-Version",
            "in": "header",
            "required": false,
            "description": "API version (date). Omit to use the version pinned to your key.",
            "schema": {
              "type": "string",
              "example": "2026-11-01"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of results.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PaymentRequest"
                      }
                    },
                    "nextCursor": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  },
                  "required": [
                    "data",
                    "nextCursor"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "`validation_error` – Invalid request data – see `message`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/payment_requests/{id}": {
      "get": {
        "operationId": "getPaymentRequest",
        "tags": [
          "payment_requests"
        ],
        "summary": "Retrieve a payment request",
        "description": "Use it to re-check the status if a webhook has not arrived.\n\n**Possible errors:** `not_found` (404)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Payment request ID.",
            "schema": {
              "type": "string",
              "format": "starts_with",
              "pattern": "^prq_.*"
            }
          },
          {
            "name": "Hensa-Version",
            "in": "header",
            "required": false,
            "description": "API version (date). Omit to use the version pinned to your key.",
            "schema": {
              "type": "string",
              "example": "2026-11-01"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The payment request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentRequest"
                }
              }
            }
          },
          "404": {
            "description": "`not_found` – Not found (or not owned by this key's store).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/bank_transactions": {
      "get": {
        "operationId": "listBankTransactions",
        "tags": [
          "bank_transactions"
        ],
        "summary": "List bank transactions",
        "description": "Newest first. Poll periodically (e.g. every 15 minutes) as a safety net next to webhooks.\n\n**Possible errors:** `validation_error` (400)",
        "parameters": [
          {
            "name": "reconciliation",
            "in": "query",
            "required": false,
            "description": "Filter by reconciliation status.",
            "schema": {
              "type": "string",
              "enum": [
                "matched",
                "pending",
                "unmatched"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Items per page (1–100, default 25).",
            "schema": {
              "default": 25,
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "The `nextCursor` value from the previous page.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Hensa-Version",
            "in": "header",
            "required": false,
            "description": "API version (date). Omit to use the version pinned to your key.",
            "schema": {
              "type": "string",
              "example": "2026-11-01"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of results.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/BankTransaction"
                      }
                    },
                    "nextCursor": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  },
                  "required": [
                    "data",
                    "nextCursor"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "`validation_error` – Invalid request data – see `message`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/bank_transactions/{id}": {
      "get": {
        "operationId": "getBankTransaction",
        "tags": [
          "bank_transactions"
        ],
        "summary": "Retrieve a bank transaction",
        "description": "Details of one incoming transfer.\n\n**Possible errors:** `not_found` (404)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Bank transaction ID.",
            "schema": {
              "type": "string",
              "format": "starts_with",
              "pattern": "^btx_.*"
            }
          },
          {
            "name": "Hensa-Version",
            "in": "header",
            "required": false,
            "description": "API version (date). Omit to use the version pinned to your key.",
            "schema": {
              "type": "string",
              "example": "2026-11-01"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The bank transaction.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BankTransaction"
                }
              }
            }
          },
          "404": {
            "description": "`not_found` – Not found (or not owned by this key's store).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/bank_transactions/{id}/reconcile": {
      "post": {
        "operationId": "reconcileBankTransaction",
        "tags": [
          "bank_transactions"
        ],
        "summary": "Reconcile a transaction manually",
        "description": "When the customer typed a wrong description: link the transaction to a payment request, one of your order refs, or mark it \"not an order\".\n\n**Possible errors:** `not_found` (404) · `already_reconciled` (409) · `validation_error` (400)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Bank transaction ID.",
            "schema": {
              "type": "string",
              "format": "starts_with",
              "pattern": "^btx_.*"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "description": "Idempotency key (UUID). Repeating the same key with the same body within 24 hours returns the original result instead of creating a new record.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Hensa-Version",
            "in": "header",
            "required": false,
            "description": "API version (date). Omit to use the version pinned to your key.",
            "schema": {
              "type": "string",
              "example": "2026-11-01"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The reconciled transaction.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BankTransaction"
                }
              }
            }
          },
          "400": {
            "description": "`validation_error` – Invalid request data – see `message`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "`not_found` – Not found (or not owned by this key's store).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "`already_reconciled` – The transaction is already reconciled.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "anyOf": [
                  {
                    "type": "object",
                    "properties": {
                      "paymentRequestId": {
                        "type": "string",
                        "format": "starts_with",
                        "pattern": "^prq_.*"
                      }
                    },
                    "required": [
                      "paymentRequestId"
                    ]
                  },
                  {
                    "type": "object",
                    "properties": {
                      "orderRef": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 64
                      }
                    },
                    "required": [
                      "orderRef"
                    ]
                  },
                  {
                    "type": "object",
                    "properties": {
                      "notAnOrder": {
                        "type": "boolean",
                        "const": true
                      }
                    },
                    "required": [
                      "notAnOrder"
                    ]
                  }
                ]
              },
              "example": {
                "paymentRequestId": "prq_01JD7Q8M4K2X9V3T6B5N0R1W8Y"
              }
            }
          }
        }
      }
    },
    "/webhook_endpoints": {
      "get": {
        "operationId": "listWebhookEndpoints",
        "tags": [
          "webhook_endpoints"
        ],
        "summary": "List webhook endpoints",
        "description": "All URLs receiving events.",
        "parameters": [
          {
            "name": "Hensa-Version",
            "in": "header",
            "required": false,
            "description": "API version (date). Omit to use the version pinned to your key.",
            "schema": {
              "type": "string",
              "example": "2026-11-01"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Endpoints.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WebhookEndpoint"
                      }
                    }
                  },
                  "required": [
                    "data"
                  ],
                  "additionalProperties": false
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "createWebhookEndpoint",
        "tags": [
          "webhook_endpoints"
        ],
        "summary": "Create a webhook endpoint",
        "description": "The URL must be public HTTPS (private addresses are rejected). The signing secret (`whsec_…`) is **returned once** – store it now.\n\n**Possible errors:** `validation_error` (400) · `url_not_https` (422) · `url_private_address` (422)",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "description": "Idempotency key (UUID). Repeating the same key with the same body within 24 hours returns the original result instead of creating a new record.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Hensa-Version",
            "in": "header",
            "required": false,
            "description": "API version (date). Omit to use the version pinned to your key.",
            "schema": {
              "type": "string",
              "example": "2026-11-01"
            }
          }
        ],
        "responses": {
          "201": {
            "description": "The endpoint and its signing secret.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "endpoint": {
                      "$ref": "#/components/schemas/WebhookEndpoint"
                    },
                    "secret": {
                      "type": "string",
                      "description": "whsec_… – shown once"
                    }
                  },
                  "required": [
                    "endpoint",
                    "secret"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "`validation_error` – Invalid request data – see `message`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "`url_not_https` – Webhook URLs must use HTTPS.\n`url_private_address` – The URL resolves to a private address – not allowed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "starts_with",
                    "pattern": "^https:\\/\\/.*",
                    "description": "Public HTTPS URL that will receive events.",
                    "examples": [
                      "https://cuahang.vn/webhooks/hensa"
                    ]
                  },
                  "events": {
                    "minItems": 1,
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "bank_transaction.created",
                        "payment_request.paid",
                        "payment_request.partially_paid",
                        "payment_request.expired",
                        "bank_connection.status_changed"
                      ]
                    },
                    "description": "Events to receive (at least one).",
                    "examples": [
                      [
                        "payment_request.paid"
                      ]
                    ]
                  }
                },
                "required": [
                  "url",
                  "events"
                ]
              },
              "example": {
                "url": "https://cuahang.vn/webhooks/hensa",
                "events": [
                  "payment_request.paid",
                  "bank_transaction.created"
                ]
              }
            }
          }
        }
      }
    },
    "/webhook_endpoints/{id}": {
      "delete": {
        "operationId": "deleteWebhookEndpoint",
        "tags": [
          "webhook_endpoints"
        ],
        "summary": "Delete a webhook endpoint",
        "description": "Stops sending events to this URL immediately.\n\n**Possible errors:** `not_found` (404)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Endpoint ID.",
            "schema": {
              "type": "string",
              "format": "starts_with",
              "pattern": "^whe_.*"
            }
          },
          {
            "name": "Hensa-Version",
            "in": "header",
            "required": false,
            "description": "API version (date). Omit to use the version pinned to your key.",
            "schema": {
              "type": "string",
              "example": "2026-11-01"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted."
          },
          "404": {
            "description": "`not_found` – Not found (or not owned by this key's store).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/webhook_endpoints/{id}/rotate_secret": {
      "post": {
        "operationId": "rotateWebhookSecret",
        "tags": [
          "webhook_endpoints"
        ],
        "summary": "Rotate the signing secret",
        "description": "Creates a new secret. The old one stays valid for 24 hours – meanwhile each event carries two `v1` signatures so you can switch without downtime.\n\n**Possible errors:** `not_found` (404)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Endpoint ID.",
            "schema": {
              "type": "string",
              "format": "starts_with",
              "pattern": "^whe_.*"
            }
          },
          {
            "name": "Hensa-Version",
            "in": "header",
            "required": false,
            "description": "API version (date). Omit to use the version pinned to your key.",
            "schema": {
              "type": "string",
              "example": "2026-11-01"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The endpoint and the new secret.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "endpoint": {
                      "$ref": "#/components/schemas/WebhookEndpoint"
                    },
                    "secret": {
                      "type": "string",
                      "description": "whsec_… – shown once"
                    }
                  },
                  "required": [
                    "endpoint",
                    "secret"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "404": {
            "description": "`not_found` – Not found (or not owned by this key's store).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/webhook_endpoints/{id}/test": {
      "post": {
        "operationId": "sendTestWebhook",
        "tags": [
          "webhook_endpoints"
        ],
        "summary": "Send a test event",
        "description": "Sends a signed sample event to the endpoint right away and returns the result.\n\n**Possible errors:** `not_found` (404)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Endpoint ID.",
            "schema": {
              "type": "string",
              "format": "starts_with",
              "pattern": "^whe_.*"
            }
          },
          {
            "name": "Hensa-Version",
            "in": "header",
            "required": false,
            "description": "API version (date). Omit to use the version pinned to your key.",
            "schema": {
              "type": "string",
              "example": "2026-11-01"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The delivery result.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookDelivery"
                }
              }
            }
          },
          "404": {
            "description": "`not_found` – Not found (or not owned by this key's store).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/webhook_endpoints/{id}/deliveries": {
      "get": {
        "operationId": "listWebhookDeliveries",
        "tags": [
          "webhook_endpoints"
        ],
        "summary": "List deliveries",
        "description": "Every delivery attempt in the last 30 days, including failures.\n\n**Possible errors:** `not_found` (404)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Endpoint ID.",
            "schema": {
              "type": "string",
              "format": "starts_with",
              "pattern": "^whe_.*"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Items per page (1–100, default 25).",
            "schema": {
              "default": 25,
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "The `nextCursor` value from the previous page.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Hensa-Version",
            "in": "header",
            "required": false,
            "description": "API version (date). Omit to use the version pinned to your key.",
            "schema": {
              "type": "string",
              "example": "2026-11-01"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of results.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WebhookDelivery"
                      }
                    },
                    "nextCursor": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  },
                  "required": [
                    "data",
                    "nextCursor"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "404": {
            "description": "`not_found` – Not found (or not owned by this key's store).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/webhook_deliveries/{id}/redeliver": {
      "post": {
        "operationId": "redeliverWebhook",
        "tags": [
          "webhook_endpoints"
        ],
        "summary": "Redeliver",
        "description": "Resends an event (within 30 days). Same event `id` – your system must be idempotent.\n\n**Possible errors:** `not_found` (404) · `delivery_too_old` (422)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Delivery ID.",
            "schema": {
              "type": "string",
              "format": "starts_with",
              "pattern": "^whd_.*"
            }
          },
          {
            "name": "Hensa-Version",
            "in": "header",
            "required": false,
            "description": "API version (date). Omit to use the version pinned to your key.",
            "schema": {
              "type": "string",
              "example": "2026-11-01"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The redelivery result.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookDelivery"
                }
              }
            }
          },
          "404": {
            "description": "`not_found` – Not found (or not owned by this key's store).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "`delivery_too_old` – Only events from the last 30 days can be redelivered.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/test/bank_transfers": {
      "post": {
        "operationId": "simulateBankTransfer",
        "tags": [
          "test"
        ],
        "summary": "Simulate an incoming transfer",
        "description": "**Only with `hk_test_` keys.** Creates a fake incoming transfer and emits real events exactly like production. Put a payment request's `referenceCode` in `description` to test auto-matching.\n\n**Possible errors:** `test_mode_only` (403) · `validation_error` (400)",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "description": "Idempotency key (UUID). Repeating the same key with the same body within 24 hours returns the original result instead of creating a new record.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Hensa-Version",
            "in": "header",
            "required": false,
            "description": "API version (date). Omit to use the version pinned to your key.",
            "schema": {
              "type": "string",
              "example": "2026-11-01"
            }
          }
        ],
        "responses": {
          "201": {
            "description": "The simulated transaction.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BankTransaction"
                }
              }
            }
          },
          "400": {
            "description": "`validation_error` – Invalid request data – see `message`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "`test_mode_only` – Only available with `hk_test_` keys.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "amount": {
                    "type": "integer",
                    "minimum": 1000,
                    "maximum": 9007199254740991,
                    "description": "Simulated amount, integer VND, minimum 1,000.",
                    "examples": [
                      150000
                    ]
                  },
                  "description": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 140,
                    "description": "Transfer description as typed by the customer. Include a `referenceCode` to test auto-matching.",
                    "examples": [
                      "LK7F3K9 thanh toan don hang"
                    ]
                  },
                  "counterpartyName": {
                    "type": "string",
                    "maxLength": 70,
                    "description": "Sender name (optional).",
                    "examples": [
                      "NGUYEN VAN AN"
                    ]
                  }
                },
                "required": [
                  "amount",
                  "description"
                ]
              },
              "example": {
                "amount": 150000,
                "description": "LK7F3K9 thanh toan don hang",
                "counterpartyName": "NGUYEN VAN AN"
              }
            }
          }
        }
      }
    }
  },
  "webhooks": {
    "bank_transaction.created": {
      "post": {
        "summary": "bank_transaction.created",
        "description": "Money arrived in a linked account. Signed with the `Hensa-Signature` header. Respond 2xx within 10 seconds.",
        "parameters": [
          {
            "name": "Hensa-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "example": "t=1762135200,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string",
                    "format": "starts_with",
                    "pattern": "^evt_.*"
                  },
                  "type": {
                    "type": "string",
                    "const": "bank_transaction.created"
                  },
                  "createdAt": {
                    "type": "string",
                    "format": "date-time",
                    "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
                  },
                  "data": {
                    "$ref": "#/components/schemas/BankTransaction"
                  }
                },
                "required": [
                  "id",
                  "type",
                  "createdAt",
                  "data"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "2xx"
          }
        }
      }
    },
    "payment_request.paid": {
      "post": {
        "summary": "payment_request.paid",
        "description": "A payment request was paid in full. Signed with the `Hensa-Signature` header. Respond 2xx within 10 seconds.",
        "parameters": [
          {
            "name": "Hensa-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "example": "t=1762135200,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string",
                    "format": "starts_with",
                    "pattern": "^evt_.*"
                  },
                  "type": {
                    "type": "string",
                    "const": "payment_request.paid"
                  },
                  "createdAt": {
                    "type": "string",
                    "format": "date-time",
                    "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
                  },
                  "data": {
                    "$ref": "#/components/schemas/PaymentRequest"
                  }
                },
                "required": [
                  "id",
                  "type",
                  "createdAt",
                  "data"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "2xx"
          }
        }
      }
    },
    "payment_request.partially_paid": {
      "post": {
        "summary": "payment_request.partially_paid",
        "description": "undefined Signed with the `Hensa-Signature` header. Respond 2xx within 10 seconds.",
        "parameters": [
          {
            "name": "Hensa-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "example": "t=1762135200,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string",
                    "format": "starts_with",
                    "pattern": "^evt_.*"
                  },
                  "type": {
                    "type": "string",
                    "const": "payment_request.partially_paid"
                  },
                  "createdAt": {
                    "type": "string",
                    "format": "date-time",
                    "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
                  },
                  "data": {
                    "$ref": "#/components/schemas/PaymentRequest"
                  }
                },
                "required": [
                  "id",
                  "type",
                  "createdAt",
                  "data"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "2xx"
          }
        }
      }
    },
    "payment_request.expired": {
      "post": {
        "summary": "payment_request.expired",
        "description": "A payment request expired unpaid. Signed with the `Hensa-Signature` header. Respond 2xx within 10 seconds.",
        "parameters": [
          {
            "name": "Hensa-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "example": "t=1762135200,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string",
                    "format": "starts_with",
                    "pattern": "^evt_.*"
                  },
                  "type": {
                    "type": "string",
                    "const": "payment_request.expired"
                  },
                  "createdAt": {
                    "type": "string",
                    "format": "date-time",
                    "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
                  },
                  "data": {
                    "$ref": "#/components/schemas/PaymentRequest"
                  }
                },
                "required": [
                  "id",
                  "type",
                  "createdAt",
                  "data"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "2xx"
          }
        }
      }
    },
    "bank_connection.status_changed": {
      "post": {
        "summary": "bank_connection.status_changed",
        "description": "A bank connection changed status (error, restored…). Signed with the `Hensa-Signature` header. Respond 2xx within 10 seconds.",
        "parameters": [
          {
            "name": "Hensa-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "example": "t=1762135200,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string",
                    "format": "starts_with",
                    "pattern": "^evt_.*"
                  },
                  "type": {
                    "type": "string",
                    "const": "bank_connection.status_changed"
                  },
                  "createdAt": {
                    "type": "string",
                    "format": "date-time",
                    "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "bankAccountId": {
                        "type": "string",
                        "format": "starts_with",
                        "pattern": "^bac_.*"
                      },
                      "status": {
                        "type": "string",
                        "enum": [
                          "active",
                          "error",
                          "disabled"
                        ]
                      },
                      "reason": {
                        "type": [
                          "string",
                          "null"
                        ]
                      }
                    },
                    "required": [
                      "bankAccountId",
                      "status",
                      "reason"
                    ],
                    "additionalProperties": false
                  }
                },
                "required": [
                  "id",
                  "type",
                  "createdAt",
                  "data"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "2xx"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "API key `hk_test_…` (test) or `hk_live_…` (live), sent as `Authorization: Bearer <key>`."
      }
    },
    "schemas": {
      "PaymentRequest": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "starts_with",
            "pattern": "^prq_.*",
            "description": "Payment request ID.",
            "examples": [
              "prq_01JD7Q8M4K2X9V3T6B5N0R1W8Y"
            ]
          },
          "amount": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Amount to collect, integer VND.",
            "examples": [
              150000
            ]
          },
          "amountReceived": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Total of the transfers matched to this request. Less than `amount` while `partially_paid`; can exceed it on overpayment.",
            "examples": [
              0
            ]
          },
          "currency": {
            "type": "string",
            "const": "VND",
            "description": "Always `VND`.",
            "examples": [
              "VND"
            ]
          },
          "referenceCode": {
            "type": "string",
            "description": "Reference the customer puts in the transfer description – used for auto-matching.",
            "examples": [
              "LK7F3K9"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "paid",
              "partially_paid",
              "expired",
              "cancelled"
            ],
            "description": "`pending` · `paid` in full · `partially_paid` underpaid · `expired` · `cancelled`.",
            "examples": [
              "pending"
            ]
          },
          "qrPayload": {
            "type": "string",
            "description": "VietQR (EMVCo) string – render it as a QR code.",
            "examples": [
              "00020101021238…"
            ]
          },
          "receiver": {
            "type": "object",
            "properties": {
              "name": {
                "type": "string"
              },
              "bankName": {
                "type": "string"
              },
              "accountMasked": {
                "type": "string"
              }
            },
            "required": [
              "name",
              "bankName",
              "accountMasked"
            ],
            "additionalProperties": false,
            "description": "Receiver shown to the customer before paying.",
            "examples": [
              {
                "name": "CA PHE MOC",
                "bankName": "MB Bank",
                "accountMasked": "•••• 6789"
              }
            ]
          },
          "externalOrderRef": {
            "type": [
              "string",
              "null"
            ],
            "description": "Your own order id (if sent at creation).",
            "examples": [
              "DH-1024"
            ]
          },
          "payUrl": {
            "anyOf": [
              {
                "type": "string",
                "format": "uri"
              },
              {
                "type": "null"
              }
            ],
            "description": "The hosted Hensa Pay page for this request (QR, open-banking-app buttons, receipt). Send it to the payer or redirect them here instead of rendering the QR yourself. The URL carries a secret – share it only with the payer. `null` once the page is revoked.",
            "examples": [
              "https://pay.hensa.vn/r/pt_7Hq2mX9vKd4RtY1nB8cZ0w"
            ]
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time",
            "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$",
            "description": "Expires 15 minutes after creation.",
            "examples": [
              "2026-11-03T08:15:00Z"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$",
            "description": "Creation time.",
            "examples": [
              "2026-11-03T08:00:00Z"
            ]
          }
        },
        "required": [
          "id",
          "amount",
          "amountReceived",
          "currency",
          "referenceCode",
          "status",
          "qrPayload",
          "receiver",
          "externalOrderRef",
          "payUrl",
          "expiresAt",
          "createdAt"
        ],
        "additionalProperties": false
      },
      "BankTransaction": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "starts_with",
            "pattern": "^btx_.*",
            "description": "Transaction ID.",
            "examples": [
              "btx_01JD7QA9F3W5Y7Z1B3D5G7J9KM"
            ]
          },
          "bankAccountId": {
            "type": "string",
            "format": "starts_with",
            "pattern": "^bac_.*",
            "description": "The receiving bank account.",
            "examples": [
              "bac_01JD5A2B4C6D8E0F2G4H6J8K0M"
            ]
          },
          "direction": {
            "type": "string",
            "enum": [
              "in",
              "out"
            ],
            "description": "`in` incoming · `out` outgoing.",
            "examples": [
              "in"
            ]
          },
          "amount": {
            "type": "integer",
            "minimum": 0,
            "maximum": 9007199254740991,
            "description": "Amount, integer VND.",
            "examples": [
              150000
            ]
          },
          "currency": {
            "type": "string",
            "const": "VND",
            "description": "Always `VND`.",
            "examples": [
              "VND"
            ]
          },
          "description": {
            "type": "string",
            "description": "Transfer description exactly as the bank sent it.",
            "examples": [
              "LK7F3K9 thanh toan don hang"
            ]
          },
          "referenceCode": {
            "type": [
              "string",
              "null"
            ],
            "description": "Hensa reference found in the description, if any.",
            "examples": [
              "LK7F3K9"
            ]
          },
          "counterpartyName": {
            "type": [
              "string",
              "null"
            ],
            "description": "Sender name, when the bank provides it.",
            "examples": [
              "NGUYEN VAN AN"
            ]
          },
          "counterpartyBank": {
            "type": [
              "string",
              "null"
            ],
            "description": "Sender bank, when available.",
            "examples": [
              "VCB"
            ]
          },
          "channel": {
            "anyOf": [
              {
                "type": "string",
                "enum": [
                  "counter",
                  "website",
                  "facebook",
                  "zalo",
                  "delivery"
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "Sales channel, when known.",
            "examples": [
              "website"
            ]
          },
          "reconciliation": {
            "type": "string",
            "enum": [
              "matched",
              "pending",
              "unmatched"
            ],
            "description": "`matched` · `pending` needs a human · `unmatched` not an order.",
            "examples": [
              "matched"
            ]
          },
          "matchedOrderRef": {
            "type": [
              "string",
              "null"
            ],
            "description": "Matched order ref, if any.",
            "examples": [
              "DH-1024"
            ]
          },
          "occurredAt": {
            "type": "string",
            "format": "date-time",
            "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$",
            "description": "When the money landed, per the bank.",
            "examples": [
              "2026-11-03T08:03:10Z"
            ]
          }
        },
        "required": [
          "id",
          "bankAccountId",
          "direction",
          "amount",
          "currency",
          "description",
          "referenceCode",
          "counterpartyName",
          "counterpartyBank",
          "channel",
          "reconciliation",
          "matchedOrderRef",
          "occurredAt"
        ],
        "additionalProperties": false
      },
      "WebhookEndpoint": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "starts_with",
            "pattern": "^whe_.*",
            "description": "Endpoint ID.",
            "examples": [
              "whe_01JD7R1S3T5V7X9Z1B3D5F7H9K"
            ]
          },
          "url": {
            "type": "string",
            "format": "starts_with",
            "pattern": "^https:\\/\\/.*",
            "description": "Public HTTPS URL receiving events.",
            "examples": [
              "https://cuahang.vn/webhooks/hensa"
            ]
          },
          "events": {
            "minItems": 1,
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "bank_transaction.created",
                "payment_request.paid",
                "payment_request.partially_paid",
                "payment_request.expired",
                "bank_connection.status_changed"
              ]
            },
            "description": "Subscribed events.",
            "examples": [
              [
                "payment_request.paid",
                "bank_transaction.created"
              ]
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "failing",
              "disabled"
            ],
            "description": "`active` · `failing` (alerts sent) · `disabled`.",
            "examples": [
              "active"
            ]
          },
          "secretLast4": {
            "type": "string",
            "description": "Last 4 characters of the signing secret – for identification.",
            "examples": [
              "9f2a"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$",
            "description": "Creation time.",
            "examples": [
              "2026-11-03T07:00:00Z"
            ]
          }
        },
        "required": [
          "id",
          "url",
          "events",
          "status",
          "secretLast4",
          "createdAt"
        ],
        "additionalProperties": false
      },
      "WebhookDelivery": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "starts_with",
            "pattern": "^whd_.*",
            "description": "Delivery ID.",
            "examples": [
              "whd_01JD7QC4V6X8Z0B2D4F6H8K0MP"
            ]
          },
          "endpointId": {
            "type": "string",
            "format": "starts_with",
            "pattern": "^whe_.*",
            "description": "Target endpoint.",
            "examples": [
              "whe_01JD7R1S3T5V7X9Z1B3D5F7H9K"
            ]
          },
          "eventType": {
            "type": "string",
            "enum": [
              "bank_transaction.created",
              "payment_request.paid",
              "payment_request.partially_paid",
              "payment_request.expired",
              "bank_connection.status_changed"
            ],
            "description": "Event type.",
            "examples": [
              "payment_request.paid"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "succeeded",
              "failed",
              "retrying"
            ],
            "description": "`succeeded` · `failed` · `retrying`.",
            "examples": [
              "succeeded"
            ]
          },
          "httpStatus": {
            "anyOf": [
              {
                "type": "integer",
                "minimum": -9007199254740991,
                "maximum": 9007199254740991
              },
              {
                "type": "null"
              }
            ],
            "description": "HTTP status your server returned.",
            "examples": [
              200
            ]
          },
          "durationMs": {
            "anyOf": [
              {
                "type": "integer",
                "minimum": -9007199254740991,
                "maximum": 9007199254740991
              },
              {
                "type": "null"
              }
            ],
            "description": "Response time (ms).",
            "examples": [
              142
            ]
          },
          "attempt": {
            "type": "integer",
            "minimum": -9007199254740991,
            "maximum": 9007199254740991,
            "description": "Attempt number (1–8).",
            "examples": [
              1
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$",
            "description": "Delivery time.",
            "examples": [
              "2026-11-03T08:03:12Z"
            ]
          }
        },
        "required": [
          "id",
          "endpointId",
          "eventType",
          "status",
          "httpStatus",
          "durationMs",
          "attempt",
          "createdAt"
        ],
        "additionalProperties": false
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              },
              "request_id": {
                "type": "string"
              }
            },
            "required": [
              "code",
              "message",
              "request_id"
            ],
            "additionalProperties": false,
            "description": "Stable `code` for logic · human `message` · `request_id` for support.",
            "examples": [
              {
                "code": "no_active_bank_account",
                "message": "Link a bank account before creating payment requests.",
                "request_id": "req_01JD7QF0X2C4M8R6T1Y3W5Z7NB"
              }
            ]
          }
        },
        "required": [
          "error"
        ],
        "additionalProperties": false
      }
    }
  }
}
