{
  "openapi": "3.1.0",
  "info": {
    "title": "SendFax.sh API",
    "version": "1.0.0",
    "summary": "Send real faxes over HTTP, paid per page with x402 or MPP (USDC).",
    "description": "A single-purpose, account-free fax API. Agents authorize each send by paying per request with x402 or MPP (USDC) — there are no accounts and no API keys. Faxes are real-time: the PDF is streamed to the carrier and deleted after sending. Pricing is $0.05 per page. See https://sendfax.sh/llms.txt and https://sendfax.sh/quickstart.md.",
    "contact": { "name": "SendFax.sh support", "email": "support@sendfax.sh", "url": "https://sendfax.sh" },
    "license": { "name": "Terms at sendfax.sh", "url": "https://sendfax.sh" }
  },
  "servers": [{ "url": "https://sendfax.sh", "description": "Production" }],
  "externalDocs": {
    "description": "llms.txt index",
    "url": "https://sendfax.sh/llms.txt"
  },
  "tags": [
    { "name": "fax", "description": "Send a fax and poll its status." },
    { "name": "human", "description": "Endpoints backing the human /human + Stripe flow." }
  ],
  "paths": {
    "/api/v1/faxes": {
      "post": {
        "tags": ["fax"],
        "operationId": "sendFax",
        "summary": "Send a fax (agent, paid per page)",
        "description": "Submit a PDF and destination number. The first call returns HTTP 402 with payment requirements ($0.05/page, USDC) advertised for both x402 (PAYMENT-REQUIRED header + accepts body; retry with X-PAYMENT) and MPP (WWW-Authenticate: Payment header; retry with PAYMENT). Retrying the identical request with a valid payment header returns 202 with the fax id and status URL.",
        "security": [{ "x402": [] }, { "mpp": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": ["document", "to"],
                "properties": {
                  "document": {
                    "type": "string",
                    "format": "binary",
                    "description": "Unencrypted PDF, ≤20 MB."
                  },
                  "to": {
                    "type": "string",
                    "pattern": "^\\+[1-9]\\d{7,14}$",
                    "description": "Destination fax number in E.164 form.",
                    "example": "+14155550123"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Payment accepted; fax queued for delivery.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/FaxAccepted" }
              }
            }
          },
          "402": {
            "description": "Payment required. Pay the advertised amount and retry with the payment header.",
            "headers": {
              "WWW-Authenticate": {
                "description": "MPP challenge — the value is `Payment`.",
                "schema": { "type": "string", "example": "Payment" }
              },
              "PAYMENT-REQUIRED": {
                "description": "x402 payment requirements (encoded).",
                "schema": { "type": "string" }
              }
            },
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/PaymentRequired" }
              }
            }
          },
          "400": {
            "description": "Missing/invalid `document` or `to`, or an invalid/encrypted PDF.",
            "content": {
              "application/problem+json": {
                "schema": { "$ref": "#/components/schemas/Problem" }
              }
            }
          },
          "413": {
            "description": "The PDF exceeds the 20 MB limit.",
            "content": {
              "application/problem+json": {
                "schema": { "$ref": "#/components/schemas/Problem" }
              }
            }
          }
        }
      }
    },
    "/api/v1/faxes/{id}": {
      "get": {
        "tags": ["fax"],
        "operationId": "getFax",
        "summary": "Get fax status (free)",
        "description": "Poll the public status of a fax. No payment or auth required; the id is an unguessable capability. Shared by agents and the human status page.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "The fax id returned when the fax was accepted.",
            "schema": { "type": "string", "example": "fax_9f3c2a" }
          }
        ],
        "responses": {
          "200": {
            "description": "The public view of the fax.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/FaxPublic" }
              }
            }
          },
          "404": {
            "description": "No fax with that id.",
            "content": {
              "application/problem+json": {
                "schema": { "$ref": "#/components/schemas/Problem" }
              }
            }
          }
        }
      }
    },
    "/api/v1/uploads": {
      "post": {
        "tags": ["human"],
        "operationId": "createUpload",
        "summary": "Upload a PDF for the human flow (unpaid)",
        "description": "Backs the /human page. Validates and page-counts the PDF (which is never stored) and returns the Stripe price in cents. Not part of the agent flow.",
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": ["document", "to"],
                "properties": {
                  "document": { "type": "string", "format": "binary", "description": "Unencrypted PDF, ≤20 MB." },
                  "to": { "type": "string", "pattern": "^\\+[1-9]\\d{7,14}$", "example": "+14155550123" }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Upload accepted; price computed.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["id", "pageCount", "amountCents"],
                  "properties": {
                    "id": { "type": "string", "example": "fax_9f3c2a" },
                    "pageCount": { "type": "integer", "minimum": 1, "example": 3 },
                    "amountCents": { "type": "integer", "minimum": 50, "description": "Stripe charge in cents (min 50).", "example": 50 }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing/invalid `document` or `to`, or an invalid/encrypted PDF.",
            "content": { "application/problem+json": { "schema": { "$ref": "#/components/schemas/Problem" } } }
          },
          "413": {
            "description": "The PDF exceeds the 20 MB limit.",
            "content": { "application/problem+json": { "schema": { "$ref": "#/components/schemas/Problem" } } }
          }
        }
      }
    },
    "/api/v1/checkout": {
      "post": {
        "tags": ["human"],
        "operationId": "createCheckout",
        "summary": "Create a Stripe Checkout session (human flow)",
        "description": "Backs the /human page's Pay & Send button. Creates a Stripe Checkout session (Link + card + crypto) for a prior upload and returns its hosted URL. Not part of the agent flow.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["id"],
                "properties": { "id": { "type": "string", "example": "fax_9f3c2a" } }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Checkout session created.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["url"],
                  "properties": { "url": { "type": "string", "format": "uri", "description": "Hosted Stripe Checkout URL." } }
                }
              }
            }
          },
          "404": {
            "description": "No pending fax with that id.",
            "content": { "application/problem+json": { "schema": { "$ref": "#/components/schemas/Problem" } } }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "x402": {
        "type": "http",
        "scheme": "PAYMENT",
        "description": "Per-request payment via x402. On 402, pay the advertised USDC amount and retry with the X-PAYMENT header. See https://x402.org."
      },
      "mpp": {
        "type": "http",
        "scheme": "Payment",
        "description": "Per-request payment via MPP (Machine Payment Protocol). On 402 (WWW-Authenticate: Payment), pay and retry with the PAYMENT header. See https://mpp.dev."
      }
    },
    "schemas": {
      "FaxAccepted": {
        "type": "object",
        "required": ["id", "statusUrl"],
        "properties": {
          "id": { "type": "string", "example": "fax_9f3c2a" },
          "statusUrl": { "type": "string", "format": "uri", "example": "https://sendfax.sh/f/fax_9f3c2a" }
        }
      },
      "FaxPublic": {
        "type": "object",
        "description": "Public view of a fax; excludes the raw destination number and all carrier internals.",
        "required": ["id", "status", "pageCount", "toMasked", "failureReason", "createdAt", "terminalAt"],
        "properties": {
          "id": { "type": "string", "example": "fax_9f3c2a" },
          "status": { "$ref": "#/components/schemas/FaxStatus" },
          "pageCount": { "type": "integer", "minimum": 1, "example": 1 },
          "toMasked": { "type": "string", "description": "Destination redacted to last 4 digits.", "example": "•••• 0123" },
          "failureReason": { "type": ["string", "null"], "description": "Set when status = failed." },
          "createdAt": { "type": "integer", "description": "Epoch milliseconds.", "example": 1752096000000 },
          "terminalAt": { "type": ["integer", "null"], "description": "Epoch ms at terminal state; null until then." }
        }
      },
      "FaxStatus": {
        "type": "string",
        "enum": [
          "awaiting_payment",
          "paid",
          "queued",
          "media_processed",
          "sending",
          "delivered",
          "failed",
          "expired"
        ],
        "description": "Lifecycle. Terminal: delivered, failed, expired."
      },
      "PaymentRequired": {
        "type": "object",
        "description": "x402-style payment requirements returned with HTTP 402.",
        "properties": {
          "x402Version": { "type": "integer", "example": 1 },
          "error": { "type": "string", "example": "payment required" },
          "accepts": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "scheme": { "type": "string", "example": "exact" },
                "network": { "type": "string", "example": "base" },
                "asset": { "type": "string", "example": "USDC" },
                "maxAmountRequired": { "type": "string", "description": "USDC base units (6 decimals) × page count.", "example": "50000" },
                "resource": { "type": "string", "format": "uri", "example": "https://sendfax.sh/api/v1/faxes" },
                "description": { "type": "string", "example": "Fax transmission — $0.05/page" },
                "mimeType": { "type": "string", "example": "application/json" }
              }
            }
          }
        }
      },
      "Problem": {
        "type": "object",
        "description": "RFC 9457 problem details.",
        "properties": {
          "type": { "type": "string", "format": "uri", "example": "about:blank" },
          "title": { "type": "string", "example": "Invalid destination" },
          "status": { "type": "integer", "example": 400 },
          "detail": { "type": "string", "example": "Field `to` must be an E.164 fax number, e.g. +14155550123." }
        }
      }
    }
  }
}
