{
  "openapi": "3.1.0",
  "info": {
    "title": "Tempest Pay API",
    "version": "1.0.0",
    "description": "Backend-only merchant API. All charge prices are in USD; customers select supported settlement crypto at checkout. Keep secret keys out of browser/mobile apps. Use test keys for sandbox testing and live keys only after approval/operational readiness. Additional docs: /docs/sandbox.html, /docs/examples.html, /docs/migration.html, /docs/postman.tempest-pay.json.",
    "contact": {
      "name": "Tempest Touch Support",
      "email": "support@tempesttouchwallet.com"
    }
  },
  "servers": [
    { "url": "https://tempesttouchwallet.com/api/v1" }
  ],
  "security": [
    { "bearerAuth": [] }
  ],
  "paths": {
    "/ping": {
      "get": {
        "summary": "Check the API key",
        "responses": {
          "200": { "description": "Key is valid" },
          "401": { "$ref": "#/components/responses/ApiError" }
        }
      }
    },
    "/charges": {
      "get": {
        "summary": "List charges",
        "parameters": [
          { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 20 } },
          { "name": "status", "in": "query", "schema": { "type": "string" } },
          { "name": "reference", "in": "query", "schema": { "type": "string" } },
          { "name": "starting_after", "in": "query", "schema": { "type": "string", "description": "A public charge id from the previous page." } }
        ],
        "responses": {
          "200": {
            "description": "Charge list",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ListCharges" } } }
          },
          "401": { "$ref": "#/components/responses/ApiError" }
        }
      },
      "post": {
        "summary": "Create a hosted checkout charge",
        "description": "Redirect the customer to hosted_url. At terminal status, checkout returns to success_url (paid/overpaid) or cancel_url (expired/underpaid/cancelled), adding tempest_status, tempest_message and tempest_charge_id query parameters. Fulfil orders only after a verified charge.paid webhook or a server-side charge retrieval.",
        "parameters": [
          { "$ref": "#/components/parameters/IdempotencyKey" }
        ],
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateCharge" } } }
        },
        "responses": {
          "201": {
            "description": "Charge created",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Charge" } } }
          },
          "200": {
            "description": "An existing charge was returned for this idempotency key",
            "headers": { "Idempotent-Replayed": { "schema": { "type": "string", "const": "true" } } },
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Charge" } } }
          },
          "400": { "$ref": "#/components/responses/ApiError" },
          "401": { "$ref": "#/components/responses/ApiError" },
          "422": { "$ref": "#/components/responses/ApiError" },
          "429": { "$ref": "#/components/responses/ApiError" }
        }
      }
    },
    "/charges/{id}": {
      "parameters": [
        { "$ref": "#/components/parameters/ChargeId" }
      ],
      "get": {
        "summary": "Retrieve a charge",
        "responses": {
          "200": {
            "description": "Charge details",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Charge" } } }
          },
          "404": { "$ref": "#/components/responses/ApiError" }
        }
      }
    },
    "/charges/{id}/cancel": {
      "parameters": [
        { "$ref": "#/components/parameters/ChargeId" }
      ],
      "post": {
        "summary": "Cancel an unpaid charge",
        "responses": {
          "200": {
            "description": "Updated charge",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Charge" } } }
          },
          "409": { "$ref": "#/components/responses/ApiError" }
        }
      }
    },
    "/charges/{id}/refund": {
      "parameters": [
        { "$ref": "#/components/parameters/ChargeId" }
      ],
      "post": {
        "summary": "Refund a charge",
        "deprecated": true,
        "description": "Temporarily unavailable. Returns 503 until the payout processor can execute and reconcile refunds; this request does not book a refund.",
        "responses": {
          "503": { "$ref": "#/components/responses/ApiError" }
        }
      }
    },
    "/events": {
      "get": {
        "summary": "List recent events",
        "parameters": [
          { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 20 } },
          { "name": "type", "in": "query", "schema": { "type": "string" } },
          { "name": "created_before", "in": "query", "description": "Unix timestamp in seconds.", "schema": { "type": "integer" } }
        ],
        "responses": {
          "200": {
            "description": "Event list",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ListEvents" } } }
          }
        }
      }
    },
    "/balance": {
      "get": {
        "summary": "Get available balance",
        "responses": {
          "200": {
            "description": "Per-asset available balance",
            "content": { "application/json": { "schema": { "type": "object" } } }
          }
        }
      }
    },
    "/assets": {
      "get": {
        "summary": "List supported assets",
        "responses": {
          "200": {
            "description": "Networks, assets, decimals, and confirmations",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ListAssets" } } }
          }
        }
      }
    },
    "/test/charges/{id}/simulate": {
      "parameters": [
        { "$ref": "#/components/parameters/ChargeId" }
      ],
      "post": {
        "summary": "Simulate a test charge payment",
        "description": "Test-mode keys only.",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "network": { "type": "string" },
                  "symbol": { "type": "string" },
                  "fraction": { "type": "number", "minimum": 0.01, "maximum": 1 }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated charge",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Charge" } } }
          },
          "403": { "$ref": "#/components/responses/ApiError" },
          "409": { "$ref": "#/components/responses/ApiError" }
        }
      }
    },
    "/webhook-endpoints": {
      "get": {
        "summary": "List webhook endpoints",
        "responses": {
          "200": {
            "description": "Webhook endpoint list",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ListWebhookEndpoints" } } }
          }
        }
      },
      "post": {
        "summary": "Create a webhook endpoint",
        "description": "Store the returned signing secret on your server only.",
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateWebhookEndpoint" } } }
        },
        "responses": {
          "201": {
            "description": "Created endpoint",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookEndpoint" } } }
          }
        }
      }
    },
    "/webhook-endpoints/{id}": {
      "parameters": [
        { "$ref": "#/components/parameters/EndpointId" }
      ],
      "get": {
        "summary": "Retrieve a webhook endpoint",
        "responses": {
          "200": {
            "description": "Endpoint details",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookEndpoint" } } }
          },
          "404": { "$ref": "#/components/responses/ApiError" }
        }
      },
      "patch": {
        "summary": "Update a webhook endpoint",
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UpdateWebhookEndpoint" } } }
        },
        "responses": {
          "200": {
            "description": "Updated endpoint",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookEndpoint" } } }
          }
        }
      },
      "delete": {
        "summary": "Delete a webhook endpoint",
        "responses": {
          "200": { "description": "Deleted endpoint" }
        }
      }
    },
    "/payment-links": {
      "get": {
        "summary": "List payment links",
        "responses": {
          "200": {
            "description": "Payment link list",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ListPaymentLinks" } } }
          }
        }
      },
      "post": {
        "summary": "Create a fixed-price payment link",
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePaymentLink" } } }
        },
        "responses": {
          "201": {
            "description": "Created payment link",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaymentLink" } } }
          }
        }
      }
    },
    "/payment-links/{id}": {
      "parameters": [
        { "$ref": "#/components/parameters/LinkId" }
      ],
      "get": {
        "summary": "Retrieve a payment link",
        "responses": {
          "200": {
            "description": "Payment link details",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaymentLink" } } }
          },
          "404": { "$ref": "#/components/responses/ApiError" }
        }
      },
      "patch": {
        "summary": "Update a payment link",
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/UpdatePaymentLink" } } }
        },
        "responses": {
          "200": {
            "description": "Updated payment link",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaymentLink" } } }
          }
        }
      },
      "delete": {
        "summary": "Delete a payment link",
        "responses": {
          "200": { "description": "Deleted payment link" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "Tempest Pay secret key"
      }
    },
    "parameters": {
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": true,
        "description": "Stable, unique key per order. A retry returns the original charge.",
        "schema": { "type": "string", "minLength": 1, "maxLength": 200 }
      },
      "ChargeId": {
        "name": "id",
        "in": "path",
        "required": true,
        "schema": { "type": "string", "pattern": "^ch_" }
      },
      "EndpointId": {
        "name": "id",
        "in": "path",
        "required": true,
        "schema": { "type": "string", "format": "uuid" }
      },
      "LinkId": {
        "name": "id",
        "in": "path",
        "required": true,
        "schema": { "type": "string", "format": "uuid" }
      }
    },
    "responses": {
      "ApiError": {
        "description": "API error",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "required": ["error"],
              "properties": {
                "error": {
                  "type": "object",
                  "required": ["type", "code", "message", "request_id"],
                  "properties": {
                    "type": { "type": "string" },
                    "code": { "type": "string" },
                    "message": { "type": "string" },
                    "request_id": { "type": "string" }
                  }
                }
              }
            }
          }
        }
      }
    },
    "schemas": {
      "CreateCharge": {
        "type": "object",
        "required": ["amount", "title", "reference"],
        "properties": {
          "amount": { "type": "string", "pattern": "^\\d+(\\.\\d{1,8})?$", "examples": ["25.00"] },
          "currency": { "type": "string", "const": "USD", "default": "USD", "description": "Charge prices must be denominated in USD." },
          "title": { "type": "string", "maxLength": 120 },
          "description": { "type": "string", "maxLength": 500 },
          "reference": { "type": "string", "maxLength": 120 },
          "customer_email": { "type": "string", "format": "email" },
          "success_url": { "type": "string", "format": "uri", "description": "Return destination for paid and overpaid outcomes; checkout appends tempest_status, tempest_message and tempest_charge_id." },
          "cancel_url": { "type": "string", "format": "uri", "description": "Return destination for expired, underpaid and cancelled outcomes; checkout appends tempest_status, tempest_message and tempest_charge_id." },
          "expires_in": { "type": "integer", "minimum": 60, "maximum": 86400, "default": 1800 },
          "accepted": { "type": "array", "items": { "$ref": "#/components/schemas/Asset" } },
          "metadata": { "type": "object", "maxProperties": 20, "additionalProperties": { "type": "string" } }
        }
      },
      "Charge": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "pattern": "^ch_" },
          "object": { "const": "charge" },
          "livemode": { "type": "boolean" },
          "status": { "type": "string", "enum": ["pending", "processing", "partial", "paid", "overpaid", "underpaid", "expired", "cancelled"] },
          "price": { "type": "object", "properties": { "amount": { "type": "string" }, "currency": { "type": "string" } } },
          "title": { "type": ["string", "null"] },
          "reference": { "type": ["string", "null"] },
          "metadata": { "type": "object" },
          "pay_address": { "type": "string" },
          "hosted_url": { "type": "string", "format": "uri" },
          "options": { "type": "array", "items": { "$ref": "#/components/schemas/AssetAmount" } },
          "payments": { "type": "array", "items": { "type": "object" } },
          "created_at": { "type": "string", "format": "date-time" },
          "expires_at": { "type": "string", "format": "date-time" },
          "paid_at": { "type": ["string", "null"], "format": "date-time" }
        }
      },
      "Asset": {
        "type": "object",
        "required": ["network", "symbol"],
        "properties": {
          "network": { "type": "string" },
          "symbol": { "type": "string" }
        }
      },
      "AssetAmount": {
        "allOf": [
          { "$ref": "#/components/schemas/Asset" },
          {
            "type": "object",
            "properties": {
              "amount": { "type": "string" },
              "decimals": { "type": "integer" },
              "contract": { "type": ["string", "null"] }
            }
          }
        ]
      },
      "ListCharges": {
        "type": "object",
        "properties": {
          "object": { "const": "list" },
          "has_more": { "type": "boolean" },
          "data": { "type": "array", "items": { "$ref": "#/components/schemas/Charge" } }
        }
      },
      "ListEvents": {
        "type": "object",
        "properties": {
          "object": { "const": "list" },
          "has_more": { "type": "boolean" },
          "data": { "type": "array", "items": { "type": "object" } }
        }
      },
      "ListAssets": {
        "type": "object",
        "properties": {
          "object": { "const": "list" },
          "data": { "type": "array", "items": { "type": "object" } }
        }
      },
      "CreateWebhookEndpoint": {
        "type": "object",
        "required": ["url"],
        "properties": {
          "url": { "type": "string", "format": "uri", "description": "Public HTTPS endpoint." },
          "description": { "type": "string", "maxLength": 200 },
          "events": { "type": "array", "items": { "type": "string" } }
        }
      },
      "UpdateWebhookEndpoint": {
        "type": "object",
        "minProperties": 1,
        "properties": {
          "url": { "type": "string", "format": "uri" },
          "description": { "type": ["string", "null"] },
          "events": { "type": "array", "items": { "type": "string" } },
          "enabled": { "type": "boolean" }
        }
      },
      "WebhookEndpoint": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "url": { "type": "string", "format": "uri" },
          "description": { "type": ["string", "null"] },
          "events": { "type": "array", "items": { "type": "string" } },
          "secret": { "type": "string" },
          "enabled": { "type": "boolean" },
          "created_at": { "type": "string", "format": "date-time" }
        }
      },
      "ListWebhookEndpoints": {
        "type": "object",
        "properties": {
          "object": { "const": "list" },
          "data": { "type": "array", "items": { "$ref": "#/components/schemas/WebhookEndpoint" } }
        }
      },
      "CreatePaymentLink": {
        "type": "object",
        "required": ["amount", "title"],
        "properties": {
          "amount": { "type": "string", "description": "Price denominated in USD." },
          "currency": { "type": "string", "const": "USD", "default": "USD" },
          "title": { "type": "string", "maxLength": 120 },
          "description": { "type": "string", "maxLength": 500 },
          "success_url": { "type": "string", "format": "uri" },
          "cancel_url": { "type": "string", "format": "uri" }
        }
      },
      "UpdatePaymentLink": {
        "type": "object",
        "minProperties": 1,
        "properties": {
          "title": { "type": "string" },
          "description": { "type": ["string", "null"] },
          "amount": { "type": "string" },
          "currency": { "type": "string" },
          "success_url": { "type": ["string", "null"] },
          "cancel_url": { "type": ["string", "null"] },
          "active": { "type": "boolean" }
        }
      },
      "PaymentLink": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "public_id": { "type": "string" },
          "title": { "type": "string" },
          "price_amount": { "type": "string" },
          "price_currency": { "type": "string" },
          "active": { "type": "boolean" },
          "created_at": { "type": "string", "format": "date-time" }
        }
      },
      "ListPaymentLinks": {
        "type": "object",
        "properties": {
          "object": { "const": "list" },
          "data": { "type": "array", "items": { "$ref": "#/components/schemas/PaymentLink" } }
        }
      }
    }
  }
}
