{
  "openapi": "3.0.3",
  "info": {
    "title": "ASG Card API",
    "version": "0.5.0",
    "description": "ASG Card virtual card issuance API for AI agents. Supports two payment rails: x402 with USDC on Stellar mainnet (agent-autonomous) and Stripe Machine Payments Protocol (owner-in-the-loop). See https://asgcard.dev/docs for integration guide."
  },
  "servers": [
    {
      "url": "https://api.asgcard.dev",
      "description": "ASG Card API — Stellar Mainnet + Stripe MPP"
    }
  ],
  "tags": [
    {
      "name": "public",
      "description": "No authentication required"
    },
    {
      "name": "x402",
      "description": "Paid endpoints requiring x402 payment on Stellar"
    },
    {
      "name": "wallet",
      "description": "Wallet-signed endpoints for card management"
    },
    {
      "name": "portal",
      "description": "Owner portal — Telegram-linked wallet actions"
    },
    {
      "name": "ops",
      "description": "Operations dashboard — secured by OPS_API_KEY"
    },
    {
      "name": "stripe-beta",
      "description": "Stripe Machine Payments Protocol — session-based, owner-in-the-loop"
    }
  ],
  "paths": {
    "/health": {
      "get": {
        "tags": [
          "public"
        ],
        "summary": "Health check",
        "operationId": "getHealth",
        "responses": {
          "200": {
            "description": "Health status",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HealthResponse"
                }
              }
            }
          }
        }
      }
    },
    "/pricing": {
      "get": {
        "tags": [
          "public"
        ],
        "summary": "Get pricing ($10 flat card creation, 3.5% on loads)",
        "operationId": "getPricing",
        "responses": {
          "200": {
            "description": "Pricing data",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PricingResponse"
                }
              }
            }
          }
        }
      }
    },
    "/cards/tiers": {
      "get": {
        "tags": [
          "public"
        ],
        "summary": "Get pricing (alias for /pricing)",
        "operationId": "getCardTiers",
        "responses": {
          "200": {
            "description": "Pricing info",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CardTiersResponse"
                }
              }
            }
          }
        }
      }
    },
    "/cards/create/tier/{amount}": {
      "post": {
        "tags": [
          "x402"
        ],
        "summary": "Create a virtual card (amount 0 = card-only $10, or $5–$5,000 with load)",
        "description": "x402 payment flow. Amount 0 creates a card with no initial load ($10 flat fee). Amount $5–$5,000 creates a card with initial load ($10 + amount + 3.5%). Clients receive HTTP 402 with a payment challenge, then retry with X-PAYMENT header. The 201 response includes a `detailsEnvelope` with full PAN, CVV, expiry, billing address.",
        "operationId": "createCardByTier",
        "parameters": [
          {
            "name": "amount",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "description": "Amount in USD (0 = card-only, or $5–$5,000 for card+load)"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateCardRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Card created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateCardResponse"
                }
              }
            }
          },
          "402": {
            "description": "x402 payment challenge (challenge schema may vary by facilitator)"
          }
        }
      }
    },
    "/cards/fund/tier/{amount}": {
      "post": {
        "tags": [
          "x402"
        ],
        "summary": "Fund an existing card ($5–$5,000)",
        "description": "This endpoint participates in the x402 payment flow. Minimum $5, maximum $5,000.",
        "operationId": "fundCardByTier",
        "parameters": [
          {
            "name": "amount",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer",
              "minimum": 5
            },
            "description": "Amount in USD ($5–$5,000)"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FundCardRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Card funded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FundCardResponse"
                }
              }
            }
          },
          "402": {
            "description": "x402 payment challenge"
          }
        }
      }
    },
    "/cards": {
      "get": {
        "tags": [
          "wallet"
        ],
        "summary": "List cards for authenticated wallet",
        "operationId": "listCards",
        "description": "Requires wallet signature authentication. Header names and signing details are documented at https://asgcard.dev/docs.",
        "responses": {
          "200": {
            "description": "Card list",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListCardsResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/ErrorResponse"
          }
        }
      }
    },
    "/cards/{cardId}": {
      "get": {
        "tags": [
          "wallet"
        ],
        "summary": "Get card metadata",
        "operationId": "getCard",
        "description": "Requires wallet signature authentication.",
        "parameters": [
          {
            "$ref": "#/components/parameters/CardId"
          }
        ],
        "responses": {
          "200": {
            "description": "Card details",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetCardResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/ErrorResponse"
          },
          "404": {
            "$ref": "#/components/responses/ErrorResponse"
          }
        }
      }
    },
    "/cards/{cardId}/details": {
      "get": {
        "tags": [
          "wallet"
        ],
        "summary": "Get sensitive card details (PAN, CVV, expiry)",
        "operationId": "getCardSensitiveDetails",
        "description": "Agent-first endpoint for reading card PAN/CVV. Requires wallet signature + X-AGENT-NONCE header for anti-replay. Rate limited to 5 unique nonces per card per hour. Returns 403 if owner has revoked details access.",
        "parameters": [
          {
            "$ref": "#/components/parameters/CardId"
          },
          {
            "name": "X-AGENT-NONCE",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Unique nonce (UUID v4) per request for anti-replay protection. Each nonce can only be used once."
          }
        ],
        "responses": {
          "200": {
            "description": "Sensitive card details",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GetCardDetailsResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/ErrorResponse"
          },
          "403": {
            "description": "Card details access revoked by owner",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/ErrorResponse"
          },
          "409": {
            "description": "Nonce already used (replay detected)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string",
                      "example": "Nonce already used (replay detected)"
                    },
                    "code": {
                      "type": "string",
                      "example": "REPLAY_REJECTED"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (max 5 reads per card per hour)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/cards/{cardId}/freeze": {
      "post": {
        "tags": [
          "wallet"
        ],
        "summary": "Freeze a card",
        "operationId": "freezeCard",
        "description": "Requires wallet signature authentication.",
        "parameters": [
          {
            "$ref": "#/components/parameters/CardId"
          }
        ],
        "responses": {
          "200": {
            "description": "Card frozen",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CardStatusMutationResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/ErrorResponse"
          },
          "404": {
            "$ref": "#/components/responses/ErrorResponse"
          }
        }
      }
    },
    "/cards/{cardId}/unfreeze": {
      "post": {
        "tags": [
          "wallet"
        ],
        "summary": "Unfreeze a card",
        "operationId": "unfreezeCard",
        "description": "Requires wallet signature authentication.",
        "parameters": [
          {
            "$ref": "#/components/parameters/CardId"
          }
        ],
        "responses": {
          "200": {
            "description": "Card unfrozen",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CardStatusMutationResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/ErrorResponse"
          },
          "404": {
            "$ref": "#/components/responses/ErrorResponse"
          }
        }
      }
    },
    "/portal/cards/{cardId}/revoke-details": {
      "post": {
        "tags": [
          "portal"
        ],
        "summary": "Revoke agent access to card details",
        "operationId": "revokeCardDetails",
        "description": "Owner action via Telegram portal. After revoking, GET /cards/{cardId}/details returns 403.",
        "parameters": [
          {
            "$ref": "#/components/parameters/CardId"
          }
        ],
        "responses": {
          "200": {
            "description": "Details access revoked"
          },
          "401": {
            "$ref": "#/components/responses/ErrorResponse"
          },
          "404": {
            "$ref": "#/components/responses/ErrorResponse"
          }
        }
      }
    },
    "/portal/cards/{cardId}/restore-details": {
      "post": {
        "tags": [
          "portal"
        ],
        "summary": "Restore agent access to card details",
        "operationId": "restoreCardDetails",
        "description": "Owner action via Telegram portal. After restoring, GET /cards/{cardId}/details returns 200 again.",
        "parameters": [
          {
            "$ref": "#/components/parameters/CardId"
          }
        ],
        "responses": {
          "200": {
            "description": "Details access restored"
          },
          "401": {
            "$ref": "#/components/responses/ErrorResponse"
          },
          "404": {
            "$ref": "#/components/responses/ErrorResponse"
          }
        }
      }
    },
    "/ops/nonce-cleanup": {
      "post": {
        "tags": [
          "ops"
        ],
        "summary": "Run nonce TTL cleanup",
        "operationId": "nonceCleanup",
        "description": "Batch-deletes expired agent nonces older than retention window. Secured by OPS_API_KEY.",
        "parameters": [
          {
            "name": "retention_hours",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 24
            }
          },
          {
            "name": "batch_limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 10000
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Cleanup result with deleted_count and latency_ms"
          },
          "401": {
            "$ref": "#/components/responses/ErrorResponse"
          }
        }
      }
    },
    "/stripe-beta/session": {
      "post": {
        "tags": ["stripe-beta"],
        "summary": "Create managed session",
        "operationId": "createStripeSession",
        "description": "Creates a managed session for Stripe MPP. Returns a session key for subsequent requests.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["email"],
                "properties": {
                  "email": { "type": "string", "format": "email" },
                  "nameOnCard": { "type": "string" },
                  "phone": { "type": "string" }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Session created with session key" },
          "403": { "description": "Email not in beta allowlist" }
        }
      }
    },
    "/stripe-beta/payment-requests": {
      "post": {
        "tags": ["stripe-beta"],
        "summary": "Create payment request",
        "operationId": "createPaymentRequest",
        "description": "Creates a payment request. Returns approval_required status with approvalUrl for owner.",
        "parameters": [
          { "name": "X-STRIPE-SESSION", "in": "header", "required": true, "schema": { "type": "string" } }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["amount"],
                "properties": {
                  "amount": { "type": "number", "minimum": 5, "maximum": 5000 },
                  "description": { "type": "string" }
                }
              }
            }
          }
        },
        "responses": {
          "201": { "description": "Payment request created (status: approval_required)" },
          "401": { "$ref": "#/components/responses/ErrorResponse" }
        }
      }
    },
    "/stripe-beta/payment-requests/{id}": {
      "get": {
        "tags": ["stripe-beta"],
        "summary": "Poll payment request status",
        "operationId": "getPaymentRequest",
        "description": "Returns current status of a payment request (pending, approved, completed, rejected, expired).",
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string" } },
          { "name": "X-STRIPE-SESSION", "in": "header", "required": true, "schema": { "type": "string" } }
        ],
        "responses": {
          "200": { "description": "Payment request status" },
          "404": { "$ref": "#/components/responses/ErrorResponse" }
        }
      }
    },
    "/stripe-beta/approve/{id}/complete": {
      "post": {
        "tags": ["stripe-beta"],
        "summary": "Complete payment (MPP credential)",
        "operationId": "completeStripePayment",
        "description": "Called after Stripe payment succeeds. Submits MPP credential and triggers card creation.",
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string" } }
        ],
        "responses": {
          "201": { "description": "Card created successfully" },
          "400": { "$ref": "#/components/responses/ErrorResponse" }
        }
      }
    }
  },
  "components": {
    "parameters": {
      "CardId": {
        "name": "cardId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      }
    },
    "responses": {
      "ErrorResponse": {
        "description": "Error response",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      }
    },
    "schemas": {
      "HealthResponse": {
        "type": "object",
        "required": [
          "status"
        ],
        "properties": {
          "status": {
            "type": "string",
            "example": "ok"
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          },
          "version": {
            "type": "string",
            "example": "0.5.0"
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string",
            "example": "Human-readable error message"
          }
        }
      },
      "PricingResponse": {
        "type": "object",
        "properties": {
          "cardFee": {
            "type": "number",
            "example": 10,
            "description": "One-time card creation fee in USD"
          },
          "topUpPercent": {
            "type": "number",
            "example": 3.5,
            "description": "Top-up fee percentage"
          },
          "minAmount": {
            "type": "number",
            "example": 5
          },
          "maxAmount": {
            "type": "number",
            "example": 5000
          },
          "description": {
            "type": "string",
            "example": "$10 flat card creation. Initial load optional. 3.5% on loads."
          }
        }
      },
      "CardTiersResponse": {
        "description": "Alias — same shape as PricingResponse",
        "$ref": "#/components/schemas/PricingResponse"
      },
      "CreateCardRequest": {
        "type": "object",
        "required": [
          "nameOnCard",
          "email"
        ],
        "properties": {
          "nameOnCard": {
            "type": "string",
            "minLength": 1,
            "example": "AI AGENT"
          },
          "email": {
            "type": "string",
            "format": "email",
            "example": "agent@example.com"
          }
        }
      },
      "PaymentInfo": {
        "type": "object",
        "properties": {
          "amountCharged": {
            "type": "number",
            "format": "float",
            "example": 20.35
          },
          "txHash": {
            "type": "string"
          },
          "network": {
            "type": "string",
            "example": "stellar"
          }
        }
      },
      "BillingAddress": {
        "type": "object",
        "properties": {
          "street": {
            "type": "string"
          },
          "city": {
            "type": "string"
          },
          "state": {
            "type": "string"
          },
          "zip": {
            "type": "string"
          },
          "country": {
            "type": "string"
          }
        }
      },
      "SensitiveCardDetails": {
        "type": "object",
        "properties": {
          "cardNumber": {
            "type": "string"
          },
          "expiryMonth": {
            "type": "integer"
          },
          "expiryYear": {
            "type": "integer"
          },
          "cvv": {
            "type": "string"
          },
          "billingAddress": {
            "$ref": "#/components/schemas/BillingAddress"
          }
        }
      },
      "CardSummary": {
        "type": "object",
        "properties": {
          "cardId": {
            "type": "string",
            "format": "uuid"
          },
          "nameOnCard": {
            "type": "string"
          },
          "balance": {
            "type": "number",
            "format": "float"
          },
          "status": {
            "type": "string"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CreateCardResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "card": {
            "$ref": "#/components/schemas/CardSummary"
          },
          "payment": {
            "$ref": "#/components/schemas/PaymentInfo"
          },
          "detailsEnvelope": {
            "type": "object",
            "description": "Sensitive card details with one-time access metadata (agent-first model)",
            "properties": {
              "cardNumber": {
                "type": "string"
              },
              "expiryMonth": {
                "type": "integer"
              },
              "expiryYear": {
                "type": "integer"
              },
              "cvv": {
                "type": "string"
              },
              "billingAddress": {
                "$ref": "#/components/schemas/BillingAddress"
              },
              "oneTimeAccess": {
                "type": "boolean",
                "description": "Indicates this envelope is only returned on creation"
              },
              "expiresInSeconds": {
                "type": "integer",
                "description": "Seconds before the caller should consider these details expired"
              },
              "note": {
                "type": "string"
              }
            }
          }
        }
      },
      "FundCardRequest": {
        "type": "object",
        "required": [
          "cardId"
        ],
        "properties": {
          "cardId": {
            "type": "string",
            "format": "uuid"
          }
        }
      },
      "FundCardResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "cardId": {
            "type": "string",
            "format": "uuid"
          },
          "fundedAmount": {
            "type": "number",
            "format": "float"
          },
          "newBalance": {
            "type": "number",
            "format": "float"
          },
          "payment": {
            "$ref": "#/components/schemas/PaymentInfo"
          }
        }
      },
      "ListCardsResponse": {
        "type": "object",
        "properties": {
          "cards": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "cardId": {
                  "type": "string",
                  "format": "uuid"
                },
                "nameOnCard": {
                  "type": "string"
                },
                "lastFour": {
                  "type": "string"
                },
                "balance": {
                  "type": "number",
                  "format": "float"
                },
                "status": {
                  "type": "string"
                },
                "createdAt": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            }
          }
        }
      },
      "GetCardResponse": {
        "type": "object",
        "properties": {
          "card": {
            "type": "object",
            "properties": {
              "cardId": {
                "type": "string",
                "format": "uuid"
              },
              "nameOnCard": {
                "type": "string"
              },
              "email": {
                "type": "string",
                "format": "email"
              },
              "balance": {
                "type": "number",
                "format": "float"
              },
              "initialAmountUsd": {
                "type": "number",
                "format": "float"
              },
              "status": {
                "type": "string"
              },
              "createdAt": {
                "type": "string",
                "format": "date-time"
              },
              "updatedAt": {
                "type": "string",
                "format": "date-time"
              }
            }
          }
        }
      },
      "GetCardDetailsResponse": {
        "type": "object",
        "properties": {
          "details": {
            "$ref": "#/components/schemas/SensitiveCardDetails"
          }
        }
      },
      "CardStatusMutationResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean"
          },
          "cardId": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "frozen"
            ]
          }
        }
      }
    }
  },
  "externalDocs": {
    "description": "ASG Card HTML docs",
    "url": "https://asgcard.dev/docs"
  }
}