{
  "openapi": "3.1.0",
  "info": {
    "title": "paperfeel.ing",
    "version": "0.1.0",
    "description": "Account-free scan jobs. Timestamps are Unix milliseconds; EUR amounts are integer cents; USDC amounts are atomic 6-decimal units. No status or download endpoint charges. Provider methods only advertised when configured and purchases enabled."
  },
  "servers": [
    {
      "url": "/"
    }
  ],
  "paths": {
    "/v1/capabilities": {
      "get": {
        "summary": "Discover configured payment methods, supported billing countries and limits",
        "operationId": "get_v1_capabilities",
        "security": [],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Capabilities"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request or missing idempotency key"
          },
          "402": {
            "description": "Payment required or SDK payment challenge"
          },
          "403": {
            "description": "Origin check failed"
          },
          "404": {
            "description": "Job unavailable or capability invalid"
          },
          "408": {
            "description": "Upload interrupted or deadline exceeded; retry identical bytes"
          },
          "409": {
            "description": "Not ready, expired quote, unresolved purchase, or conflicting idempotency key"
          },
          "410": {
            "description": "Expired or deleted job"
          },
          "413": {
            "description": "Request or PDF too large"
          },
          "422": {
            "description": "Invalid PDF or unsupported billing country"
          },
          "429": {
            "description": "Rate limited; respect Retry-After"
          },
          "503": {
            "description": "Upload capacity busy (respect Retry-After), provider unavailable, purchases disabled, or settlement recording pending; poll before retrying a payment"
          }
        }
      }
    },
    "/v1/feedback": {
      "post": {
        "summary": "Submit feedback or a feature request; never charges",
        "operationId": "post_v1_feedback",
        "security": [],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 32,
              "maxLength": 128,
              "pattern": "^[a-zA-Z0-9_-]+$"
            },
            "description": "Stable random key per operation. Creation requires at least 32 characters. Reuse with a different operation returns 409."
          },
          {
            "name": "X-Job-Token",
            "in": "header",
            "schema": {
              "type": "string"
            },
            "description": "Required only when jobId is provided."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "category": {
                    "enum": [
                      "bug",
                      "missing_feature",
                      "feature_request",
                      "documentation"
                    ]
                  },
                  "summary": {
                    "type": "string",
                    "minLength": 5,
                    "maxLength": 160
                  },
                  "details": {
                    "type": "string",
                    "minLength": 10,
                    "maxLength": 4000
                  },
                  "contact": {
                    "type": "string",
                    "minLength": 3,
                    "maxLength": 300,
                    "description": "Optional reply address or handle with platform. Providing it permits a reply about this request, not marketing. Do not include secrets."
                  },
                  "paymentMethod": {
                    "enum": [
                      "checkout",
                      "mpp",
                      "x402"
                    ]
                  },
                  "httpStatus": {
                    "type": "integer",
                    "minimum": 400,
                    "maximum": 599
                  },
                  "operation": {
                    "type": "string",
                    "maxLength": 100
                  },
                  "errorCode": {
                    "type": "string",
                    "pattern": "^[a-z0-9_]{1,80}$"
                  },
                  "jobId": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Optional accessible job; requires its X-Job-Token. Only renderer version/error diagnostics are retained, not the job ID or document."
                  }
                },
                "required": [
                  "category",
                  "summary",
                  "details"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "status": {
                      "const": "received"
                    },
                    "expiresAt": {
                      "type": "integer"
                    }
                  },
                  "required": [
                    "id",
                    "status",
                    "expiresAt"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "Malformed request or missing idempotency key"
          },
          "402": {
            "description": "Payment required or SDK payment challenge"
          },
          "403": {
            "description": "Origin check failed"
          },
          "404": {
            "description": "Job unavailable or capability invalid"
          },
          "408": {
            "description": "Upload interrupted or deadline exceeded; retry identical bytes"
          },
          "409": {
            "description": "Not ready, expired quote, unresolved purchase, or conflicting idempotency key"
          },
          "410": {
            "description": "Expired or deleted job"
          },
          "413": {
            "description": "Request or PDF too large"
          },
          "422": {
            "description": "Invalid PDF or unsupported billing country"
          },
          "429": {
            "description": "Rate limited; respect Retry-After"
          },
          "503": {
            "description": "Upload capacity busy (respect Retry-After), provider unavailable, purchases disabled, or settlement recording pending; poll before retrying a payment"
          }
        },
        "description": "No account, job, purchase, or job token is required unless attaching jobId. Payment failures can be reported before uploading a document; omit jobId if access is unavailable. Optional paymentMethod/httpStatus plus operation/errorCode describe the failed call without credentials. Check capabilities.feedback.enabled. JSON body at most 16 KiB. Identical retries with the same key return the same receipt; different content returns 409. Feedback/contact expire after 90 days. Received is not an implementation promise. Exclude document contents, filenames and payment credentials. Limits: 10 new submissions per source IP/day and 200 globally/hour; respect Retry-After on 429. No public feedback-reading endpoint."
      }
    },
    "/v1/jobs": {
      "post": {
        "summary": "Create a private document job",
        "operationId": "post_v1_jobs",
        "security": [],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 32,
              "maxLength": 128,
              "pattern": "^[a-zA-Z0-9_-]+$"
            },
            "description": "Save this secret random creation key before sending; retries recover the same job and token while the job exists."
          }
        ],
        "responses": {
          "201": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "job": {
                      "$ref": "#/components/schemas/Job"
                    },
                    "token": {
                      "type": "string"
                    },
                    "uploadUrl": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "job",
                    "token",
                    "uploadUrl"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "Malformed request or missing idempotency key"
          },
          "402": {
            "description": "Payment required or SDK payment challenge"
          },
          "403": {
            "description": "Origin check failed"
          },
          "404": {
            "description": "Job unavailable or capability invalid"
          },
          "408": {
            "description": "Upload interrupted or deadline exceeded; retry identical bytes"
          },
          "409": {
            "description": "Not ready, expired quote, unresolved purchase, or conflicting idempotency key"
          },
          "410": {
            "description": "Expired or deleted job"
          },
          "413": {
            "description": "Request or PDF too large"
          },
          "422": {
            "description": "Invalid PDF or unsupported billing country"
          },
          "429": {
            "description": "Rate limited; respect Retry-After"
          },
          "503": {
            "description": "Upload capacity busy (respect Retry-After), provider unavailable, purchases disabled, or settlement recording pending; poll before retrying a payment"
          }
        }
      }
    },
    "/v1/jobs/{id}": {
      "get": {
        "summary": "Poll without payment",
        "operationId": "get_v1_jobs__id_",
        "security": [
          {
            "JobToken": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request or missing idempotency key"
          },
          "402": {
            "description": "Payment required or SDK payment challenge"
          },
          "403": {
            "description": "Origin check failed"
          },
          "404": {
            "description": "Job unavailable or capability invalid"
          },
          "408": {
            "description": "Upload interrupted or deadline exceeded; retry identical bytes"
          },
          "409": {
            "description": "Not ready, expired quote, unresolved purchase, or conflicting idempotency key"
          },
          "410": {
            "description": "Expired or deleted job"
          },
          "413": {
            "description": "Request or PDF too large"
          },
          "422": {
            "description": "Invalid PDF or unsupported billing country"
          },
          "429": {
            "description": "Rate limited; respect Retry-After"
          },
          "503": {
            "description": "Upload capacity busy (respect Retry-After), provider unavailable, purchases disabled, or settlement recording pending; poll before retrying a payment"
          }
        }
      },
      "delete": {
        "summary": "Revoke access and delete files",
        "operationId": "delete_v1_jobs__id_",
        "security": [
          {
            "JobToken": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deleted": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "deleted"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "Malformed request or missing idempotency key"
          },
          "402": {
            "description": "Payment required or SDK payment challenge"
          },
          "403": {
            "description": "Origin check failed"
          },
          "404": {
            "description": "Job unavailable or capability invalid"
          },
          "408": {
            "description": "Upload interrupted or deadline exceeded; retry identical bytes"
          },
          "409": {
            "description": "Not ready, expired quote, unresolved purchase, or conflicting idempotency key"
          },
          "410": {
            "description": "Expired or deleted job"
          },
          "413": {
            "description": "Request or PDF too large"
          },
          "422": {
            "description": "Invalid PDF or unsupported billing country"
          },
          "429": {
            "description": "Rate limited; respect Retry-After"
          },
          "503": {
            "description": "Upload capacity busy (respect Retry-After), provider unavailable, purchases disabled, or settlement recording pending; poll before retrying a payment"
          }
        }
      }
    },
    "/v1/jobs/{id}/upload": {
      "post": {
        "summary": "Upload immutable PDF bytes; enqueue first-page sample",
        "operationId": "post_v1_jobs__id__upload",
        "security": [
          {
            "JobToken": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/pdf": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request or missing idempotency key"
          },
          "402": {
            "description": "Payment required or SDK payment challenge"
          },
          "403": {
            "description": "Origin check failed"
          },
          "404": {
            "description": "Job unavailable or capability invalid"
          },
          "408": {
            "description": "Upload interrupted or deadline exceeded; retry identical bytes"
          },
          "409": {
            "description": "Not ready, expired quote, unresolved purchase, or conflicting idempotency key"
          },
          "410": {
            "description": "Expired or deleted job"
          },
          "413": {
            "description": "Request or PDF too large"
          },
          "422": {
            "description": "Invalid PDF or unsupported billing country"
          },
          "429": {
            "description": "Rate limited; respect Retry-After"
          },
          "503": {
            "description": "Upload capacity busy (respect Retry-After), provider unavailable, purchases disabled, or settlement recording pending; poll before retrying a payment"
          }
        }
      }
    },
    "/v1/jobs/{id}/complete-upload": {
      "post": {
        "summary": "Recover lost upload response or queue dispatch",
        "operationId": "post_v1_jobs__id__complete_upload",
        "security": [
          {
            "JobToken": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "202": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request or missing idempotency key"
          },
          "402": {
            "description": "Payment required or SDK payment challenge"
          },
          "403": {
            "description": "Origin check failed"
          },
          "404": {
            "description": "Job unavailable or capability invalid"
          },
          "408": {
            "description": "Upload interrupted or deadline exceeded; retry identical bytes"
          },
          "409": {
            "description": "Not ready, expired quote, unresolved purchase, or conflicting idempotency key"
          },
          "410": {
            "description": "Expired or deleted job"
          },
          "413": {
            "description": "Request or PDF too large"
          },
          "422": {
            "description": "Invalid PDF or unsupported billing country"
          },
          "429": {
            "description": "Rate limited; respect Retry-After"
          },
          "503": {
            "description": "Upload capacity busy (respect Retry-After), provider unavailable, purchases disabled, or settlement recording pending; poll before retrying a payment"
          }
        }
      }
    },
    "/v1/jobs/{id}/quote": {
      "post": {
        "summary": "Quote the frozen base/fee amount; tax is fixed in manual mode or confirmed inside Stripe Checkout",
        "operationId": "post_v1_jobs__id__quote",
        "security": [
          {
            "JobToken": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "country": {
                    "type": "string",
                    "pattern": "^[A-Z]{2}$",
                    "description": "Required when capabilities.taxMode is manual, including MPP and x402. Omit in stripe_checkout mode; Stripe collects billing location and confirms final tax before payment. Supply the buyer’s actual billing country as an uppercase two-letter code from capabilities.countries. If unknown, ask the buyer before requesting a quote. This is a paperfeel.ing pricing requirement, not a payment-protocol requirement. Never default to the merchant country."
                  },
                  "stablecoin": {
                    "type": "boolean",
                    "description": "Set true for an x402 USDC quote. The billing country is still required to calculate tax before currency conversion."
                  }
                },
                "required": [],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Quote"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request or missing idempotency key"
          },
          "402": {
            "description": "Payment required or SDK payment challenge"
          },
          "403": {
            "description": "Origin check failed"
          },
          "404": {
            "description": "Job unavailable or capability invalid"
          },
          "408": {
            "description": "Upload interrupted or deadline exceeded; retry identical bytes"
          },
          "409": {
            "description": "Not ready, expired quote, unresolved purchase, or conflicting idempotency key"
          },
          "410": {
            "description": "Expired or deleted job"
          },
          "413": {
            "description": "Request or PDF too large"
          },
          "422": {
            "description": "Invalid PDF or unsupported billing country"
          },
          "429": {
            "description": "Rate limited; respect Retry-After"
          },
          "503": {
            "description": "Upload capacity busy (respect Retry-After), provider unavailable, purchases disabled, or settlement recording pending; poll before retrying a payment"
          }
        }
      }
    },
    "/v1/jobs/{id}/checkout": {
      "post": {
        "summary": "Create or recover Stripe Checkout",
        "operationId": "post_v1_jobs__id__checkout",
        "security": [
          {
            "JobToken": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 16,
              "maxLength": 128,
              "pattern": "^[a-zA-Z0-9_-]+$"
            },
            "description": "Stable random key per operation. Creation requires at least 32 characters. Reuse with a different operation returns 409."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "quoteId": {
                    "type": "string",
                    "format": "uuid"
                  }
                },
                "required": [
                  "quoteId"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "url": {
                      "type": "string",
                      "format": "uri"
                    }
                  },
                  "required": [
                    "url"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "Malformed request or missing idempotency key"
          },
          "402": {
            "description": "Payment required or SDK payment challenge"
          },
          "403": {
            "description": "Origin check failed"
          },
          "404": {
            "description": "Job unavailable or capability invalid"
          },
          "408": {
            "description": "Upload interrupted or deadline exceeded; retry identical bytes"
          },
          "409": {
            "description": "Not ready, expired quote, unresolved purchase, or conflicting idempotency key"
          },
          "410": {
            "description": "Expired or deleted job"
          },
          "413": {
            "description": "Request or PDF too large"
          },
          "422": {
            "description": "Invalid PDF or unsupported billing country"
          },
          "429": {
            "description": "Rate limited; respect Retry-After"
          },
          "503": {
            "description": "Upload capacity busy (respect Retry-After), provider unavailable, purchases disabled, or settlement recording pending; poll before retrying a payment"
          }
        }
      }
    },
    "/v1/jobs/{id}/purchase/mpp": {
      "post": {
        "summary": "Optional mpp purchase (disabled by default); requires agenticPaymentsEnabled and advertised protocol. Retry with same quote and proof",
        "operationId": "post_v1_jobs__id__purchase_mpp",
        "security": [
          {
            "JobToken": []
          },
          {
            "JobToken": [],
            "MppPayment": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 16,
              "maxLength": 128,
              "pattern": "^[a-zA-Z0-9_-]+$"
            },
            "description": "Stable random key per operation. Creation requires at least 32 characters. Reuse with a different operation returns 409."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "quoteId": {
                    "type": "string",
                    "format": "uuid"
                  }
                },
                "required": [
                  "quoteId"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "jobId": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "statusUrl": {
                      "type": "string"
                    },
                    "payment": {
                      "const": "paid"
                    }
                  },
                  "required": [
                    "jobId",
                    "statusUrl",
                    "payment"
                  ],
                  "additionalProperties": false
                }
              }
            },
            "headers": {
              "Payment-Receipt": {
                "schema": {
                  "type": "string"
                },
                "description": "Confirmed payment receipt"
              },
              "Retry-After": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request or missing idempotency key"
          },
          "402": {
            "description": "Payment required or SDK payment challenge",
            "headers": {
              "WWW-Authenticate": {
                "schema": {
                  "type": "string"
                },
                "description": "SDK payment challenge"
              }
            }
          },
          "403": {
            "description": "Origin check failed"
          },
          "404": {
            "description": "Job unavailable or capability invalid"
          },
          "408": {
            "description": "Upload interrupted or deadline exceeded; retry identical bytes"
          },
          "409": {
            "description": "Not ready, expired quote, unresolved purchase, or conflicting idempotency key"
          },
          "410": {
            "description": "Expired or deleted job"
          },
          "413": {
            "description": "Request or PDF too large"
          },
          "422": {
            "description": "Invalid PDF or unsupported billing country"
          },
          "429": {
            "description": "Rate limited; respect Retry-After"
          },
          "503": {
            "description": "Upload capacity busy (respect Retry-After), provider unavailable, purchases disabled, or settlement recording pending; poll before retrying a payment"
          }
        },
        "description": "Omit payment credentials for the initial 402 challenge; retry with SDK credentials, the same quote and idempotency key. A 202 confirms payment, not completed rendering."
      }
    },
    "/v1/jobs/{id}/purchase/x402": {
      "post": {
        "summary": "Optional x402 purchase (disabled by default); requires agenticPaymentsEnabled and advertised protocol. Retry with same quote and proof",
        "operationId": "post_v1_jobs__id__purchase_x402",
        "security": [
          {
            "JobToken": []
          },
          {
            "JobToken": [],
            "X402Payment": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 16,
              "maxLength": 128,
              "pattern": "^[a-zA-Z0-9_-]+$"
            },
            "description": "Stable random key per operation. Creation requires at least 32 characters. Reuse with a different operation returns 409."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "quoteId": {
                    "type": "string",
                    "format": "uuid"
                  }
                },
                "required": [
                  "quoteId"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "jobId": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "statusUrl": {
                      "type": "string"
                    },
                    "payment": {
                      "const": "paid"
                    }
                  },
                  "required": [
                    "jobId",
                    "statusUrl",
                    "payment"
                  ],
                  "additionalProperties": false
                }
              }
            },
            "headers": {
              "PAYMENT-RESPONSE": {
                "schema": {
                  "type": "string"
                },
                "description": "Confirmed payment receipt"
              },
              "Retry-After": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request or missing idempotency key"
          },
          "402": {
            "description": "Payment required or SDK payment challenge",
            "headers": {
              "PAYMENT-REQUIRED": {
                "schema": {
                  "type": "string"
                },
                "description": "SDK payment challenge"
              }
            }
          },
          "403": {
            "description": "Origin check failed"
          },
          "404": {
            "description": "Job unavailable or capability invalid"
          },
          "408": {
            "description": "Upload interrupted or deadline exceeded; retry identical bytes"
          },
          "409": {
            "description": "Not ready, expired quote, unresolved purchase, or conflicting idempotency key"
          },
          "410": {
            "description": "Expired or deleted job"
          },
          "413": {
            "description": "Request or PDF too large"
          },
          "422": {
            "description": "Invalid PDF or unsupported billing country"
          },
          "429": {
            "description": "Rate limited; respect Retry-After"
          },
          "503": {
            "description": "Upload capacity busy (respect Retry-After), provider unavailable, purchases disabled, or settlement recording pending; poll before retrying a payment"
          }
        },
        "description": "Omit payment credentials for the initial 402 challenge; retry with SDK credentials, the same quote and idempotency key. A 202 confirms payment, not completed rendering."
      }
    },
    "/v1/jobs/{id}/cancel-payment": {
      "post": {
        "summary": "Cancel a cancellable pending attempt before changing method",
        "operationId": "post_v1_jobs__id__cancel_payment",
        "security": [
          {
            "JobToken": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request or missing idempotency key"
          },
          "402": {
            "description": "Payment required or SDK payment challenge"
          },
          "403": {
            "description": "Origin check failed"
          },
          "404": {
            "description": "Job unavailable or capability invalid"
          },
          "408": {
            "description": "Upload interrupted or deadline exceeded; retry identical bytes"
          },
          "409": {
            "description": "Not ready, expired quote, unresolved purchase, or conflicting idempotency key"
          },
          "410": {
            "description": "Expired or deleted job"
          },
          "413": {
            "description": "Request or PDF too large"
          },
          "422": {
            "description": "Invalid PDF or unsupported billing country"
          },
          "429": {
            "description": "Rate limited; respect Retry-After"
          },
          "503": {
            "description": "Upload capacity busy (respect Retry-After), provider unavailable, purchases disabled, or settlement recording pending; poll before retrying a payment"
          }
        }
      }
    },
    "/v1/jobs/{id}/sample": {
      "get": {
        "summary": "First-page SAMPLE; never charges",
        "operationId": "get_v1_jobs__id__sample",
        "security": [
          {
            "JobToken": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "format",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "png"
              ]
            },
            "description": "Omit for PDF; png returns a first-page image."
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "image/png": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request or missing idempotency key"
          },
          "402": {
            "description": "Payment required or SDK payment challenge"
          },
          "403": {
            "description": "Origin check failed"
          },
          "404": {
            "description": "Job unavailable or capability invalid"
          },
          "408": {
            "description": "Upload interrupted or deadline exceeded; retry identical bytes"
          },
          "409": {
            "description": "Not ready, expired quote, unresolved purchase, or conflicting idempotency key"
          },
          "410": {
            "description": "Expired or deleted job"
          },
          "413": {
            "description": "Request or PDF too large"
          },
          "422": {
            "description": "Invalid PDF or unsupported billing country"
          },
          "429": {
            "description": "Rate limited; respect Retry-After"
          },
          "503": {
            "description": "Upload capacity busy (respect Retry-After), provider unavailable, purchases disabled, or settlement recording pending; poll before retrying a payment"
          }
        }
      }
    },
    "/v1/jobs/{id}/download": {
      "get": {
        "summary": "Full paid PDF; repeat downloads never charge",
        "operationId": "get_v1_jobs__id__download",
        "security": [
          {
            "JobToken": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request or missing idempotency key"
          },
          "402": {
            "description": "Payment required or SDK payment challenge"
          },
          "403": {
            "description": "Origin check failed"
          },
          "404": {
            "description": "Job unavailable or capability invalid"
          },
          "408": {
            "description": "Upload interrupted or deadline exceeded; retry identical bytes"
          },
          "409": {
            "description": "Not ready, expired quote, unresolved purchase, or conflicting idempotency key"
          },
          "410": {
            "description": "Expired or deleted job"
          },
          "413": {
            "description": "Request or PDF too large"
          },
          "422": {
            "description": "Invalid PDF or unsupported billing country"
          },
          "429": {
            "description": "Rate limited; respect Retry-After"
          },
          "503": {
            "description": "Upload capacity busy (respect Retry-After), provider unavailable, purchases disabled, or settlement recording pending; poll before retrying a payment"
          }
        }
      }
    },
    "/v1/jobs/{id}/recovery": {
      "get": {
        "summary": "Secret recovery URL; fragment must never be logged",
        "operationId": "get_v1_jobs__id__recovery",
        "security": [
          {
            "JobToken": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "url": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "url"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "Malformed request or missing idempotency key"
          },
          "402": {
            "description": "Payment required or SDK payment challenge"
          },
          "403": {
            "description": "Origin check failed"
          },
          "404": {
            "description": "Job unavailable or capability invalid"
          },
          "408": {
            "description": "Upload interrupted or deadline exceeded; retry identical bytes"
          },
          "409": {
            "description": "Not ready, expired quote, unresolved purchase, or conflicting idempotency key"
          },
          "410": {
            "description": "Expired or deleted job"
          },
          "413": {
            "description": "Request or PDF too large"
          },
          "422": {
            "description": "Invalid PDF or unsupported billing country"
          },
          "429": {
            "description": "Rate limited; respect Retry-After"
          },
          "503": {
            "description": "Upload capacity busy (respect Retry-After), provider unavailable, purchases disabled, or settlement recording pending; poll before retrying a payment"
          }
        }
      }
    },
    "/v1/jobs/{id}/session": {
      "post": {
        "summary": "Exchange recovery token for an HttpOnly browser cookie",
        "operationId": "post_v1_jobs__id__session",
        "security": [],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "token": {
                    "type": "string",
                    "minLength": 32,
                    "maxLength": 128
                  }
                },
                "required": [
                  "token"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            }
          },
          "400": {
            "description": "Malformed request or missing idempotency key"
          },
          "402": {
            "description": "Payment required or SDK payment challenge"
          },
          "403": {
            "description": "Origin check failed"
          },
          "404": {
            "description": "Job unavailable or capability invalid"
          },
          "408": {
            "description": "Upload interrupted or deadline exceeded; retry identical bytes"
          },
          "409": {
            "description": "Not ready, expired quote, unresolved purchase, or conflicting idempotency key"
          },
          "410": {
            "description": "Expired or deleted job"
          },
          "413": {
            "description": "Request or PDF too large"
          },
          "422": {
            "description": "Invalid PDF or unsupported billing country"
          },
          "429": {
            "description": "Rate limited; respect Retry-After"
          },
          "503": {
            "description": "Upload capacity busy (respect Retry-After), provider unavailable, purchases disabled, or settlement recording pending; poll before retrying a payment"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "JobToken": {
        "type": "apiKey",
        "in": "header",
        "name": "X-Job-Token"
      },
      "MppPayment": {
        "type": "apiKey",
        "in": "header",
        "name": "Authorization",
        "description": "SDK-generated Payment credential answering the MPP WWW-Authenticate challenge."
      },
      "X402Payment": {
        "type": "apiKey",
        "in": "header",
        "name": "PAYMENT-SIGNATURE",
        "description": "SDK-generated proof answering the x402 PAYMENT-REQUIRED challenge."
      }
    },
    "schemas": {
      "Job": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "processing": {
            "enum": [
              "created",
              "uploading",
              "validating",
              "preview_ready",
              "rendering",
              "ready",
              "failed",
              "expired"
            ]
          },
          "payment": {
            "enum": [
              "unpaid",
              "pending",
              "paid",
              "refund_pending",
              "refunded",
              "disputed"
            ]
          },
          "pages": {
            "type": "integer"
          },
          "error": {
            "type": "string"
          },
          "expiresAt": {
            "type": "integer"
          },
          "quote": {
            "$ref": "#/components/schemas/Quote"
          },
          "sampleAvailable": {
            "type": "boolean"
          },
          "downloadAvailable": {
            "type": "boolean"
          },
          "uploadAvailable": {
            "type": "boolean",
            "description": "File bytes can be submitted or retried for this job."
          },
          "uploadAccepted": {
            "type": "boolean",
            "description": "Immutable input is durably accepted; complete-upload can recover dispatch."
          },
          "checkoutResumeAvailable": {
            "type": "boolean"
          },
          "paymentCancelAvailable": {
            "type": "boolean"
          },
          "paymentRecoveryActionRequired": {
            "type": "boolean"
          },
          "refundActionRequired": {
            "type": "boolean"
          }
        },
        "required": [
          "id",
          "processing",
          "payment",
          "expiresAt",
          "sampleAvailable",
          "downloadAvailable"
        ],
        "additionalProperties": false
      },
      "Quote": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "baseAmount": {
            "type": "integer",
            "const": 100
          },
          "feeAmount": {
            "type": "integer"
          },
          "taxAmount": {
            "type": "integer"
          },
          "totalAmount": {
            "type": "integer"
          },
          "currency": {
            "const": "eur"
          },
          "country": {
            "type": "string"
          },
          "taxBps": {
            "type": "integer"
          },
          "taxPending": {
            "type": "boolean",
            "description": "True means totalAmount is before tax; Stripe Checkout confirms the final total before payment. False or absent means an authoritative final/fixed quote."
          },
          "policyVersion": {
            "type": "string"
          },
          "expiresAt": {
            "type": "integer"
          },
          "inputHash": {
            "type": "string"
          },
          "version": {
            "type": "string"
          },
          "payTo": {
            "type": "string"
          },
          "usdcAmount": {
            "type": "string"
          },
          "fxRate": {
            "type": "string"
          },
          "fxAsOf": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "baseAmount",
          "feeAmount",
          "taxAmount",
          "totalAmount",
          "currency",
          "country",
          "taxBps",
          "policyVersion",
          "expiresAt",
          "inputHash",
          "version"
        ],
        "additionalProperties": false
      },
      "Capabilities": {
        "type": "object",
        "properties": {
          "service": {
            "type": "object",
            "properties": {
              "output": {
                "type": "object",
                "properties": {
                  "mediaType": {
                    "type": "string",
                    "const": "application/pdf"
                  },
                  "style": {
                    "type": "string",
                    "const": "simulated_scan"
                  },
                  "colorMode": {
                    "type": "string",
                    "const": "grayscale"
                  },
                  "dpi": {
                    "type": "integer",
                    "const": 200
                  },
                  "imageBased": {
                    "type": "boolean",
                    "const": true
                  },
                  "selectableText": {
                    "type": "boolean",
                    "const": false
                  },
                  "editableFields": {
                    "type": "boolean",
                    "const": false
                  },
                  "paidWatermark": {
                    "type": "boolean",
                    "const": false
                  }
                },
                "required": [
                  "mediaType",
                  "style",
                  "colorMode",
                  "dpi",
                  "imageBased",
                  "selectableText",
                  "editableFields",
                  "paidWatermark"
                ],
                "additionalProperties": false
              },
              "purchase": {
                "type": "object",
                "properties": {
                  "unit": {
                    "type": "string",
                    "const": "document"
                  },
                  "includesAllAcceptedPages": {
                    "type": "boolean",
                    "const": true
                  },
                  "subscription": {
                    "type": "boolean",
                    "const": false
                  },
                  "repeatDownloadsIncluded": {
                    "type": "boolean",
                    "const": true
                  },
                  "taxAndFeesAdditional": {
                    "type": "boolean",
                    "const": true
                  },
                  "authoritativeTotal": {
                    "type": "string",
                    "const": "job.quote.totalAmount when taxPending is false or absent; Stripe Checkout confirms the final total when taxPending is true"
                  }
                },
                "required": [
                  "unit",
                  "includesAllAcceptedPages",
                  "subscription",
                  "repeatDownloadsIncluded",
                  "taxAndFeesAdditional",
                  "authoritativeTotal"
                ],
                "additionalProperties": false
              },
              "preview": {
                "type": "object",
                "properties": {
                  "free": {
                    "type": "boolean",
                    "const": true
                  },
                  "pages": {
                    "type": "integer",
                    "const": 1
                  },
                  "watermark": {
                    "type": "string",
                    "const": "SAMPLE"
                  },
                  "sameRenderingProcess": {
                    "type": "boolean",
                    "const": true
                  },
                  "previewsLaterPages": {
                    "type": "boolean",
                    "const": false
                  }
                },
                "required": [
                  "free",
                  "pages",
                  "watermark",
                  "sameRenderingProcess",
                  "previewsLaterPages"
                ],
                "additionalProperties": false
              },
              "timing": {
                "type": "object",
                "properties": {
                  "pollIntervalMs": {
                    "type": "integer",
                    "const": 2000
                  },
                  "productionEstimateAvailable": {
                    "type": "boolean",
                    "const": false
                  },
                  "previewEstimateSeconds": {
                    "type": "null"
                  },
                  "fullDocumentEstimateSeconds": {
                    "type": "null"
                  }
                },
                "required": [
                  "pollIntervalMs",
                  "productionEstimateAvailable",
                  "previewEstimateSeconds",
                  "fullDocumentEstimateSeconds"
                ],
                "additionalProperties": false
              },
              "examples": {
                "type": "array",
                "items": {
                  "anyOf": [
                    {
                      "type": "object",
                      "properties": {
                        "title": {
                          "type": "string",
                          "const": "Partly filled form"
                        },
                        "originalUrl": {
                          "type": "string",
                          "const": "https://paperfeel.ing/examples/form-original.pdf"
                        },
                        "outputUrl": {
                          "type": "string",
                          "const": "https://paperfeel.ing/examples/form-paperfeel.pdf"
                        }
                      },
                      "required": [
                        "title",
                        "originalUrl",
                        "outputUrl"
                      ],
                      "additionalProperties": false
                    },
                    {
                      "type": "object",
                      "properties": {
                        "title": {
                          "type": "string",
                          "const": "Editor-added text"
                        },
                        "originalUrl": {
                          "type": "string",
                          "const": "https://paperfeel.ing/examples/edited-original.pdf"
                        },
                        "outputUrl": {
                          "type": "string",
                          "const": "https://paperfeel.ing/examples/edited-paperfeel.pdf"
                        }
                      },
                      "required": [
                        "title",
                        "originalUrl",
                        "outputUrl"
                      ],
                      "additionalProperties": false
                    },
                    {
                      "type": "object",
                      "properties": {
                        "title": {
                          "type": "string",
                          "const": "Two-page project brief"
                        },
                        "originalUrl": {
                          "type": "string",
                          "const": "https://paperfeel.ing/examples/brief-original.pdf"
                        },
                        "outputUrl": {
                          "type": "string",
                          "const": "https://paperfeel.ing/examples/brief-paperfeel.pdf"
                        }
                      },
                      "required": [
                        "title",
                        "originalUrl",
                        "outputUrl"
                      ],
                      "additionalProperties": false
                    }
                  ]
                }
              },
              "policies": {
                "type": "object",
                "properties": {
                  "privacyUrl": {
                    "type": "string",
                    "const": "https://paperfeel.ing/privacy.html"
                  },
                  "termsUrl": {
                    "type": "string",
                    "const": "https://paperfeel.ing/terms.html"
                  },
                  "supportUrl": {
                    "type": "string",
                    "const": "https://paperfeel.ing/support.html"
                  },
                  "permanentPaidConversionFailure": {
                    "type": "string",
                    "const": "refund_service_total_including_tax_and_fees"
                  },
                  "separateWalletNetworkCostsRefunded": {
                    "type": "boolean",
                    "const": false
                  }
                },
                "required": [
                  "privacyUrl",
                  "termsUrl",
                  "supportUrl",
                  "permanentPaidConversionFailure",
                  "separateWalletNetworkCostsRefunded"
                ],
                "additionalProperties": false
              }
            },
            "required": [
              "output",
              "purchase",
              "preview",
              "timing",
              "examples",
              "policies"
            ],
            "additionalProperties": false,
            "description": "Output suitability, purchase scope, sample limits, timing availability, public examples and policy links. Null timing estimates mean unmeasured, never instant completion. Inspect the live quote for the payable total."
          },
          "feedback": {
            "type": "object",
            "properties": {
              "enabled": {
                "type": "boolean"
              },
              "requiresJob": {
                "const": false
              },
              "requiresPayment": {
                "const": false
              },
              "requiresAccount": {
                "const": false
              },
              "retentionDays": {
                "type": "integer",
                "const": 90
              },
              "maxDetailsLength": {
                "type": "integer",
                "const": 4000
              }
            },
            "required": [
              "enabled",
              "requiresJob",
              "requiresPayment",
              "requiresAccount",
              "retentionDays",
              "maxDetailsLength"
            ],
            "additionalProperties": false
          },
          "baseAmount": {
            "type": "integer",
            "const": 100
          },
          "currency": {
            "const": "eur"
          },
          "maxBytes": {
            "type": "integer"
          },
          "maxPages": {
            "type": "integer"
          },
          "countries": {
            "type": "array",
            "items": {
              "type": "string",
              "pattern": "^[A-Z]{2}$"
            },
            "description": "Manual-mode billing countries. In stripe_checkout mode, Stripe collects the billing location; this list does not restrict Checkout selling countries."
          },
          "taxMode": {
            "enum": [
              "manual",
              "stripe_checkout"
            ]
          },
          "protocols": {
            "type": "array",
            "items": {
              "enum": [
                "checkout",
                "mpp",
                "x402",
                "development"
              ]
            }
          },
          "purchasesEnabled": {
            "type": "boolean"
          },
          "agenticPaymentsEnabled": {
            "type": "boolean",
            "description": "MPP and x402 rollout flag; defaults false. Use only protocols currently advertised."
          },
          "development": {
            "type": "boolean"
          },
          "feeAmount": {
            "type": "integer"
          },
          "retentionHours": {
            "type": "integer",
            "description": "Full-output retention after generation; use each job's expiresAt for its current deadline."
          }
        },
        "required": [
          "service",
          "feedback",
          "baseAmount",
          "currency",
          "maxBytes",
          "maxPages",
          "countries",
          "taxMode",
          "protocols",
          "purchasesEnabled",
          "agenticPaymentsEnabled",
          "development",
          "feeAmount",
          "retentionHours"
        ],
        "additionalProperties": false
      }
    }
  }
}
