{
  "openapi": "3.1.0",
  "info": {
    "title": "XRPay Cashier API",
    "version": "2026-08-22",
    "description": "Server-to-server API for creating multi-rail payment intents, reconciling cashier orders, issuing refunds, and consuming durable webhooks. Fiat amounts are integer minor units."
  },
  "servers": [{ "url": "https://api.xrpay.it" }],
  "security": [{ "bearerAuth": [] }],
  "tags": [
    { "name": "Payment intents" },
    { "name": "Connected account" },
    { "name": "Refunds" },
    { "name": "Webhooks" },
    { "name": "Sandbox" }
  ],
  "paths": {
    "/api/v1/capabilities": {
      "get": {
        "tags": ["Payment intents"],
        "summary": "List the authenticated merchant's ready payment methods and settlement destinations",
        "operationId": "retrieveAccountCapabilities",
        "responses": { "200": { "description": "Rail-derived merchant capabilities for direct keys or connected-account tokens" }, "401": { "$ref": "#/components/responses/Error" } }
      }
    },
    "/api/v1/connect/account": {
      "get": {
        "tags": ["Connected account"],
        "summary": "Retrieve the merchant connected to this access token",
        "operationId": "retrieveConnectedAccount",
        "responses": { "200": { "description": "Connected merchant account" }, "401": { "$ref": "#/components/responses/Error" } }
      }
    },
    "/api/v1/connect/capabilities": {
      "get": {
        "tags": ["Connected account"],
        "summary": "List ready payment methods and settlement destinations",
        "operationId": "retrieveConnectedAccountCapabilities",
        "responses": { "200": { "description": "Rail-derived connected merchant capabilities" }, "401": { "$ref": "#/components/responses/Error" } }
      }
    },
    "/api/v1/connect/revoke": {
      "post": {
        "tags": ["Connected account"],
        "summary": "Revoke the current platform connection",
        "operationId": "revokeConnectedAccountConnection",
        "responses": { "200": { "description": "Connection revoked" }, "401": { "$ref": "#/components/responses/Error" } }
      }
    },
    "/api/v1/payment-intents": {
      "get": {
        "tags": ["Payment intents"],
        "summary": "List payment intents",
        "operationId": "listPaymentIntents",
        "parameters": [
          { "name": "status", "in": "query", "schema": { "enum": ["requires_customer_action", "processing", "succeeded", "failed", "expired", "canceled"] } },
          { "name": "external_order_id", "in": "query", "schema": { "type": "string" } },
          { "name": "cursor", "in": "query", "schema": { "type": "string", "pattern": "^dlv_" } },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 25 } }
        ],
        "responses": { "200": { "description": "Payment intent list" } }
      },
      "post": {
        "tags": ["Payment intents"],
        "summary": "Create a payment intent",
        "operationId": "createPaymentIntent",
        "parameters": [{ "$ref": "#/components/parameters/IdempotencyKey" }],
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaymentIntentCreate" } } }
        },
        "responses": {
          "201": { "description": "Created", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaymentIntent" } } } },
          "200": { "description": "Idempotent replay", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaymentIntent" } } } },
          "400": { "$ref": "#/components/responses/Error" },
          "401": { "$ref": "#/components/responses/Error" },
          "409": { "$ref": "#/components/responses/Error" },
          "429": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/api/v1/payment-intents/{id}": {
      "get": {
        "tags": ["Payment intents"],
        "summary": "Retrieve a payment intent",
        "operationId": "retrievePaymentIntent",
        "parameters": [{ "$ref": "#/components/parameters/ResourceId" }],
        "responses": {
          "200": { "description": "Payment intent", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaymentIntent" } } } },
          "404": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/api/v1/payment-intents/{id}/cancel": {
      "post": {
        "tags": ["Payment intents"],
        "summary": "Cancel an unpaid payment intent",
        "operationId": "cancelPaymentIntent",
        "parameters": [{ "$ref": "#/components/parameters/ResourceId" }],
        "responses": {
          "200": { "description": "Canceled", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaymentIntent" } } } },
          "409": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/api/v1/payment-intents/{id}/simulate": {
      "post": {
        "tags": ["Sandbox"],
        "summary": "Simulate a test payment outcome",
        "description": "Available only with test credentials. No ledger transaction is submitted.",
        "operationId": "simulatePaymentIntent",
        "parameters": [{ "$ref": "#/components/parameters/ResourceId" }],
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "type": "object", "required": ["outcome"], "properties": { "outcome": { "enum": ["succeeded", "failed", "expired"] } } } } }
        },
        "responses": {
          "200": { "description": "Simulated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaymentIntent" } } } },
          "403": { "$ref": "#/components/responses/Error" },
          "409": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/api/v1/refunds": {
      "get": {
        "tags": ["Refunds"],
        "summary": "List refunds",
        "operationId": "listRefunds",
        "parameters": [
          { "name": "payment_intent_id", "in": "query", "schema": { "type": "string" } },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 25 } }
        ],
        "responses": {
          "200": { "description": "Refund list", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RefundList" } } } }
        }
      },
      "post": {
        "tags": ["Refunds"],
        "summary": "Create a full or partial refund",
        "description": "Live XRPay refunds are non-custodial and enter requires_action until the merchant approves and signs them.",
        "operationId": "createRefund",
        "parameters": [{ "$ref": "#/components/parameters/IdempotencyKey" }],
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RefundCreate" } } }
        },
        "responses": {
          "201": { "description": "Created", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Refund" } } } },
          "200": { "description": "Idempotent replay", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Refund" } } } },
          "409": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/api/v1/refunds/{id}": {
      "get": {
        "tags": ["Refunds"],
        "summary": "Retrieve a refund",
        "operationId": "retrieveRefund",
        "parameters": [{ "$ref": "#/components/parameters/ResourceId" }],
        "responses": {
          "200": { "description": "Refund", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Refund" } } } },
          "404": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/api/v1/refunds/{id}/simulate": {
      "post": {
        "tags": ["Sandbox"],
        "summary": "Simulate a test refund outcome",
        "operationId": "simulateRefund",
        "parameters": [{ "$ref": "#/components/parameters/ResourceId" }],
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "type": "object", "required": ["outcome"], "properties": { "outcome": { "enum": ["completed", "failed"] } } } } }
        },
        "responses": {
          "200": { "description": "Simulated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Refund" } } } },
          "403": { "$ref": "#/components/responses/Error" },
          "409": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/api/v1/webhooks": {
      "get": {
        "tags": ["Webhooks"],
        "summary": "List webhook endpoints for the current credential mode",
        "operationId": "listWebhookEndpoints",
        "responses": {
          "200": { "description": "Mode-scoped endpoint list", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookEndpointList" } } } }
        }
      },
      "post": {
        "tags": ["Webhooks"],
        "summary": "Register a mode-scoped webhook endpoint",
        "description": "The signing secret is returned only when the endpoint is first created; updating the same URL never reveals it again. Test and live credentials create separate endpoints, even for the same URL.",
        "operationId": "createWebhookEndpoint",
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookEndpointCreate" } } }
        },
        "responses": {
          "201": { "description": "Endpoint created", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookEndpoint" } } } },
          "200": { "description": "Existing endpoint updated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookEndpoint" } } } },
          "400": { "$ref": "#/components/responses/Error" }
        }
      },
      "delete": {
        "tags": ["Webhooks"],
        "summary": "Delete a webhook endpoint",
        "operationId": "deleteWebhookEndpoint",
        "parameters": [{ "name": "id", "in": "query", "required": true, "schema": { "type": "string" } }],
        "responses": {
          "200": { "description": "Endpoint deleted" },
          "404": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/api/v1/webhook-deliveries": {
      "get": {
        "tags": ["Webhooks"],
        "summary": "List durable webhook deliveries",
        "operationId": "listWebhookDeliveries",
        "parameters": [
          { "name": "status", "in": "query", "schema": { "enum": ["queued", "delivering", "delivered", "failed"] } },
          { "name": "cursor", "in": "query", "schema": { "type": "string" } },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 25 } }
        ],
        "responses": {
          "200": { "description": "Delivery list", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookDeliveryList" } } } },
          "400": { "$ref": "#/components/responses/Error" }
        }
      },
      "post": {
        "tags": ["Webhooks"],
        "summary": "Replay one webhook delivery with a fresh delivery-attempt cycle",
        "operationId": "replayWebhookDelivery",
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "type": "object", "required": ["delivery_id"], "properties": { "delivery_id": { "type": "string", "pattern": "^dlv_" } } } } }
        },
        "responses": {
          "202": { "description": "Replay accepted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookDelivery" } } } },
          "404": { "$ref": "#/components/responses/Error" },
          "409": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/api/v1/webhooks/{id}/secret": {
      "post": {
        "tags": ["Webhooks"],
        "summary": "Rotate a webhook signing secret",
        "description": "Immediately replaces the endpoint's prior secret. The new secret is returned only in this response.",
        "operationId": "rotateWebhookSecret",
        "parameters": [{ "$ref": "#/components/parameters/ResourceId" }],
        "responses": {
          "201": { "description": "Secret rotated and revealed once", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookSecret" } } } },
          "404": { "$ref": "#/components/responses/Error" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": { "type": "http", "scheme": "bearer", "description": "Secret API key or XRPay Connect access token." }
    },
    "parameters": {
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": true,
        "schema": { "type": "string", "minLength": 8, "maxLength": 255, "pattern": "^[A-Za-z0-9._:/-]+$" },
        "description": "Stable key for one logical operation. Reusing a key with different parameters returns HTTP 409."
      },
      "ResourceId": { "name": "id", "in": "path", "required": true, "schema": { "type": "string" } }
    },
    "responses": {
      "Error": {
        "description": "Error response",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      }
    },
    "schemas": {
      "PaymentIntentCreate": {
        "type": "object",
        "additionalProperties": false,
        "required": ["amount_minor", "currency", "external_order_id"],
        "properties": {
          "amount_minor": { "type": "integer", "minimum": 1, "description": "Fiat amount in the currency's smallest unit." },
          "currency": { "type": "string", "minLength": 3, "maxLength": 3, "examples": ["USD"] },
          "external_order_id": { "type": "string", "maxLength": 200 },
          "order_number": { "type": "string", "maxLength": 200 },
          "customer_email": { "type": "string", "format": "email" },
          "location_id": { "type": "string", "maxLength": 200 },
          "terminal_id": { "type": "string", "maxLength": 200 },
          "operator_id": { "type": "string", "maxLength": 200 },
          "integration_type": { "enum": ["pos", "ecommerce", "invoice", "payment_link", "api"], "default": "pos" },
          "payment_method_types": {
            "type": "array",
            "minItems": 1,
            "uniqueItems": true,
            "items": { "enum": ["card", "mobile_money", "bank", "xrp", "rlusd", "crypto"] },
            "description": "Optional allow-list enforced by both the hosted checkout UI and server actions."
          },
          "settlement_currency": { "enum": ["XRP", "RLUSD"] },
          "metadata": { "type": "object", "maxProperties": 100 },
          "line_items": { "type": "array", "maxItems": 100, "items": { "$ref": "#/components/schemas/LineItem" } },
          "success_url": { "type": "string", "format": "uri" },
          "cancel_url": { "type": "string", "format": "uri" },
          "expires_in": { "type": "integer", "minimum": 60, "maximum": 2592000, "description": "Relative expiry in seconds. Mutually exclusive with expires_at." },
          "expires_at": { "type": "string", "format": "date-time", "description": "Absolute expiry at least 60 seconds and no more than 30 days ahead. Mutually exclusive with expires_in." }
        }
      },
      "LineItem": {
        "type": "object",
        "required": ["name"],
        "properties": {
          "name": { "type": "string", "maxLength": 200 },
          "quantity": { "type": "integer", "minimum": 1, "default": 1 },
          "unit_amount_minor": { "type": "integer", "minimum": 0 },
          "sku": { "type": "string" }
        }
      },
      "PaymentIntent": {
        "type": "object",
        "required": ["object", "id", "status", "amount_minor", "currency", "external_order_id", "livemode", "created_at", "expires_at"],
        "properties": {
          "object": { "const": "payment_intent" },
          "id": { "type": "string" },
          "status": { "enum": ["requires_customer_action", "processing", "succeeded", "failed", "expired", "canceled"] },
          "amount_minor": { "type": "integer" },
          "currency": { "type": "string" },
          "external_order_id": { "type": "string" },
          "order_number": { "type": ["string", "null"] },
          "customer_email": { "type": ["string", "null"] },
          "location_id": { "type": ["string", "null"] },
          "terminal_id": { "type": ["string", "null"] },
          "operator_id": { "type": ["string", "null"] },
          "integration_type": { "type": "string" },
          "metadata": { "type": ["object", "null"] },
          "line_items": { "type": ["array", "null"], "items": { "type": "object" } },
          "payment_method_types": { "type": ["array", "null"], "items": { "enum": ["card", "mobile_money", "bank", "xrp", "rlusd", "crypto"] } },
          "payment_method": { "type": ["string", "null"] },
          "refunded_amount_minor": { "type": "integer", "minimum": 0 },
          "failure_code": { "type": ["string", "null"] },
          "settlement": { "$ref": "#/components/schemas/Settlement" },
          "next_action": { "anyOf": [{ "$ref": "#/components/schemas/PaymentNextAction" }, { "type": "null" }] },
          "livemode": { "type": "boolean" },
          "sandbox_simulated": { "type": "boolean" },
          "api_version": { "type": "string" },
          "created_at": { "type": "string", "format": "date-time" },
          "updated_at": { "type": "string", "format": "date-time" },
          "expires_at": { "type": "string", "format": "date-time" },
          "canceled_at": { "type": ["string", "null"], "format": "date-time" }
        }
      },
      "Settlement": {
        "type": "object",
        "properties": {
          "amount": { "type": "string" },
          "currency": { "type": "string" },
          "transaction_hash": { "type": ["string", "null"] },
          "destination_address": { "type": ["string", "null"] }
        }
      },
      "PaymentNextAction": {
        "type": "object",
        "required": ["type", "qr_data", "hosted_url"],
        "properties": {
          "type": { "const": "display_qr" },
          "qr_data": { "type": "string" },
          "qr_image": { "type": "string" },
          "hosted_url": { "type": "string", "format": "uri" },
          "deep_link": { "type": "string" }
        }
      },
      "RefundCreate": {
        "type": "object",
        "additionalProperties": false,
        "required": ["payment_intent_id"],
        "properties": {
          "payment_intent_id": { "type": "string" },
          "amount_minor": { "type": "integer", "minimum": 1, "description": "Omit to refund the remaining refundable amount." },
          "reason": { "type": "string", "maxLength": 500 },
          "external_refund_id": { "type": "string", "maxLength": 200 }
        }
      },
      "Refund": {
        "type": "object",
        "required": ["object", "id", "payment_intent_id", "status", "amount_minor", "currency"],
        "properties": {
          "object": { "const": "refund" },
          "id": { "type": "string" },
          "payment_intent_id": { "type": "string" },
          "status": { "enum": ["pending", "requires_action", "processing", "completed", "failed", "canceled"] },
          "amount_minor": { "type": "integer" },
          "currency": { "type": "string" },
          "settlement": { "$ref": "#/components/schemas/Settlement" },
          "type": { "enum": ["full", "partial"] },
          "reason": { "type": ["string", "null"] },
          "external_refund_id": { "type": ["string", "null"] },
          "next_action": { "type": ["object", "null"] },
          "livemode": { "type": "boolean" },
          "sandbox_simulated": { "type": "boolean" },
          "created_at": { "type": "string", "format": "date-time" },
          "updated_at": { "type": "string", "format": "date-time" },
          "completed_at": { "type": ["string", "null"], "format": "date-time" },
          "failed_at": { "type": ["string", "null"], "format": "date-time" }
        }
      },
      "RefundList": {
        "type": "object",
        "required": ["object", "data", "has_more"],
        "properties": {
          "object": { "const": "list" },
          "data": { "type": "array", "items": { "$ref": "#/components/schemas/Refund" } },
          "has_more": { "type": "boolean" }
        }
      },
      "WebhookEvent": {
        "type": "object",
        "description": "The id/webhook_id remains stable across all attempts and replay operations.",
        "required": ["id", "object", "api_version", "created_at", "type", "livemode", "data"],
        "properties": {
          "id": { "type": "string", "pattern": "^evt_" },
          "object": { "const": "event" },
          "api_version": { "type": "string" },
          "created_at": { "type": "string", "format": "date-time" },
          "type": { "type": "string" },
          "connected_account_id": { "type": "string" },
          "platform_connection_id": { "type": "string" },
          "livemode": { "type": "boolean", "description": "False for deterministic sandbox events." },
          "data": { "type": "object" }
        }
      },
      "WebhookEndpointCreate": {
        "type": "object",
        "additionalProperties": false,
        "required": ["url", "events"],
        "properties": {
          "name": { "type": "string", "maxLength": 200 },
          "url": { "type": "string", "format": "uri", "description": "A public HTTPS endpoint." },
          "events": { "type": "array", "minItems": 1, "uniqueItems": true, "items": { "enum": ["payment.created", "payment.confirmed", "payment.failed", "payment.canceled", "payment.refunded", "payment.swept", "refund.created", "refund.completed", "refund.failed", "checkout.session.completed", "checkout.session.expired", "escrow.created", "escrow.released", "escrow.cancelled", "invoice.created", "invoice.sent", "invoice.paid", "b2b.order.created", "b2b.order.paid", "b2b.order.shipped"] } }
        }
      },
      "WebhookEndpoint": {
        "type": "object",
        "required": ["object", "id", "url", "events", "is_active", "livemode"],
        "properties": {
          "object": { "const": "webhook_endpoint" },
          "id": { "type": "string" },
          "name": { "type": "string" },
          "url": { "type": "string", "format": "uri" },
          "events": { "type": "array", "items": { "type": "string" } },
          "secret": { "type": "string", "description": "Signing secret. Returned only when an endpoint is first created." },
          "secret_revealed": { "type": "boolean" },
          "is_active": { "type": "boolean" },
          "livemode": { "type": "boolean" },
          "created_at": { "type": "string", "format": "date-time" }
        }
      },
      "WebhookSecret": {
        "type": "object",
        "required": ["object", "webhook_id", "secret", "secret_revealed", "livemode", "rotated_at"],
        "properties": {
          "object": { "const": "webhook_secret" },
          "webhook_id": { "type": "string" },
          "secret": { "type": "string", "pattern": "^whsec_" },
          "secret_revealed": { "const": true },
          "livemode": { "type": "boolean" },
          "rotated_at": { "type": "string", "format": "date-time" }
        }
      },
      "WebhookEndpointList": {
        "type": "object",
        "required": ["object", "data"],
        "properties": {
          "object": { "const": "list" },
          "data": { "type": "array", "items": { "$ref": "#/components/schemas/WebhookEndpoint" } }
        }
      },
      "WebhookDelivery": {
        "type": "object",
        "required": ["object", "id", "event_id", "destination_type", "endpoint", "status", "attempt_count", "created_at", "updated_at"],
        "properties": {
          "object": { "const": "webhook_delivery" },
          "id": { "type": "string", "pattern": "^dlv_" },
          "event_id": { "type": "string", "pattern": "^evt_" },
          "event_type": { "type": ["string", "null"] },
          "destination_type": { "enum": ["webhook_endpoint", "platform_connection"] },
          "destination_id": { "type": "string" },
          "endpoint": { "type": "string", "format": "uri" },
          "status": { "enum": ["queued", "delivering", "delivered", "failed"] },
          "attempt_count": { "type": "integer", "minimum": 0 },
          "last_status_code": { "type": ["integer", "null"] },
          "last_error": { "type": ["string", "null"] },
          "delivered_at": { "type": ["string", "null"], "format": "date-time" },
          "created_at": { "type": "string", "format": "date-time" },
          "updated_at": { "type": "string", "format": "date-time" }
        }
      },
      "WebhookDeliveryList": {
        "type": "object",
        "required": ["object", "data", "has_more", "next_cursor"],
        "properties": {
          "object": { "const": "list" },
          "data": { "type": "array", "items": { "$ref": "#/components/schemas/WebhookDelivery" } },
          "has_more": { "type": "boolean" },
          "next_cursor": { "type": ["string", "null"], "pattern": "^dlv_" }
        }
      },
      "Error": {
        "type": "object",
        "required": ["error"],
        "properties": {
          "error": {
            "type": "object",
            "required": ["type", "message"],
            "properties": {
              "type": { "type": "string" },
              "code": { "type": ["string", "null"] },
              "message": { "type": "string" }
            }
          }
        }
      }
    }
  }
}
