{
  "openapi": "3.1.0",
  "info": {
    "title": "GiftPulse API",
    "description": "Send a REAL mobile airtime top-up or order a REAL digital gift card, worldwide, via Reloadly. This is an ACTION API, not a data lookup: paying causes airtime to be credited or a gift card to be issued. Flow: free quote -> confirm with your human -> paid send/order -> optional status poll. Pay-per-call via x402 (USDC on Base). No API keys, no accounts, no subscriptions.",
    "version": "1.0.0",
    "contact": {
      "name": "The Aslan Group LLC",
      "url": "https://pulsenetwork.theaslangroupllc.com",
      "email": "info@theaslangroupllc.com"
    },
    "x-guidance": "# GiftPulse API (x402 pay-per-call)\n\nSend a REAL mobile airtime top-up or order a REAL digital gift card, worldwide, via Reloadly. ACTION API, not a data lookup: paying causes real value to move. Flow: free quote (confirm phone number / recipient with your human) -> paid send/order -> optional status/redemption poll. Pay-per-call via x402 on Base.\n\nBase URL: https://giftpulse.theaslangroupllc.com\n\n## Payment\nAll paid endpoints use the x402 protocol (HTTP 402): USDC on Base mainnet (eip155:8453). No API keys, no accounts, no subscriptions — pay per call.\n\n## Agent workflow\n1. Request the endpoint. Unpaid requests receive a 402 challenge with exact price and pay-to details.\n2. Settle with any x402 client (agentcash fetch, x402-fetch, x402-axios) and retry with the X-PAYMENT header.\n3. Responses are JSON and always include a `mode` field (\"sandbox\" or \"live\").\n\n## Paid endpoints\n- GET /api/topup/lookup — $0.01 — Auto-detect mobile operator + live amount range/discount for a phone number.\n- POST /api/topup — dynamic (amount_usd + $0.50) — THE action: sends real airtime once payment settles.\n- GET /api/topup/{id} — $0.005 — Delivery status/reconciliation poll.\n- GET /api/giftcard/catalog — $0.01 — Search 300+ gift card brands / 14,000+ products.\n- POST /api/giftcard — dynamic (unit_price x quantity) — THE action: orders a real gift card once payment settles.\n- GET /api/giftcard/{id} — $0.005 — Order status + redemption code re-fetch (gated by client_request_id).\n\nMore APIs from this provider: https://pulsenetwork.theaslangroupllc.com (grouped catalog, all x402)."
  },
  "servers": [
    { "url": "https://giftpulse.theaslangroupllc.com" }
  ],
  "components": {
    "securitySchemes": {
      "x402": {
        "type": "apiKey",
        "in": "header",
        "name": "PAYMENT-SIGNATURE",
        "description": "x402 micropayment signature. Omit to receive 402 with payment requirements. Sign and retry to receive data."
      }
    }
  },
  "paths": {
    "/api/health": {
      "get": {
        "summary": "Liveness + current mode",
        "description": "Free liveness check. Reports whether the service is currently in sandbox or live Reloadly mode.",
        "operationId": "health",
        "responses": { "200": { "description": "Service is up" } }
      }
    },
    "/api/topup/lookup": {
      "get": {
        "summary": "Auto-detect mobile operator + live amount range/discount",
        "description": "Auto-detects the mobile carrier for a phone number via Reloadly and returns its live top-up amount range/denominations and current discount, before quoting or sending airtime.",
        "operationId": "topupLookup",
        "security": [ { "x402": [] } ],
        "parameters": [
          { "name": "phone", "in": "query", "required": true, "schema": { "type": "string" }, "description": "Full E.164 phone number, e.g. +2348021234567" },
          { "name": "country_iso", "in": "query", "required": false, "schema": { "type": "string" }, "description": "ISO-2 override for shared calling codes (e.g. NANP +1)" }
        ],
        "responses": {
          "200": { "description": "Resolved operator + amount range/discount" },
          "402": { "description": "Payment required" },
          "404": { "description": "Operator could not be auto-detected" }
        },
        "x-price-usd": 0.01,
        "x-agent-use-case": "on-demand",
        "x-agent-chaining": "Run before POST /api/topup/quote (which also auto-detects internally) to preview the operator before quoting.",
        "x-payment-info": {
          "price": { "mode": "fixed", "currency": "USD", "amount": "0.010000" },
          "protocols": [ { "x402": {} } ]
        }
      }
    },
    "/api/topup/quote": {
      "post": {
        "summary": "Free signed price quote for an airtime top-up",
        "description": "FREE — no x402 gate. Auto-detects the operator (or validates operator_id if given), validates amount_usd against the operator's min/max/fixed set, and returns a signed quote_id, the exact total price (amount_usd + $0.50 processing fee), and a confirm_notice echoing the full phone number and operator name for human confirmation.",
        "operationId": "quoteTopup",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["phone", "amount_usd"],
                "properties": {
                  "phone": { "type": "string", "description": "Full E.164 phone number" },
                  "country_iso": { "type": "string", "description": "ISO-2 country, optional if operator_id given or auto-resolvable from phone" },
                  "operator_id": { "type": "integer", "description": "Optional — from GET /api/topup/lookup. Omit to auto-detect." },
                  "amount_usd": { "type": "number", "description": "Airtime face value in USD, before the processing fee" }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Signed quote: quote_id, issued_at, expires_at, price_usd, amount_micro, operator, confirm_notice" },
          "400": { "description": "Invalid phone/operator/amount, or amount outside the operator's allowed range" }
        }
      }
    },
    "/api/topup": {
      "post": {
        "summary": "Send a real mobile airtime top-up (THE paid action)",
        "description": "Credits real airtime to a phone number via Reloadly once payment settles. Requires a quote_id from POST /api/topup/quote (recomputed and verified server-side) and a client_request_id (8-128 chars) for idempotency — also passed to Reloadly as customIdentifier, so even a race cannot double-send. Price = the quoted amount_usd + $0.50 processing fee. Every order capped at $25.00 total.",
        "operationId": "sendTopup",
        "security": [ { "x402": [] } ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["quote_id", "issued_at", "amount_micro", "phone", "country_iso", "operator_id", "amount_usd", "client_request_id"],
                "properties": {
                  "quote_id": { "type": "string", "description": "From POST /api/topup/quote" },
                  "issued_at": { "type": "string", "description": "From POST /api/topup/quote, copied verbatim" },
                  "amount_micro": { "type": "string", "description": "From POST /api/topup/quote, copied verbatim" },
                  "phone": { "type": "string", "description": "Full E.164 — must match the quote exactly" },
                  "country_iso": { "type": "string", "description": "Must match the quote exactly" },
                  "operator_id": { "type": "integer", "description": "From the quote response — must match exactly" },
                  "amount_usd": { "type": "number", "description": "Must match the quote exactly" },
                  "client_request_id": { "type": "string", "minLength": 8, "maxLength": 128, "description": "Unique per top-up. Enables safe retries." }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Reloadly transaction_id, status, delivered_amount, delivered_currency, price_charged_usd, mode" },
          "400": { "description": "Invalid, expired, tampered, or over-cap order — rejected before payment" },
          "402": { "description": "Payment required" },
          "409": { "description": "client_request_id reused for a different order, or a race with a concurrent identical retry" }
        },
        "x-price-usd": 1.5,
        "x-price-usd-note": "Price = the caller's quoted amount_usd + $0.50 flat processing fee. The actual x402 402 challenge amount always matches the caller's quote_id-verified order; $1.50 shown here (a representative $1 top-up + fee) is for discovery purposes only.",
        "x-agent-use-case": "action",
        "x-agent-chaining": "Always call POST /api/topup/quote first (free) and confirm the phone number/operator with your human. Poll GET /api/topup/{id} afterward to reconcile delivery.",
        "x-payment-info": {
          "price": { "mode": "fixed", "currency": "USD", "amount": "1.500000" },
          "protocols": [ { "x402": {} } ]
        }
      }
    },
    "/api/topup/{id}": {
      "get": {
        "summary": "Airtime top-up delivery status / reconciliation",
        "description": "Cheap status/reconciliation poll for a previously sent top-up. Always reconciles directly against Reloadly — never trust POST /api/topup's synchronous response alone for anything acted on downstream.",
        "operationId": "getTopupStatus",
        "security": [ { "x402": [] } ],
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^\\d+$" }, "description": "Reloadly transaction_id returned by POST /api/topup" }
        ],
        "responses": {
          "200": { "description": "transaction_id, status, delivered_amount, delivered_currency" },
          "402": { "description": "Payment required" },
          "404": { "description": "No transaction with that id in the current mode (sandbox/live are separate namespaces)" }
        },
        "x-price-usd": 0.005,
        "x-agent-use-case": "on-demand",
        "x-payment-info": {
          "price": { "mode": "fixed", "currency": "USD", "amount": "0.005000" },
          "protocols": [ { "x402": {} } ]
        }
      }
    },
    "/api/giftcard/catalog": {
      "get": {
        "summary": "Search the gift card product catalog",
        "description": "Searches Reloadly's gift card catalog (300+ brands, 14,000+ products/denominations across countries). Returns each product's id, brand, country, denomination range or fixed amounts, and sender fee/discount.",
        "operationId": "giftcardCatalog",
        "security": [ { "x402": [] } ],
        "parameters": [
          { "name": "query", "in": "query", "required": false, "schema": { "type": "string" }, "description": "Brand/product name search, e.g. Amazon" },
          { "name": "country", "in": "query", "required": false, "schema": { "type": "string" }, "description": "ISO-2 country filter" },
          { "name": "page", "in": "query", "required": false, "schema": { "type": "integer" }, "description": "Zero-indexed page, default 0" },
          { "name": "size", "in": "query", "required": false, "schema": { "type": "integer" }, "description": "Results per page, default 20, max 50" }
        ],
        "responses": {
          "200": { "description": "Paginated product list with denominations and fees" },
          "402": { "description": "Payment required" }
        },
        "x-price-usd": 0.01,
        "x-agent-use-case": "on-demand",
        "x-agent-chaining": "Run before POST /api/giftcard/quote to find a valid product_id.",
        "x-payment-info": {
          "price": { "mode": "fixed", "currency": "USD", "amount": "0.010000" },
          "protocols": [ { "x402": {} } ]
        }
      }
    },
    "/api/giftcard/quote": {
      "post": {
        "summary": "Free signed price quote for a gift card order",
        "description": "FREE — no x402 gate. Validates the product and unit_price against Reloadly's real catalog and returns a signed quote_id, the exact total price (unit_price x quantity, no additional fee in v1), and a delivery_notice explaining whether the redemption code will be emailed or returned directly.",
        "operationId": "quoteGiftcard",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["product_id", "unit_price"],
                "properties": {
                  "product_id": { "type": "integer", "description": "From GET /api/giftcard/catalog" },
                  "unit_price": { "type": "number", "description": "Must match one of the product's allowed denominations" },
                  "quantity": { "type": "integer", "minimum": 1, "maximum": 10, "default": 1 },
                  "recipient_email": { "type": "string", "description": "Optional — if omitted, the redemption code is returned directly at order time instead" }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Signed quote: quote_id, issued_at, expires_at, price_usd, amount_micro, product, delivery_notice" },
          "400": { "description": "Invalid product_id, unit_price outside allowed denominations, or invalid recipient_email" },
          "404": { "description": "product_id not found" }
        }
      }
    },
    "/api/giftcard": {
      "post": {
        "summary": "Order a real digital gift card (THE paid action)",
        "description": "Issues a real gift card via Reloadly once payment settles, either emailed to recipient_email or returned as a redemption code directly in this response. Requires a quote_id from POST /api/giftcard/quote (recomputed and verified server-side) and a client_request_id (8-128 chars) for idempotency — also passed to Reloadly as customIdentifier. Price = unit_price x quantity. Every order capped at $25.00 total.",
        "operationId": "orderGiftcard",
        "security": [ { "x402": [] } ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["quote_id", "issued_at", "amount_micro", "product_id", "unit_price", "client_request_id"],
                "properties": {
                  "quote_id": { "type": "string", "description": "From POST /api/giftcard/quote" },
                  "issued_at": { "type": "string", "description": "From POST /api/giftcard/quote, copied verbatim" },
                  "amount_micro": { "type": "string", "description": "From POST /api/giftcard/quote, copied verbatim" },
                  "product_id": { "type": "integer", "description": "Must match the quote exactly" },
                  "unit_price": { "type": "number", "description": "Must match the quote exactly" },
                  "quantity": { "type": "integer", "minimum": 1, "maximum": 10, "default": 1, "description": "Must match the quote exactly" },
                  "recipient_email": { "type": "string", "description": "Must match the quote exactly" },
                  "client_request_id": { "type": "string", "minLength": 8, "maxLength": 128, "description": "Unique per order. Enables safe retries." }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Reloadly transaction_id, status, delivery mode, redemption_code (if not emailed), price_charged_usd, mode" },
          "400": { "description": "Invalid, expired, tampered, or over-cap order — rejected before payment" },
          "402": { "description": "Payment required" },
          "409": { "description": "client_request_id reused for a different order, or a race with a concurrent identical retry" }
        },
        "x-price-usd": 1,
        "x-price-usd-note": "Price = the caller's quoted unit_price x quantity. The actual x402 402 challenge amount always matches the caller's quote_id-verified order; $1.00 shown here (a representative single low-denomination card) is for discovery purposes only.",
        "x-agent-use-case": "action",
        "x-agent-chaining": "Always call GET /api/giftcard/catalog then POST /api/giftcard/quote first (free). Poll GET /api/giftcard/{id} afterward to reconcile or re-fetch the code.",
        "x-payment-info": {
          "price": { "mode": "fixed", "currency": "USD", "amount": "1.000000" },
          "protocols": [ { "x402": {} } ]
        }
      }
    },
    "/api/giftcard/{id}": {
      "get": {
        "summary": "Gift card order status / redemption code re-fetch",
        "description": "Cheap status poll and redemption-code re-fetch for a previously placed gift card order. Requires the client_request_id used at purchase as a query parameter — this gates access (only the original buyer, who knows that id, can re-fetch the code).",
        "operationId": "getGiftcardStatus",
        "security": [ { "x402": [] } ],
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string", "pattern": "^\\d+$" }, "description": "Reloadly transaction_id returned by POST /api/giftcard" },
          { "name": "client_request_id", "in": "query", "required": true, "schema": { "type": "string" }, "description": "The client_request_id used in the original POST /api/giftcard call" }
        ],
        "responses": {
          "200": { "description": "transaction_id, status, product, redemption_code" },
          "402": { "description": "Payment required" },
          "403": { "description": "client_request_id does not match the buyer of record for this transaction" },
          "404": { "description": "No transaction with that id in the current mode" }
        },
        "x-price-usd": 0.005,
        "x-agent-use-case": "on-demand",
        "x-payment-info": {
          "price": { "mode": "fixed", "currency": "USD", "amount": "0.005000" },
          "protocols": [ { "x402": {} } ]
        }
      }
    }
  }
}
