{
  "openapi": "3.1.0",
  "info": {
    "title": "Paya API",
    "description": "Stablecoin payment platform — USDC on Solana. Auth via JWT. Idempotency keys on writes. HMAC-signed webhooks.",
    "version": "0.1.0",
    "contact": { "name": "Paya", "email": "dev@paya.fund", "url": "https://paya.fund" },
    "license": { "name": "Proprietary" }
  },
  "servers": [
    { "url": "https://paya.fund/api/v1", "description": "Production" }
  ],
  "tags": [
    { "name": "Health" },
    { "name": "Auth" },
    { "name": "Profile" },
    { "name": "Wallets" },
    { "name": "Payments" },
    { "name": "Payment Links" },
    { "name": "Invoices" },
    { "name": "Payroll" },
    { "name": "Cashout" },
    { "name": "KYC" },
    { "name": "Webhooks" },
    { "name": "Billing" },
    { "name": "Notifications" },
    { "name": "Export" }
  ],
  "paths": {
    "/health": {
      "get": {
        "tags": ["Health"], "summary": "Liveness check", "security": [],
        "responses": { "200": { "description": "Service is up", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Health" } } } } }
      }
    },
    "/health/ready": {
      "get": {
        "tags": ["Health"], "summary": "Readiness — db + redis", "security": [],
        "responses": { "200": { "description": "Ready", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Health" } } } }, "503": { "description": "Not ready" } }
      }
    },
    "/auth/register": {
      "post": {
        "tags": ["Auth"], "summary": "Create an account", "security": [],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RegisterRequest" } } } },
        "responses": { "200": { "description": "Account created + JWT issued", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AuthResponse" } } } }, "400": { "$ref": "#/components/responses/Error" }, "409": { "$ref": "#/components/responses/Error" }, "429": { "$ref": "#/components/responses/Error" } }
      }
    },
    "/auth/login": {
      "post": {
        "tags": ["Auth"], "summary": "Exchange password for JWT", "security": [],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LoginRequest" } } } },
        "responses": { "200": { "description": "JWT issued", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AuthResponse" } } } }, "401": { "$ref": "#/components/responses/Error" }, "429": { "$ref": "#/components/responses/Error" } }
      }
    },
    "/auth/refresh": {
      "post": {
        "tags": ["Auth"], "summary": "Refresh access token", "security": [],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "refresh_token": { "type": "string" } }, "required": ["refresh_token"] } } } },
        "responses": { "200": { "description": "New JWT", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AuthResponse" } } } } }
      }
    },
    "/auth/me": {
      "get": { "tags": ["Auth"], "summary": "Current authenticated user",
        "responses": { "200": { "description": "User", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/User" } } } } } }
    },
    "/auth/logout": { "post": { "tags": ["Auth"], "summary": "Invalidate session", "responses": { "204": { "description": "Done" } } } },
    "/auth/change-password": {
      "post": { "tags": ["Auth"], "summary": "Change password",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["old_password","new_password"], "properties": { "old_password": { "type": "string" }, "new_password": { "type": "string", "minLength": 12 } } } } } },
        "responses": { "204": { "description": "Done" }, "401": { "$ref": "#/components/responses/Error" } } }
    },
    "/wallets": {
      "get": { "tags": ["Wallets"], "summary": "List your wallets",
        "responses": { "200": { "description": "List", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Wallet" } } } } } } },
      "post": { "tags": ["Wallets"], "summary": "Create a new wallet (Solana keypair generated)",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "label": { "type": "string" } } } } } },
        "responses": { "201": { "description": "Created", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Wallet" } } } } } }
    },
    "/wallets/{id}": {
      "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }],
      "get": { "tags": ["Wallets"], "summary": "Get wallet by ID", "responses": { "200": { "description": "Wallet", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Wallet" } } } } } }
    },
    "/wallets/{id}/balance": {
      "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }],
      "post": { "tags": ["Wallets"], "summary": "Refresh on-chain balance",
        "responses": { "200": { "description": "Updated wallet" } } }
    },
    "/wallets/{id}/default": {
      "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }],
      "post": { "tags": ["Wallets"], "summary": "Mark as default", "responses": { "204": { "description": "Done" } } }
    },
    "/payments": {
      "get": { "tags": ["Payments"], "summary": "List payments you sent",
        "parameters": [{ "name": "limit", "in": "query", "schema": { "type": "integer", "default": 50 } }, { "name": "cursor", "in": "query", "schema": { "type": "string" } }],
        "responses": { "200": { "description": "List", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Payment" } } } } } } },
      "post": { "tags": ["Payments"], "summary": "Send a USDC payment",
        "parameters": [{ "name": "Idempotency-Key", "in": "header", "schema": { "type": "string", "format": "uuid" }, "description": "Recommended for safe retry" }],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePaymentRequest" } } } },
        "responses": { "201": { "description": "Queued for submission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Payment" } } } }, "402": { "description": "Insufficient balance" }, "403": { "description": "Fraud-blocked recipient" } } }
    },
    "/payments/{id}": {
      "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }],
      "get": { "tags": ["Payments"], "summary": "Get payment by ID", "responses": { "200": { "description": "Payment", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Payment" } } } } } }
    },
    "/payments/{id}/cancel": {
      "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }],
      "post": { "tags": ["Payments"], "summary": "Cancel a queued payment (only before submission)", "responses": { "204": { "description": "Cancelled" }, "409": { "description": "Already submitted" } } }
    },
    "/payments/received": {
      "get": { "tags": ["Payments"], "summary": "Inbound payments",
        "responses": { "200": { "description": "List", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Payment" } } } } } } }
    },
    "/payment-links": {
      "get": { "tags": ["Payment Links"], "summary": "Your payment links",
        "responses": { "200": { "description": "List", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/PaymentLink" } } } } } } },
      "post": { "tags": ["Payment Links"], "summary": "Create a payment link",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreatePaymentLinkRequest" } } } },
        "responses": { "201": { "description": "Created", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaymentLink" } } } } } }
    },
    "/payment-links/{slug}": {
      "parameters": [{ "name": "slug", "in": "path", "required": true, "schema": { "type": "string" } }],
      "get": { "tags": ["Payment Links"], "summary": "Public lookup (also serves HTML when Accept: text/html)", "security": [],
        "responses": { "200": { "description": "Payment link", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaymentLink" } }, "text/html": { "schema": { "type": "string" } } } } } }
    },
    "/invoices": {
      "get": { "tags": ["Invoices"], "summary": "List invoices",
        "responses": { "200": { "description": "List", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Invoice" } } } } } } },
      "post": { "tags": ["Invoices"], "summary": "Create draft invoice",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CreateInvoiceRequest" } } } },
        "responses": { "201": { "description": "Created" } } }
    },
    "/invoices/{id}/send": {
      "parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }],
      "post": { "tags": ["Invoices"], "summary": "Email invoice to recipient", "responses": { "204": { "description": "Sent" } } }
    },
    "/invoices/shared/{token}": {
      "parameters": [{ "name": "token", "in": "path", "required": true, "schema": { "type": "string" } }],
      "get": { "tags": ["Invoices"], "summary": "Public shared-invoice viewer (also serves HTML)", "security": [],
        "responses": { "200": { "description": "Invoice", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Invoice" } }, "text/html": { "schema": { "type": "string" } } } } } }
    },
    "/cashout": {
      "post": { "tags": ["Cashout"], "summary": "Initiate USDC → fiat cashout",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["amount_usdc","provider","destination"], "properties": { "amount_usdc": { "type": "number" }, "provider": { "type": "string", "enum": ["bank_transfer","mobile_money","exchange","partner"] }, "destination": { "type": "object", "additionalProperties": true } } } } } },
        "responses": { "201": { "description": "Cashout request created" } } }
    },
    "/kyc/submit": {
      "post": { "tags": ["KYC"], "summary": "Submit KYC tier upgrade",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["tier"], "properties": { "tier": { "type": "string", "enum": ["basic","standard","enhanced"] }, "document_type": { "type": "string" }, "document_country": { "type": "string" } } } } } },
        "responses": { "200": { "description": "Submission accepted" }, "400": { "description": "KYC unavailable (stub mode)" } } }
    },
    "/webhooks": {
      "get": { "tags": ["Webhooks"], "summary": "List your webhooks",
        "responses": { "200": { "description": "List", "content": { "application/json": { "schema": { "type": "array", "items": { "$ref": "#/components/schemas/Webhook" } } } } } } },
      "post": { "tags": ["Webhooks"], "summary": "Register webhook",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["url","events"], "properties": { "url": { "type": "string", "format": "uri" }, "events": { "type": "array", "items": { "type": "string" } } } } } } },
        "responses": { "201": { "description": "Registered + secret returned (only shown once)" } } }
    },
    "/billing/checkout": {
      "post": { "tags": ["Billing"], "summary": "Stripe Checkout session",
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["plan"], "properties": { "plan": { "type": "string", "enum": ["pro","enterprise"] } } } } } },
        "responses": { "200": { "description": "Checkout URL", "content": { "application/json": { "schema": { "type": "object", "properties": { "checkout_url": { "type": "string" } } } } } } } }
    },
    "/billing/portal": {
      "post": { "tags": ["Billing"], "summary": "Stripe customer portal session", "responses": { "200": { "description": "Portal URL" } } }
    },
    "/notifications": {
      "get": { "tags": ["Notifications"], "summary": "Recent notifications", "responses": { "200": { "description": "List" } } }
    },
    "/export/transactions": {
      "get": { "tags": ["Export"], "summary": "CSV export of all transactions",
        "responses": { "200": { "description": "CSV", "content": { "text/csv": { "schema": { "type": "string" } } } } } }
    }
  },
  "components": {
    "securitySchemes": {
      "BearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "JWT" }
    },
    "schemas": {
      "Health": { "type": "object", "properties": {
        "status": { "type": "string", "enum": ["ok","degraded"] },
        "service": { "type": "string", "example": "paya-api" },
        "version": { "type": "string" }
      } },
      "RegisterRequest": { "type": "object", "required": ["email","password"], "properties": {
        "email": { "type": "string", "format": "email" },
        "password": { "type": "string", "minLength": 12 },
        "display_name": { "type": "string" }
      } },
      "LoginRequest": { "type": "object", "required": ["email","password"], "properties": {
        "email": { "type": "string", "format": "email" },
        "password": { "type": "string" }
      } },
      "AuthResponse": { "type": "object", "properties": {
        "token": { "type": "string", "description": "Short-lived JWT (1h)" },
        "refresh_token": { "type": "string", "format": "uuid" },
        "user": { "$ref": "#/components/schemas/User" }
      } },
      "User": { "type": "object", "properties": {
        "id": { "type": "string", "format": "uuid" },
        "email": { "type": "string", "format": "email" },
        "display_name": { "type": "string" },
        "role": { "type": "string", "enum": ["user","admin"] },
        "email_verified": { "type": "boolean" },
        "is_active": { "type": "boolean" },
        "last_login_at": { "type": "string", "format": "date-time", "nullable": true },
        "created_at": { "type": "string", "format": "date-time" }
      } },
      "Wallet": { "type": "object", "properties": {
        "id": { "type": "string", "format": "uuid" },
        "user_id": { "type": "string", "format": "uuid" },
        "label": { "type": "string", "nullable": true },
        "pubkey": { "type": "string", "description": "Solana base58 pubkey" },
        "token_account": { "type": "string", "nullable": true, "description": "USDC associated token account" },
        "custody_type": { "type": "string", "enum": ["platform","external"] },
        "status": { "type": "string", "enum": ["active","suspended","exported"] },
        "usdc_balance": { "type": "number" },
        "sol_balance": { "type": "number" },
        "balance_updated_at": { "type": "string", "format": "date-time", "nullable": true },
        "network": { "type": "string", "enum": ["mainnet-beta","devnet"] },
        "is_default": { "type": "boolean" },
        "created_at": { "type": "string", "format": "date-time" }
      } },
      "Payment": { "type": "object", "properties": {
        "id": { "type": "string", "format": "uuid" },
        "sender_id": { "type": "string", "format": "uuid" },
        "sender_wallet_id": { "type": "string", "format": "uuid" },
        "recipient_pubkey": { "type": "string" },
        "recipient_user_id": { "type": "string", "format": "uuid", "nullable": true },
        "amount": { "type": "number" },
        "currency": { "type": "string", "default": "USDC" },
        "status": { "type": "string", "enum": ["pending","queued","submitted","confirmed","failed","cancelled"] },
        "tx_signature": { "type": "string", "nullable": true },
        "memo": { "type": "string", "nullable": true },
        "idempotency_key": { "type": "string", "format": "uuid", "nullable": true },
        "created_at": { "type": "string", "format": "date-time" }
      } },
      "CreatePaymentRequest": { "type": "object", "required": ["wallet_id","recipient_pubkey","amount"], "properties": {
        "wallet_id": { "type": "string", "format": "uuid" },
        "recipient_pubkey": { "type": "string" },
        "amount": { "type": "number", "minimum": 0.01 },
        "memo": { "type": "string" }
      } },
      "PaymentLink": { "type": "object", "properties": {
        "id": { "type": "string", "format": "uuid" },
        "creator_id": { "type": "string", "format": "uuid" },
        "slug": { "type": "string" },
        "amount": { "type": "number", "nullable": true, "description": "null = open amount" },
        "currency": { "type": "string" },
        "description": { "type": "string", "nullable": true },
        "recipient_wallet_id": { "type": "string", "format": "uuid" },
        "is_active": { "type": "boolean" },
        "uses_count": { "type": "integer" },
        "max_uses": { "type": "integer", "nullable": true },
        "expires_at": { "type": "string", "format": "date-time", "nullable": true },
        "created_at": { "type": "string", "format": "date-time" }
      } },
      "CreatePaymentLinkRequest": { "type": "object", "required": ["recipient_wallet_id"], "properties": {
        "recipient_wallet_id": { "type": "string", "format": "uuid" },
        "amount": { "type": "number", "nullable": true },
        "description": { "type": "string" },
        "expires_in_days": { "type": "integer", "minimum": 1, "maximum": 365 },
        "max_uses": { "type": "integer", "minimum": 1 }
      } },
      "Invoice": { "type": "object", "properties": {
        "id": { "type": "string", "format": "uuid" },
        "number": { "type": "string" },
        "status": { "type": "string", "enum": ["draft","sent","paid","overdue","cancelled"] },
        "amount": { "type": "number" },
        "currency": { "type": "string" },
        "issue_date": { "type": "string", "format": "date" },
        "due_date": { "type": "string", "format": "date" },
        "client_name": { "type": "string" },
        "client_email": { "type": "string", "format": "email" },
        "lines": { "type": "array", "items": { "type": "object" } }
      } },
      "CreateInvoiceRequest": { "type": "object", "required": ["client_name","client_email"], "properties": {
        "client_name": { "type": "string" },
        "client_email": { "type": "string", "format": "email" },
        "due_date": { "type": "string", "format": "date" },
        "currency": { "type": "string", "default": "USDC" },
        "notes": { "type": "string" }
      } },
      "Webhook": { "type": "object", "properties": {
        "id": { "type": "string", "format": "uuid" },
        "url": { "type": "string", "format": "uri" },
        "events": { "type": "array", "items": { "type": "string" } },
        "is_active": { "type": "boolean" },
        "failure_count": { "type": "integer" }
      } },
      "Error": { "type": "object", "properties": {
        "error": { "type": "object", "properties": {
          "code": { "type": "string" },
          "message": { "type": "string" }
        } }
      } }
    },
    "responses": {
      "Error": { "description": "Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
    }
  },
  "security": [{ "BearerAuth": [] }]
}
