{
  "openapi": "3.2.0",
  "info": {
    "title": "Fylane API",
    "version": "1.0.0",
    "summary": "File processing as an API.",
    "description": "Convert, compress and extract from files over HTTP.\n\nThree things shape every endpoint here:\n\n- **Bytes never traverse the API.** Uploads and downloads use pre-signed,\n  object-specific, short-lived URLs, so file size does not affect API cost\n  or latency.\n- **Anything slower than roughly three seconds is asynchronous.** Those\n  endpoints return `202` and a job resource rather than blocking.\n- **Every mutating call takes `Idempotency-Key`.** A replay with the same\n  body returns the stored response and is not re-billed; a replay with a\n  different body returns `409`.",
    "contact": {
      "name": "Fylane",
      "url": "https://fylane.dev",
      "email": "support@fylane.dev"
    },
    "license": {
      "name": "Proprietary"
    }
  },
  "servers": [
    {
      "url": "https://api.fylane.dev/v1",
      "description": "Production"
    }
  ],
  "tags": [
    {
      "name": "Core",
      "summary": "Core",
      "description": "Everything in the V1 surface.",
      "kind": "nav"
    },
    {
      "name": "Uploads",
      "parent": "Core",
      "summary": "Uploads",
      "description": "Getting bytes into Fylane without them touching the API.",
      "kind": "nav"
    },
    {
      "name": "Jobs",
      "parent": "Core",
      "summary": "Jobs",
      "description": "Asking for work and finding out how it went.",
      "kind": "nav"
    },
    {
      "name": "Files",
      "parent": "Core",
      "summary": "Files",
      "description": "Stored files and the artifacts derived from them.",
      "kind": "nav"
    },
    {
      "name": "Webhooks",
      "parent": "Core",
      "summary": "Webhooks",
      "description": "Signed delivery of events, so you do not have to poll.",
      "kind": "nav"
    },
    {
      "name": "Usage",
      "parent": "Core",
      "summary": "Usage",
      "description": "Credits, storage and egress for the current period.",
      "kind": "nav"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "paths": {
    "/uploads": {
      "post": {
        "operationId": "createUpload",
        "summary": "Create an upload session",
        "description": "Returns a single-use, size-capped, pre-signed URL. Upload the bytes directly to it; they never pass through the API, which is why a 5 GB file and a 5 KB file cost the same to accept. Two variants share the endpoint: `multipart: true` answers with one pre-signed URL per part instead of a single PUT, and `from_url` has the platform fetch a public URL itself — the answer is then the completed `File`, not a session.",
        "tags": [
          "Uploads"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "description": "Either declare the bytes you are about to send (`filename` + `byte_size`), or name a public URL (`from_url`) for the platform to fetch.",
                "oneOf": [
                  {
                    "required": [
                      "filename",
                      "byte_size"
                    ]
                  },
                  {
                    "required": [
                      "from_url"
                    ]
                  }
                ],
                "properties": {
                  "filename": {
                    "type": "string",
                    "maxLength": 1024
                  },
                  "byte_size": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "Declared size. The signed URL enforces it; a larger body is refused."
                  },
                  "mime_type": {
                    "type": "string",
                    "description": "Declared type. Verified against the bytes."
                  },
                  "multipart": {
                    "type": "boolean",
                    "description": "Ask for a multipart session: per-part pre-signed URLs (16 MB+ parts, at most 64), finished via `POST /v1/uploads/{upload_id}/complete-parts`. Parts fail and retry individually, and `POST /v1/uploads/{upload_id}/parts` re-mints fresh URLs — the resume path."
                  },
                  "from_url": {
                    "type": "string",
                    "format": "uri",
                    "description": "A public HTTPS URL for the platform to fetch server-side. Private networks and redirects are refused; the response is the completed, validated `File` rather than an upload session."
                  },
                  "project_id": {
                    "type": "string",
                    "description": "Defaults to the key’s project.",
                    "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$",
                    "examples": [
                      "prj_01JBQ8Z5T7WXK9MNP2RSTVA3C4"
                    ]
                  },
                  "validation": {
                    "type": "object",
                    "additionalProperties": false,
                    "description": "Acceptance policy for this upload, judged against the detected bytes at completion — never against the declared type. A file that fails is recorded `rejected` with the findings, and completion answers `validation_failed`.",
                    "properties": {
                      "formats": {
                        "type": "array",
                        "minItems": 1,
                        "maxItems": 32,
                        "items": {
                          "type": "string"
                        },
                        "description": "Allowed detected formats, by extension (\"pdf\") or MIME type (\"application/pdf\")."
                      },
                      "max_bytes": {
                        "type": "integer",
                        "minimum": 1
                      },
                      "min_bytes": {
                        "type": "integer",
                        "minimum": 1
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The `from_url` variant only: the URL was fetched, validated and completed in one step.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/File"
                }
              }
            }
          },
          "201": {
            "description": "Session created.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UploadSession"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "413": {
            "$ref": "#/components/responses/Error413"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          },
          "504": {
            "$ref": "#/components/responses/Error504"
          }
        }
      }
    },
    "/uploads/{upload_id}": {
      "get": {
        "operationId": "getUpload",
        "summary": "Retrieve an upload session",
        "tags": [
          "Uploads"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/UploadId"
          }
        ],
        "responses": {
          "200": {
            "description": "The session.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UploadSession"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          },
          "504": {
            "$ref": "#/components/responses/Error504"
          }
        }
      }
    },
    "/uploads/{upload_id}/parts": {
      "post": {
        "operationId": "mintUploadParts",
        "summary": "Re-mint part URLs",
        "description": "Fresh pre-signed URLs for every part of a multipart session — the resume path. Losing a connection loses only the parts in flight: re-mint, re-upload the missing parts, and complete.",
        "tags": [
          "Uploads"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/UploadId"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "One fresh URL per part.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UploadParts"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          },
          "504": {
            "$ref": "#/components/responses/Error504"
          }
        }
      }
    },
    "/uploads/{upload_id}/complete-parts": {
      "post": {
        "operationId": "completeUploadParts",
        "summary": "Assemble a multipart upload",
        "description": "Hand back the `{part_number, etag}` pairs storage answered each part with. The object is assembled, measured (the declared size was a promise; the measurement is what validation judges), and then the one standard completion runs — detection, policy, hash, duplicate detection, identical to a single PUT.",
        "tags": [
          "Uploads"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/UploadId"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "parts"
                ],
                "additionalProperties": false,
                "properties": {
                  "parts": {
                    "type": "array",
                    "minItems": 1,
                    "items": {
                      "type": "object",
                      "required": [
                        "part_number",
                        "etag"
                      ],
                      "additionalProperties": false,
                      "properties": {
                        "part_number": {
                          "type": "integer",
                          "minimum": 1
                        },
                        "etag": {
                          "type": "string",
                          "description": "The `ETag` response header from that part’s PUT, verbatim."
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Assembled and completed.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/File"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "422": {
            "$ref": "#/components/responses/Error422"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          },
          "504": {
            "$ref": "#/components/responses/Error504"
          }
        }
      }
    },
    "/uploads/{upload_id}/abort": {
      "post": {
        "operationId": "abortUpload",
        "summary": "Abort a multipart upload",
        "description": "Discards the parts uploaded so far. Sessions abandoned without an abort are swept automatically after a day.",
        "tags": [
          "Uploads"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/UploadId"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "204": {
            "description": "Aborted; stored parts discarded.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          },
          "504": {
            "$ref": "#/components/responses/Error504"
          }
        }
      }
    },
    "/jobs": {
      "post": {
        "operationId": "createJob",
        "summary": "Create a job",
        "description": "Accepts the work and returns immediately with `202`. Credits are reserved atomically at this point — a job that would exceed the balance is refused here rather than discovered on an invoice. Supply exactly one of `operation` or `preset`.",
        "tags": [
          "Jobs"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "file_id"
                ],
                "additionalProperties": false,
                "description": "Exactly one of `operation` or `preset` is required.",
                "oneOf": [
                  {
                    "required": [
                      "operation"
                    ]
                  },
                  {
                    "required": [
                      "preset"
                    ]
                  }
                ],
                "properties": {
                  "file_id": {
                    "type": "string",
                    "description": "The file to process.",
                    "pattern": "^file_[0-9A-HJKMNP-TV-Z]{26}$",
                    "examples": [
                      "file_01JBQ8Z5T7WXK9MNP2RSTVA3C4"
                    ]
                  },
                  "operation": {
                    "type": "string",
                    "examples": [
                      "inspect",
                      "scan",
                      "convert",
                      "ocr",
                      "pdf.merge",
                      "pdf.pages",
                      "pdf.split",
                      "pdf.compress",
                      "pdf.render"
                    ]
                  },
                  "preset": {
                    "type": "string",
                    "enum": [
                      "ai-ready",
                      "document",
                      "image",
                      "avatar",
                      "inspect"
                    ]
                  },
                  "options": {
                    "type": "object",
                    "description": "Operation-specific parameters.",
                    "additionalProperties": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted. The job is queued; subscribe to a webhook or poll `GET /v1/jobs/{job_id}`.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "422": {
            "$ref": "#/components/responses/Error422"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          },
          "504": {
            "$ref": "#/components/responses/Error504"
          }
        }
      },
      "get": {
        "operationId": "listJobs",
        "summary": "List jobs",
        "tags": [
          "Jobs"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/StartingAfter"
          },
          {
            "name": "state",
            "in": "query",
            "description": "Filter by job state.",
            "schema": {
              "type": "string",
              "enum": [
                "queued",
                "running",
                "succeeded",
                "failed",
                "cancelled"
              ]
            }
          },
          {
            "name": "project_id",
            "in": "query",
            "description": "Filter by project.",
            "schema": {
              "type": "string",
              "description": "Project identifier.",
              "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$",
              "examples": [
                "prj_01JBQ8Z5T7WXK9MNP2RSTVA3C4"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of jobs, newest first.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JobList"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          },
          "504": {
            "$ref": "#/components/responses/Error504"
          }
        }
      }
    },
    "/jobs/{job_id}": {
      "get": {
        "operationId": "getJob",
        "summary": "Retrieve a job",
        "tags": [
          "Jobs"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/JobId"
          }
        ],
        "responses": {
          "200": {
            "description": "The job.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          },
          "504": {
            "$ref": "#/components/responses/Error504"
          }
        }
      }
    },
    "/batches": {
      "post": {
        "operationId": "createBatch",
        "summary": "Create a batch",
        "description": "One operation across up to 100 files, accepted atomically: every member is admitted and priced in a single transaction, or the batch does not exist. Members are then ordinary jobs — same queues, same retries, same webhooks, same settlement — and a member that fails is a per-item error, charged nothing, while the rest proceed.",
        "tags": [
          "Jobs"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "file_ids"
                ],
                "additionalProperties": false,
                "description": "Exactly one of `operation` or `preset` is required.",
                "oneOf": [
                  {
                    "required": [
                      "operation"
                    ]
                  },
                  {
                    "required": [
                      "preset"
                    ]
                  }
                ],
                "properties": {
                  "file_ids": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 100,
                    "items": {
                      "type": "string",
                      "description": "A file to process.",
                      "pattern": "^file_[0-9A-HJKMNP-TV-Z]{26}$",
                      "examples": [
                        "file_01JBQ8Z5T7WXK9MNP2RSTVA3C4"
                      ]
                    }
                  },
                  "operation": {
                    "type": "string",
                    "examples": [
                      "image.convert",
                      "ocr",
                      "pdf.compress"
                    ]
                  },
                  "preset": {
                    "type": "string",
                    "enum": [
                      "ai-ready",
                      "document",
                      "image",
                      "avatar",
                      "inspect"
                    ]
                  },
                  "options": {
                    "type": "object",
                    "description": "Operation-specific parameters, applied to every member.",
                    "additionalProperties": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted. Every member is queued; poll `GET /v1/batches/{batch_id}` or subscribe to job webhooks.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Batch"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "422": {
            "$ref": "#/components/responses/Error422"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          },
          "504": {
            "$ref": "#/components/responses/Error504"
          }
        }
      }
    },
    "/batches/{batch_id}": {
      "get": {
        "operationId": "getBatch",
        "summary": "Retrieve a batch",
        "description": "Aggregated state counts, credits estimated and charged, and the per-item outcomes. `state` is `completed` once every member is terminal — individual failures are items, not a batch failure.",
        "tags": [
          "Jobs"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/BatchId"
          }
        ],
        "responses": {
          "200": {
            "description": "The batch and its members.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Batch"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          },
          "504": {
            "$ref": "#/components/responses/Error504"
          }
        }
      }
    },
    "/files": {
      "get": {
        "operationId": "listFiles",
        "summary": "List files",
        "tags": [
          "Files"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of files, newest first.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FileList"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          },
          "504": {
            "$ref": "#/components/responses/Error504"
          }
        }
      }
    },
    "/files/{file_id}": {
      "get": {
        "operationId": "getFile",
        "summary": "Retrieve a file",
        "tags": [
          "Files"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/FileId"
          }
        ],
        "responses": {
          "200": {
            "description": "The file and its artifacts.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/File"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          },
          "504": {
            "$ref": "#/components/responses/Error504"
          }
        }
      },
      "delete": {
        "operationId": "deleteFile",
        "summary": "Delete a file",
        "description": "Deletes the file and every artifact derived from it. Not reversible.",
        "tags": [
          "Files"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/FileId"
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted, along with every artifact derived from it.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          },
          "504": {
            "$ref": "#/components/responses/Error504"
          }
        }
      }
    },
    "/files/{file_id}/complete": {
      "post": {
        "operationId": "completeUpload",
        "summary": "Complete an upload",
        "description": "Tell the platform the bytes have landed. Detection, validation policy and (within the hash window) SHA-256 and duplicate detection all run here, against the bytes actually stored — never against what was declared. A file that fails its validation policy is recorded `rejected` and this call answers with the refusal.",
        "tags": [
          "Uploads"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/FileId"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "The completed file, `ready` for jobs — carrying `duplicate_of` when another ready file in the project has identical content.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/File"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "422": {
            "$ref": "#/components/responses/Error422"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          },
          "504": {
            "$ref": "#/components/responses/Error504"
          }
        }
      }
    },
    "/files/{file_id}/history": {
      "get": {
        "operationId": "getFileHistory",
        "summary": "Retrieve a file’s history",
        "description": "The file’s own story, in order: upload opened, validated or rejected, every job created and how each finished — assembled from the records the platform already keeps, never a second ledger that could disagree with the first.",
        "tags": [
          "Files"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/FileId"
          }
        ],
        "responses": {
          "200": {
            "description": "The events, oldest first.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FileHistory"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          },
          "504": {
            "$ref": "#/components/responses/Error504"
          }
        }
      }
    },
    "/files/{file_id}/download-url": {
      "post": {
        "operationId": "createDownloadUrl",
        "summary": "Mint a download URL",
        "description": "Authorises first, then mints a short-lived URL on `cdn.fylane.dev`. The authorisation decision happens before the URL exists, so possession of a URL is never itself the permission.",
        "tags": [
          "Files"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/FileId"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "artifact_id": {
                    "type": "string",
                    "description": "Defaults to the original file.",
                    "pattern": "^art_[0-9A-HJKMNP-TV-Z]{26}$",
                    "examples": [
                      "art_01JBQ8Z5T7WXK9MNP2RSTVA3C4"
                    ]
                  },
                  "expires_in": {
                    "type": "integer",
                    "minimum": 60,
                    "maximum": 86400,
                    "default": 3600,
                    "description": "Lifetime in seconds."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "A short-lived URL.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DownloadUrl"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          },
          "504": {
            "$ref": "#/components/responses/Error504"
          }
        }
      }
    },
    "/jobs/{job_id}/cancel": {
      "post": {
        "operationId": "cancelJob",
        "summary": "Cancel a queued job",
        "description": "Only queued work can be reclaimed. A job the worker already claimed keeps running — compute cannot be un-spent — and answers 409 with its state. Subscribers hear `job.cancelled`.",
        "tags": [
          "Jobs"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/JobId"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "200": {
            "description": "Cancelled.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          },
          "504": {
            "$ref": "#/components/responses/Error504"
          }
        }
      }
    },
    "/limits": {
      "get": {
        "operationId": "getLimits",
        "summary": "Supported formats, operations and ceilings",
        "description": "Machine-readable capabilities: supported formats with their byte ceilings, the operation catalogue with class and per-megabyte pricing, processing limits, and per-organization velocity ceilings. Read from the same constants the enforcing code reads.",
        "tags": [
          "Usage"
        ],
        "responses": {
          "200": {
            "description": "The current limits.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Limits"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          },
          "504": {
            "$ref": "#/components/responses/Error504"
          }
        }
      }
    },
    "/estimate": {
      "post": {
        "operationId": "createEstimate",
        "summary": "Estimate a job before creating it",
        "description": "The same arithmetic job acceptance runs: the answer is the number a created job would reserve. Naturally idempotent — the same question always gets the same answer.",
        "tags": [
          "Usage"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "operation",
                  "byte_size"
                ],
                "additionalProperties": false,
                "properties": {
                  "operation": {
                    "type": "string",
                    "examples": [
                      "ocr"
                    ]
                  },
                  "byte_size": {
                    "type": "integer",
                    "minimum": 1
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The estimate.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Estimate"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          },
          "504": {
            "$ref": "#/components/responses/Error504"
          }
        }
      }
    },
    "/webhook-endpoints": {
      "post": {
        "operationId": "createWebhookEndpoint",
        "summary": "Register a webhook endpoint",
        "description": "The response contains `secret` exactly once. It is not retrievable afterwards; rotate the endpoint if you lose it.",
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "url",
                  "enabled_events"
                ],
                "additionalProperties": false,
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri",
                    "description": "Must be HTTPS."
                  },
                  "enabled_events": {
                    "type": "array",
                    "minItems": 1,
                    "items": {
                      "type": "string",
                      "enum": [
                        "job.succeeded",
                        "job.failed",
                        "job.cancelled",
                        "file.ready",
                        "file.rejected",
                        "file.deleted",
                        "file.expired"
                      ]
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Registered. Contains the secret.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpoint"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          },
          "504": {
            "$ref": "#/components/responses/Error504"
          }
        }
      },
      "get": {
        "operationId": "listWebhookEndpoints",
        "summary": "List webhook endpoints",
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/StartingAfter"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of endpoints.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpointList"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/Error400"
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          },
          "504": {
            "$ref": "#/components/responses/Error504"
          }
        }
      }
    },
    "/webhook-endpoints/{webhook_endpoint_id}": {
      "delete": {
        "operationId": "deleteWebhookEndpoint",
        "summary": "Delete a webhook endpoint",
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/WebhookEndpointId"
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted. No further events are delivered to it.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          },
          "504": {
            "$ref": "#/components/responses/Error504"
          }
        }
      }
    },
    "/webhook-endpoints/{webhook_endpoint_id}/test": {
      "post": {
        "operationId": "testWebhookEndpoint",
        "summary": "Send a test event",
        "description": "Delivers a signed synthetic event so you can verify your handler before relying on it.",
        "tags": [
          "Webhooks"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/WebhookEndpointId"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "responses": {
          "202": {
            "description": "Test event queued for delivery.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "404": {
            "$ref": "#/components/responses/Error404"
          },
          "409": {
            "$ref": "#/components/responses/Error409"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          },
          "504": {
            "$ref": "#/components/responses/Error504"
          }
        }
      }
    },
    "/usage": {
      "get": {
        "operationId": "getUsage",
        "summary": "Retrieve usage",
        "description": "Consumption for the current billing period. This is the authoritative record — never derive usage from gateway throttling counters.",
        "tags": [
          "Usage"
        ],
        "responses": {
          "200": {
            "description": "Usage for the current period.",
            "headers": {
              "X-Request-Id": {
                "$ref": "#/components/headers/X-Request-Id"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/X-RateLimit-Limit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/X-RateLimit-Remaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/X-RateLimit-Reset"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Usage"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error401"
          },
          "403": {
            "$ref": "#/components/responses/Error403"
          },
          "429": {
            "$ref": "#/components/responses/Error429"
          },
          "500": {
            "$ref": "#/components/responses/Error500"
          },
          "503": {
            "$ref": "#/components/responses/Error503"
          },
          "504": {
            "$ref": "#/components/responses/Error504"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Error": {
        "type": "object",
        "description": "The only error shape this API returns. Every error answers four questions: what happened, whether retrying helps, what to change, and where to read more.",
        "required": [
          "type",
          "code",
          "message",
          "retryable",
          "request_id",
          "docs_url"
        ],
        "additionalProperties": false,
        "properties": {
          "type": {
            "type": "string",
            "format": "uri",
            "description": "Stable URI identifying the error class.",
            "examples": [
              "https://fylane.dev/errors/unsupported-file"
            ]
          },
          "code": {
            "type": "string",
            "description": "Machine-readable code. Switch on this, never on `message`.",
            "enum": [
              "invalid_request",
              "unsupported_file",
              "file_too_large",
              "corrupted_file",
              "encrypted_file",
              "unsupported_operation",
              "missing_credentials",
              "invalid_api_key",
              "expired_api_key",
              "revoked_api_key",
              "api_key_in_query",
              "insufficient_scope",
              "ip_not_allowed",
              "environment_mismatch",
              "organization_suspended",
              "forbidden",
              "not_found",
              "idempotency_conflict",
              "upload_session_consumed",
              "conflict",
              "payload_too_large",
              "malware_detected",
              "policy_violation",
              "validation_failed",
              "key_restricted",
              "key_spend_limit",
              "output_validation_failed",
              "unsupported_input",
              "rate_limited",
              "concurrency_limit",
              "credits_exhausted",
              "payment_required",
              "internal_error",
              "processing_unavailable",
              "dependency_unavailable",
              "timeout"
            ]
          },
          "message": {
            "type": "string",
            "description": "Human-readable and safe to display. Never contains internal identifiers or another tenant’s data."
          },
          "retryable": {
            "type": "boolean",
            "description": "Whether an identical request may succeed later. `false` means fix something first."
          },
          "request_id": {
            "type": "string",
            "description": "Also returned in `X-Request-Id`. Quote it in support requests.",
            "pattern": "^req_[0-9A-HJKMNP-TV-Z]{26}$"
          },
          "docs_url": {
            "type": "string",
            "format": "uri",
            "examples": [
              "https://fylane.dev/docs/errors/unsupported-file"
            ]
          },
          "details": {
            "type": "object",
            "description": "Structured, client-safe context. Shape varies by `code`.",
            "additionalProperties": true
          },
          "retry_after": {
            "type": "integer",
            "minimum": 0,
            "description": "Seconds to wait. Present on 429 only, and mirrors the `Retry-After` header."
          }
        }
      },
      "Artifact": {
        "type": "object",
        "description": "A file produced by a job. Delivered from `cdn.fylane.dev`, never from S3.",
        "required": [
          "id",
          "object",
          "kind",
          "mime_type",
          "byte_size",
          "created_at"
        ],
        "additionalProperties": false,
        "properties": {
          "id": {
            "type": "string",
            "description": "Artifact identifier.",
            "pattern": "^art_[0-9A-HJKMNP-TV-Z]{26}$",
            "examples": [
              "art_01JBQ8Z5T7WXK9MNP2RSTVA3C4"
            ]
          },
          "object": {
            "type": "string",
            "const": "artifact"
          },
          "kind": {
            "type": "string",
            "description": "What this artifact is, within the job that produced it.",
            "examples": [
              "optimized",
              "thumbnail",
              "markdown",
              "searchable_pdf",
              "preview"
            ]
          },
          "mime_type": {
            "type": "string",
            "examples": [
              "image/webp"
            ]
          },
          "byte_size": {
            "type": "integer",
            "minimum": 0
          },
          "width": {
            "type": "integer",
            "minimum": 1,
            "description": "Images only."
          },
          "height": {
            "type": "integer",
            "minimum": 1,
            "description": "Images only."
          },
          "page_count": {
            "type": "integer",
            "minimum": 1,
            "description": "Paged documents only."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "RFC 3339 timestamp in UTC, with a `Z` suffix.",
            "examples": [
              "2026-08-20T14:03:11Z"
            ]
          }
        }
      },
      "File": {
        "type": "object",
        "description": "An uploaded file. A file is not processable until it leaves the `quarantined` state.",
        "required": [
          "id",
          "object",
          "project_id",
          "filename",
          "byte_size",
          "mime_type",
          "state",
          "artifacts",
          "created_at"
        ],
        "additionalProperties": false,
        "properties": {
          "id": {
            "type": "string",
            "description": "File identifier.",
            "pattern": "^file_[0-9A-HJKMNP-TV-Z]{26}$",
            "examples": [
              "file_01JBQ8Z5T7WXK9MNP2RSTVA3C4"
            ]
          },
          "object": {
            "type": "string",
            "const": "file"
          },
          "project_id": {
            "type": "string",
            "description": "Project this file belongs to.",
            "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$",
            "examples": [
              "prj_01JBQ8Z5T7WXK9MNP2RSTVA3C4"
            ]
          },
          "filename": {
            "type": "string",
            "maxLength": 1024
          },
          "byte_size": {
            "type": "integer",
            "minimum": 0
          },
          "mime_type": {
            "type": "string",
            "description": "The type the client declared at upload."
          },
          "detected_mime_type": {
            "type": "string",
            "description": "The type Fylane detected by inspecting the bytes. A mismatch with `mime_type` is not an error on its own, but it is recorded."
          },
          "sha256": {
            "type": "string",
            "pattern": "^[a-f0-9]{64}$"
          },
          "state": {
            "type": "string",
            "enum": [
              "quarantined",
              "ready",
              "rejected",
              "deleted"
            ],
            "description": "`quarantined` until validation completes. `rejected` means validation refused it — see the job error for why."
          },
          "artifacts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Artifact"
            }
          },
          "duplicate_of": {
            "type": "string",
            "description": "Tenant-local duplicate signal, present on upload completion when another ready file in the same project has the same SHA-256. Policy is yours: reuse it, or process anyway.",
            "pattern": "^file_[0-9A-HJKMNP-TV-Z]{26}$",
            "examples": [
              "file_01JBQ8Z5T7WXK9MNP2RSTVA3C4"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "RFC 3339 timestamp in UTC, with a `Z` suffix.",
            "examples": [
              "2026-08-20T14:03:11Z"
            ]
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "When retention deletes this file. Absent means no scheduled expiry.",
            "examples": [
              "2026-08-20T14:03:11Z"
            ]
          }
        }
      },
      "UploadSession": {
        "type": "object",
        "description": "A single-use capability to upload one file directly to storage. The bytes never traverse the API.",
        "required": [
          "id",
          "object",
          "file_id",
          "url",
          "method",
          "max_bytes",
          "status",
          "expires_at",
          "created_at"
        ],
        "additionalProperties": false,
        "properties": {
          "id": {
            "type": "string",
            "description": "Upload session identifier.",
            "pattern": "^ups_[0-9A-HJKMNP-TV-Z]{26}$",
            "examples": [
              "ups_01JBQ8Z5T7WXK9MNP2RSTVA3C4"
            ]
          },
          "object": {
            "type": "string",
            "const": "upload_session"
          },
          "file_id": {
            "type": "string",
            "description": "The file this session will produce.",
            "pattern": "^file_[0-9A-HJKMNP-TV-Z]{26}$",
            "examples": [
              "file_01JBQ8Z5T7WXK9MNP2RSTVA3C4"
            ]
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Pre-signed URL. Object-specific, operation-specific, size-capped and short-lived. Treat it as a secret."
          },
          "method": {
            "type": "string",
            "const": "PUT"
          },
          "headers": {
            "type": "object",
            "description": "Headers that must be sent verbatim with the upload. Signature covers them.",
            "additionalProperties": {
              "type": "string"
            }
          },
          "max_bytes": {
            "type": "integer",
            "minimum": 1,
            "description": "The signature enforces this. A larger body is rejected by storage, not by us."
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "consumed",
              "expired"
            ]
          },
          "multipart": {
            "type": "boolean",
            "description": "Present and true when the session is multipart: upload the `parts`, then `POST /v1/uploads/{id}/complete-parts`."
          },
          "part_size": {
            "type": "integer",
            "minimum": 5242880,
            "description": "Bytes per part; the last part may be smaller."
          },
          "part_count": {
            "type": "integer",
            "minimum": 1
          },
          "parts": {
            "type": "array",
            "description": "One pre-signed PUT per part. Fresh URLs any time from `POST /v1/uploads/{id}/parts` — that is the resume path.",
            "items": {
              "type": "object",
              "required": [
                "part_number",
                "url"
              ],
              "additionalProperties": false,
              "properties": {
                "part_number": {
                  "type": "integer",
                  "minimum": 1
                },
                "url": {
                  "type": "string",
                  "format": "uri"
                }
              }
            }
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "RFC 3339 timestamp in UTC, with a `Z` suffix.",
            "examples": [
              "2026-08-20T14:03:11Z"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "RFC 3339 timestamp in UTC, with a `Z` suffix.",
            "examples": [
              "2026-08-20T14:03:11Z"
            ]
          }
        }
      },
      "Job": {
        "type": "object",
        "description": "A unit of processing. Every operation returns one of these rather than a result.",
        "required": [
          "id",
          "object",
          "project_id",
          "file_id",
          "state",
          "class",
          "credits_estimated",
          "artifacts",
          "created_at"
        ],
        "additionalProperties": false,
        "properties": {
          "id": {
            "type": "string",
            "description": "Job identifier.",
            "pattern": "^job_[0-9A-HJKMNP-TV-Z]{26}$",
            "examples": [
              "job_01JBQ8Z5T7WXK9MNP2RSTVA3C4"
            ]
          },
          "object": {
            "type": "string",
            "const": "job"
          },
          "project_id": {
            "type": "string",
            "description": "Project this job belongs to.",
            "pattern": "^prj_[0-9A-HJKMNP-TV-Z]{26}$",
            "examples": [
              "prj_01JBQ8Z5T7WXK9MNP2RSTVA3C4"
            ]
          },
          "file_id": {
            "type": "string",
            "description": "Input file.",
            "pattern": "^file_[0-9A-HJKMNP-TV-Z]{26}$",
            "examples": [
              "file_01JBQ8Z5T7WXK9MNP2RSTVA3C4"
            ]
          },
          "operation": {
            "type": "string",
            "description": "The single operation requested. Mutually exclusive with `preset`.",
            "examples": [
              "pdf.to_markdown",
              "image.resize",
              "office.to_pdf"
            ]
          },
          "preset": {
            "type": "string",
            "enum": [
              "ai-ready",
              "document",
              "image",
              "avatar",
              "inspect"
            ],
            "description": "A named pipeline. Mutually exclusive with `operation`."
          },
          "state": {
            "type": "string",
            "enum": [
              "queued",
              "running",
              "succeeded",
              "failed",
              "cancelled"
            ]
          },
          "class": {
            "type": "string",
            "enum": [
              "fast",
              "standard",
              "heavy"
            ],
            "description": "Latency class the job was routed to, derived from the work it implies."
          },
          "credits_estimated": {
            "type": "integer",
            "minimum": 0,
            "description": "Reserved when the job was accepted. Never exceeded without a new reservation."
          },
          "credits_charged": {
            "type": "integer",
            "minimum": 0,
            "description": "Settled on completion. Absent while the job is unfinished."
          },
          "progress": {
            "type": "object",
            "additionalProperties": false,
            "description": "How far along the work is — present only while `state` is `running`, and only for operations that can honestly divide themselves into steps. Its absence means the job is not reporting, never that it has stalled.",
            "required": [
              "phase",
              "percent"
            ],
            "properties": {
              "phase": {
                "type": "string",
                "enum": [
                  "downloading",
                  "processing",
                  "uploading"
                ],
                "description": "Reading the input, doing the work, or writing the results."
              },
              "percent": {
                "type": "integer",
                "minimum": 0,
                "maximum": 100,
                "description": "Across the whole job, not the current phase."
              },
              "detail": {
                "type": "string",
                "description": "What the work is on now, when the operation knows: \"step 2 of 5: ocr\"."
              },
              "at": {
                "type": "string",
                "format": "date-time",
                "description": "When the task reported this.",
                "examples": [
                  "2026-08-20T14:03:11Z"
                ]
              }
            }
          },
          "error": {
            "$ref": "#/components/schemas/Error"
          },
          "artifacts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Artifact"
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "RFC 3339 timestamp in UTC, with a `Z` suffix.",
            "examples": [
              "2026-08-20T14:03:11Z"
            ]
          },
          "started_at": {
            "type": "string",
            "format": "date-time",
            "description": "RFC 3339 timestamp in UTC, with a `Z` suffix.",
            "examples": [
              "2026-08-20T14:03:11Z"
            ]
          },
          "completed_at": {
            "type": "string",
            "format": "date-time",
            "description": "RFC 3339 timestamp in UTC, with a `Z` suffix.",
            "examples": [
              "2026-08-20T14:03:11Z"
            ]
          }
        }
      },
      "WebhookEndpoint": {
        "type": "object",
        "description": "A destination for event delivery. Payloads are signed; verify before trusting.",
        "required": [
          "id",
          "object",
          "url",
          "enabled_events",
          "status",
          "created_at"
        ],
        "additionalProperties": false,
        "properties": {
          "id": {
            "type": "string",
            "description": "Webhook endpoint identifier.",
            "pattern": "^whe_[0-9A-HJKMNP-TV-Z]{26}$",
            "examples": [
              "whe_01JBQ8Z5T7WXK9MNP2RSTVA3C4"
            ]
          },
          "object": {
            "type": "string",
            "const": "webhook_endpoint"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Must be HTTPS."
          },
          "enabled_events": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "string",
              "enum": [
                "job.succeeded",
                "job.failed",
                "job.cancelled",
                "file.ready",
                "file.rejected",
                "file.deleted",
                "file.expired"
              ]
            }
          },
          "status": {
            "type": "string",
            "enum": [
              "enabled",
              "disabled"
            ]
          },
          "secret": {
            "type": "string",
            "readOnly": true,
            "description": "Signing secret. Returned **once**, on creation, and never again. Store it before you close the response."
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "RFC 3339 timestamp in UTC, with a `Z` suffix.",
            "examples": [
              "2026-08-20T14:03:11Z"
            ]
          }
        }
      },
      "Usage": {
        "type": "object",
        "description": "Consumption for the current billing period. Authoritative for billing.",
        "required": [
          "object",
          "period_start",
          "period_end",
          "credits_included",
          "credits_used",
          "credits_remaining",
          "storage_bytes",
          "egress_bytes"
        ],
        "additionalProperties": false,
        "properties": {
          "object": {
            "type": "string",
            "const": "usage"
          },
          "period_start": {
            "type": "string",
            "format": "date-time",
            "description": "RFC 3339 timestamp in UTC, with a `Z` suffix.",
            "examples": [
              "2026-08-20T14:03:11Z"
            ]
          },
          "period_end": {
            "type": "string",
            "format": "date-time",
            "description": "RFC 3339 timestamp in UTC, with a `Z` suffix.",
            "examples": [
              "2026-08-20T14:03:11Z"
            ]
          },
          "credits_included": {
            "type": "integer",
            "minimum": 0
          },
          "credits_used": {
            "type": "integer",
            "minimum": 0
          },
          "credits_remaining": {
            "type": "integer",
            "minimum": 0
          },
          "storage_bytes": {
            "type": "integer",
            "minimum": 0
          },
          "egress_bytes": {
            "type": "integer",
            "minimum": 0
          },
          "breakdown": {
            "type": "array",
            "description": "Credits consumed per operation over the period.",
            "items": {
              "type": "object",
              "required": [
                "operation",
                "count",
                "credits"
              ],
              "additionalProperties": false,
              "properties": {
                "operation": {
                  "type": "string"
                },
                "count": {
                  "type": "integer",
                  "minimum": 0
                },
                "credits": {
                  "type": "integer",
                  "minimum": 0
                }
              }
            }
          }
        }
      },
      "Limits": {
        "type": "object",
        "required": [
          "object",
          "formats",
          "operations",
          "processing",
          "velocity"
        ],
        "additionalProperties": false,
        "properties": {
          "object": {
            "type": "string",
            "const": "limits"
          },
          "formats": {
            "type": "object",
            "required": [
              "supported_mime_types",
              "per_format"
            ],
            "additionalProperties": false,
            "properties": {
              "supported_mime_types": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "per_format": {
                "type": "object",
                "additionalProperties": {
                  "type": "object",
                  "required": [
                    "max_bytes",
                    "reason"
                  ],
                  "additionalProperties": false,
                  "properties": {
                    "max_bytes": {
                      "type": "integer"
                    },
                    "reason": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "operations": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "name",
                "class",
                "credits_per_megabyte",
                "summary"
              ],
              "additionalProperties": false,
              "properties": {
                "name": {
                  "type": "string"
                },
                "class": {
                  "type": "string",
                  "enum": [
                    "fast",
                    "standard",
                    "heavy"
                  ]
                },
                "credits_per_megabyte": {
                  "type": "integer"
                },
                "summary": {
                  "type": "string"
                }
              }
            }
          },
          "processing": {
            "type": "object",
            "additionalProperties": {
              "type": "integer"
            }
          },
          "velocity": {
            "type": "object",
            "additionalProperties": {
              "type": "integer"
            }
          }
        }
      },
      "Estimate": {
        "type": "object",
        "required": [
          "object",
          "operation",
          "class",
          "estimated_credits"
        ],
        "additionalProperties": false,
        "properties": {
          "object": {
            "type": "string",
            "const": "estimate"
          },
          "operation": {
            "type": "string"
          },
          "class": {
            "type": "string",
            "enum": [
              "fast",
              "standard",
              "heavy"
            ]
          },
          "estimated_credits": {
            "type": "integer"
          }
        }
      },
      "DownloadUrl": {
        "type": "object",
        "required": [
          "object",
          "url",
          "expires_at"
        ],
        "additionalProperties": false,
        "properties": {
          "object": {
            "type": "string",
            "const": "download_url"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Served from `cdn.fylane.dev`."
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "JobList": {
        "type": "object",
        "description": "A page of jobs.",
        "required": [
          "object",
          "data",
          "has_more"
        ],
        "additionalProperties": false,
        "properties": {
          "object": {
            "type": "string",
            "const": "list"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Job"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": "string",
            "description": "Pass as `starting_after` to fetch the next page. Absent or null on the last page."
          }
        }
      },
      "FileList": {
        "type": "object",
        "description": "A page of files.",
        "required": [
          "object",
          "data",
          "has_more"
        ],
        "additionalProperties": false,
        "properties": {
          "object": {
            "type": "string",
            "const": "list"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/File"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": "string",
            "description": "Pass as `starting_after` to fetch the next page. Absent or null on the last page."
          }
        }
      },
      "WebhookEndpointList": {
        "type": "object",
        "description": "A page of webhook endpoints.",
        "required": [
          "object",
          "data",
          "has_more"
        ],
        "additionalProperties": false,
        "properties": {
          "object": {
            "type": "string",
            "const": "list"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookEndpoint"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": "string",
            "description": "Pass as `starting_after` to fetch the next page. Absent or null on the last page."
          }
        }
      },
      "UploadParts": {
        "type": "object",
        "description": "Fresh pre-signed URLs for every part of a multipart session.",
        "required": [
          "object",
          "part_size",
          "parts"
        ],
        "additionalProperties": false,
        "properties": {
          "object": {
            "type": "string",
            "const": "upload_parts"
          },
          "part_size": {
            "type": "integer",
            "minimum": 5242880,
            "description": "Bytes per part; the last part may be smaller."
          },
          "parts": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "part_number",
                "url"
              ],
              "additionalProperties": false,
              "properties": {
                "part_number": {
                  "type": "integer",
                  "minimum": 1
                },
                "url": {
                  "type": "string",
                  "format": "uri"
                }
              }
            }
          }
        }
      },
      "Batch": {
        "type": "object",
        "description": "Many files, one operation, one handle. Members are ordinary jobs; the batch is bookkeeping over them, never a second execution path.",
        "required": [
          "id",
          "object",
          "total",
          "created_at"
        ],
        "additionalProperties": false,
        "properties": {
          "id": {
            "type": "string",
            "description": "Batch identifier.",
            "pattern": "^bat_[0-9A-HJKMNP-TV-Z]{26}$",
            "examples": [
              "bat_01JBQ8Z5T7WXK9MNP2RSTVA3C4"
            ]
          },
          "object": {
            "type": "string",
            "const": "batch"
          },
          "operation": {
            "type": "string"
          },
          "preset": {
            "type": "string"
          },
          "total": {
            "type": "integer",
            "minimum": 1,
            "description": "Number of member jobs."
          },
          "state": {
            "type": "string",
            "enum": [
              "processing",
              "completed"
            ],
            "description": "`completed` once every member is terminal. Individual failures are items, not a batch failure."
          },
          "state_counts": {
            "type": "object",
            "additionalProperties": {
              "type": "integer"
            },
            "description": "Members by state, e.g. `{\"succeeded\": 3, \"failed\": 1}`."
          },
          "credits_estimated": {
            "type": "integer"
          },
          "credits_charged": {
            "type": "integer",
            "description": "Only succeeded members are charged."
          },
          "items": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "job_id",
                "file_id",
                "state"
              ],
              "additionalProperties": false,
              "properties": {
                "job_id": {
                  "type": "string",
                  "description": "The member job.",
                  "pattern": "^job_[0-9A-HJKMNP-TV-Z]{26}$",
                  "examples": [
                    "job_01JBQ8Z5T7WXK9MNP2RSTVA3C4"
                  ]
                },
                "file_id": {
                  "type": "string",
                  "description": "The file it processes.",
                  "pattern": "^file_[0-9A-HJKMNP-TV-Z]{26}$",
                  "examples": [
                    "file_01JBQ8Z5T7WXK9MNP2RSTVA3C4"
                  ]
                },
                "state": {
                  "type": "string",
                  "enum": [
                    "queued",
                    "running",
                    "succeeded",
                    "failed",
                    "cancelled"
                  ]
                },
                "error_code": {
                  "description": "Why this member failed, when it did. `null` until then."
                }
              }
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "RFC 3339 timestamp in UTC, with a `Z` suffix.",
            "examples": [
              "2026-08-20T14:03:11Z"
            ]
          }
        }
      },
      "FileHistory": {
        "type": "object",
        "description": "What happened to a file, in order — assembled from the platform’s records.",
        "required": [
          "object",
          "file_id",
          "events"
        ],
        "additionalProperties": false,
        "properties": {
          "object": {
            "type": "string",
            "const": "file_history"
          },
          "file_id": {
            "type": "string",
            "description": "The file this history belongs to.",
            "pattern": "^file_[0-9A-HJKMNP-TV-Z]{26}$",
            "examples": [
              "file_01JBQ8Z5T7WXK9MNP2RSTVA3C4"
            ]
          },
          "events": {
            "type": "array",
            "description": "Oldest first.",
            "items": {
              "type": "object",
              "required": [
                "at",
                "event"
              ],
              "additionalProperties": false,
              "properties": {
                "at": {
                  "type": "string",
                  "format": "date-time",
                  "description": "RFC 3339 timestamp in UTC, with a `Z` suffix.",
                  "examples": [
                    "2026-08-20T14:03:11Z"
                  ]
                },
                "event": {
                  "type": "string",
                  "description": "`upload_opened`, `validated`, `rejected`, `job_created`, `job_succeeded`, `job_failed` or `job_cancelled`."
                },
                "detail": {
                  "type": "object",
                  "additionalProperties": true,
                  "description": "Event-specific facts: the detected type, the job and operation, credits charged, the error code."
                }
              }
            }
          }
        }
      }
    },
    "parameters": {
      "UploadId": {
        "name": "upload_id",
        "in": "path",
        "required": true,
        "description": "Upload session identifier.",
        "schema": {
          "type": "string",
          "description": "Upload session identifier.",
          "pattern": "^ups_[0-9A-HJKMNP-TV-Z]{26}$",
          "examples": [
            "ups_01JBQ8Z5T7WXK9MNP2RSTVA3C4"
          ]
        }
      },
      "JobId": {
        "name": "job_id",
        "in": "path",
        "required": true,
        "description": "Job identifier.",
        "schema": {
          "type": "string",
          "description": "Job identifier.",
          "pattern": "^job_[0-9A-HJKMNP-TV-Z]{26}$",
          "examples": [
            "job_01JBQ8Z5T7WXK9MNP2RSTVA3C4"
          ]
        }
      },
      "FileId": {
        "name": "file_id",
        "in": "path",
        "required": true,
        "description": "File identifier.",
        "schema": {
          "type": "string",
          "description": "File identifier.",
          "pattern": "^file_[0-9A-HJKMNP-TV-Z]{26}$",
          "examples": [
            "file_01JBQ8Z5T7WXK9MNP2RSTVA3C4"
          ]
        }
      },
      "WebhookEndpointId": {
        "name": "webhook_endpoint_id",
        "in": "path",
        "required": true,
        "description": "Webhook endpoint identifier.",
        "schema": {
          "type": "string",
          "description": "Webhook endpoint identifier.",
          "pattern": "^whe_[0-9A-HJKMNP-TV-Z]{26}$",
          "examples": [
            "whe_01JBQ8Z5T7WXK9MNP2RSTVA3C4"
          ]
        }
      },
      "BatchId": {
        "name": "batch_id",
        "in": "path",
        "required": true,
        "description": "Batch identifier.",
        "schema": {
          "type": "string",
          "description": "Batch identifier.",
          "pattern": "^bat_[0-9A-HJKMNP-TV-Z]{26}$",
          "examples": [
            "bat_01JBQ8Z5T7WXK9MNP2RSTVA3C4"
          ]
        }
      },
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": true,
        "description": "Client-generated key, scoped to (organization, endpoint) and retained 24 hours. A replay with the same body returns the stored response without re-executing or re-billing; a replay with a different body returns 409.",
        "schema": {
          "type": "string",
          "minLength": 8,
          "maxLength": 255
        }
      },
      "Limit": {
        "name": "limit",
        "in": "query",
        "description": "Page size.",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100,
          "default": 50
        }
      },
      "StartingAfter": {
        "name": "starting_after",
        "in": "query",
        "description": "Cursor from the previous page’s `next_cursor`.",
        "schema": {
          "type": "string"
        }
      }
    },
    "headers": {
      "X-Request-Id": {
        "description": "Correlation id for this request. Quote it in support requests.",
        "schema": {
          "type": "string",
          "pattern": "^req_[0-9A-HJKMNP-TV-Z]{26}$"
        }
      },
      "X-RateLimit-Limit": {
        "description": "Requests permitted in the current window.",
        "schema": {
          "type": "integer",
          "minimum": 0
        }
      },
      "X-RateLimit-Remaining": {
        "description": "Requests remaining in the current window.",
        "schema": {
          "type": "integer",
          "minimum": 0
        }
      },
      "X-RateLimit-Reset": {
        "description": "Unix seconds at which the window resets.",
        "schema": {
          "type": "integer",
          "minimum": 0
        }
      },
      "Retry-After": {
        "description": "Seconds to wait before retrying.",
        "schema": {
          "type": "integer",
          "minimum": 0
        }
      }
    },
    "responses": {
      "Error400": {
        "description": "Codes: invalid_request, unsupported_file, file_too_large, corrupted_file, encrypted_file, unsupported_operation.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "invalid_request": {
                "summary": "The request is malformed or missing required fields.",
                "value": {
                  "type": "https://fylane.dev/errors/invalid-request",
                  "code": "invalid_request",
                  "message": "The request is malformed or missing required fields.",
                  "retryable": false,
                  "request_id": "req_01JBQ8Z5T7WXK9MNP2RSTVA3C4",
                  "docs_url": "https://fylane.dev/docs/errors/invalid-request"
                }
              },
              "unsupported_file": {
                "summary": "This file format is not supported.",
                "value": {
                  "type": "https://fylane.dev/errors/unsupported-file",
                  "code": "unsupported_file",
                  "message": "This file format is not supported.",
                  "retryable": false,
                  "request_id": "req_01JBQ8Z5T7WXK9MNP2RSTVA3C4",
                  "docs_url": "https://fylane.dev/docs/errors/unsupported-file"
                }
              },
              "file_too_large": {
                "summary": "The file exceeds the maximum size for your plan.",
                "value": {
                  "type": "https://fylane.dev/errors/file-too-large",
                  "code": "file_too_large",
                  "message": "The file exceeds the maximum size for your plan.",
                  "retryable": false,
                  "request_id": "req_01JBQ8Z5T7WXK9MNP2RSTVA3C4",
                  "docs_url": "https://fylane.dev/docs/errors/file-too-large"
                }
              },
              "corrupted_file": {
                "summary": "The file could not be parsed and appears to be corrupted.",
                "value": {
                  "type": "https://fylane.dev/errors/corrupted-file",
                  "code": "corrupted_file",
                  "message": "The file could not be parsed and appears to be corrupted.",
                  "retryable": false,
                  "request_id": "req_01JBQ8Z5T7WXK9MNP2RSTVA3C4",
                  "docs_url": "https://fylane.dev/docs/errors/corrupted-file"
                }
              },
              "encrypted_file": {
                "summary": "The file is password-protected and cannot be processed.",
                "value": {
                  "type": "https://fylane.dev/errors/encrypted-file",
                  "code": "encrypted_file",
                  "message": "The file is password-protected and cannot be processed.",
                  "retryable": false,
                  "request_id": "req_01JBQ8Z5T7WXK9MNP2RSTVA3C4",
                  "docs_url": "https://fylane.dev/docs/errors/encrypted-file"
                }
              },
              "unsupported_operation": {
                "summary": "This operation is not available for this file type.",
                "value": {
                  "type": "https://fylane.dev/errors/unsupported-operation",
                  "code": "unsupported_operation",
                  "message": "This operation is not available for this file type.",
                  "retryable": false,
                  "request_id": "req_01JBQ8Z5T7WXK9MNP2RSTVA3C4",
                  "docs_url": "https://fylane.dev/docs/errors/unsupported-operation"
                }
              }
            }
          }
        }
      },
      "Error401": {
        "description": "Codes: missing_credentials, invalid_api_key, expired_api_key, revoked_api_key, api_key_in_query.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "missing_credentials": {
                "summary": "No API key was provided.",
                "value": {
                  "type": "https://fylane.dev/errors/missing-credentials",
                  "code": "missing_credentials",
                  "message": "No API key was provided.",
                  "retryable": false,
                  "request_id": "req_01JBQ8Z5T7WXK9MNP2RSTVA3C4",
                  "docs_url": "https://fylane.dev/docs/errors/missing-credentials"
                }
              },
              "invalid_api_key": {
                "summary": "The API key is not valid.",
                "value": {
                  "type": "https://fylane.dev/errors/invalid-api-key",
                  "code": "invalid_api_key",
                  "message": "The API key is not valid.",
                  "retryable": false,
                  "request_id": "req_01JBQ8Z5T7WXK9MNP2RSTVA3C4",
                  "docs_url": "https://fylane.dev/docs/errors/invalid-api-key"
                }
              },
              "expired_api_key": {
                "summary": "This API key has expired.",
                "value": {
                  "type": "https://fylane.dev/errors/expired-api-key",
                  "code": "expired_api_key",
                  "message": "This API key has expired.",
                  "retryable": false,
                  "request_id": "req_01JBQ8Z5T7WXK9MNP2RSTVA3C4",
                  "docs_url": "https://fylane.dev/docs/errors/expired-api-key"
                }
              },
              "revoked_api_key": {
                "summary": "This API key has been revoked.",
                "value": {
                  "type": "https://fylane.dev/errors/revoked-api-key",
                  "code": "revoked_api_key",
                  "message": "This API key has been revoked.",
                  "retryable": false,
                  "request_id": "req_01JBQ8Z5T7WXK9MNP2RSTVA3C4",
                  "docs_url": "https://fylane.dev/docs/errors/revoked-api-key"
                }
              },
              "api_key_in_query": {
                "summary": "API keys must be sent in the Authorization header, never in the URL.",
                "value": {
                  "type": "https://fylane.dev/errors/api-key-in-query",
                  "code": "api_key_in_query",
                  "message": "API keys must be sent in the Authorization header, never in the URL.",
                  "retryable": false,
                  "request_id": "req_01JBQ8Z5T7WXK9MNP2RSTVA3C4",
                  "docs_url": "https://fylane.dev/docs/errors/api-key-in-query"
                }
              }
            }
          }
        }
      },
      "Error402": {
        "description": "Codes: payment_required.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "payment_required": {
                "summary": "Payment for this organization has failed; processing is paused until it succeeds.",
                "value": {
                  "type": "https://fylane.dev/errors/payment-required",
                  "code": "payment_required",
                  "message": "Payment for this organization has failed; processing is paused until it succeeds.",
                  "retryable": false,
                  "request_id": "req_01JBQ8Z5T7WXK9MNP2RSTVA3C4",
                  "docs_url": "https://fylane.dev/docs/errors/payment-required"
                }
              }
            }
          }
        }
      },
      "Error403": {
        "description": "Codes: insufficient_scope, ip_not_allowed, environment_mismatch, organization_suspended, forbidden, key_restricted.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "insufficient_scope": {
                "summary": "This API key does not have the required scope.",
                "value": {
                  "type": "https://fylane.dev/errors/insufficient-scope",
                  "code": "insufficient_scope",
                  "message": "This API key does not have the required scope.",
                  "retryable": false,
                  "request_id": "req_01JBQ8Z5T7WXK9MNP2RSTVA3C4",
                  "docs_url": "https://fylane.dev/docs/errors/insufficient-scope"
                }
              },
              "ip_not_allowed": {
                "summary": "This API key is restricted to a set of IP addresses that does not include this one.",
                "value": {
                  "type": "https://fylane.dev/errors/ip-not-allowed",
                  "code": "ip_not_allowed",
                  "message": "This API key is restricted to a set of IP addresses that does not include this one.",
                  "retryable": false,
                  "request_id": "req_01JBQ8Z5T7WXK9MNP2RSTVA3C4",
                  "docs_url": "https://fylane.dev/docs/errors/ip-not-allowed"
                }
              },
              "environment_mismatch": {
                "summary": "A test key cannot access live resources, and vice versa.",
                "value": {
                  "type": "https://fylane.dev/errors/environment-mismatch",
                  "code": "environment_mismatch",
                  "message": "A test key cannot access live resources, and vice versa.",
                  "retryable": false,
                  "request_id": "req_01JBQ8Z5T7WXK9MNP2RSTVA3C4",
                  "docs_url": "https://fylane.dev/docs/errors/environment-mismatch"
                }
              },
              "organization_suspended": {
                "summary": "This organization has been suspended.",
                "value": {
                  "type": "https://fylane.dev/errors/organization-suspended",
                  "code": "organization_suspended",
                  "message": "This organization has been suspended.",
                  "retryable": false,
                  "request_id": "req_01JBQ8Z5T7WXK9MNP2RSTVA3C4",
                  "docs_url": "https://fylane.dev/docs/errors/organization-suspended"
                }
              },
              "forbidden": {
                "summary": "You do not have permission to perform this action.",
                "value": {
                  "type": "https://fylane.dev/errors/forbidden",
                  "code": "forbidden",
                  "message": "You do not have permission to perform this action.",
                  "retryable": false,
                  "request_id": "req_01JBQ8Z5T7WXK9MNP2RSTVA3C4",
                  "docs_url": "https://fylane.dev/docs/errors/forbidden"
                }
              },
              "key_restricted": {
                "summary": "This API key is not allowed to run this operation.",
                "value": {
                  "type": "https://fylane.dev/errors/key-restricted",
                  "code": "key_restricted",
                  "message": "This API key is not allowed to run this operation.",
                  "retryable": false,
                  "request_id": "req_01JBQ8Z5T7WXK9MNP2RSTVA3C4",
                  "docs_url": "https://fylane.dev/docs/errors/key-restricted"
                }
              }
            }
          }
        }
      },
      "Error404": {
        "description": "Not found. A resource belonging to another organization returns 404, never 403 — a 403 would confirm it exists. Codes: not_found.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "not_found": {
                "summary": "No such resource.",
                "value": {
                  "type": "https://fylane.dev/errors/not-found",
                  "code": "not_found",
                  "message": "No such resource.",
                  "retryable": false,
                  "request_id": "req_01JBQ8Z5T7WXK9MNP2RSTVA3C4",
                  "docs_url": "https://fylane.dev/docs/errors/not-found"
                }
              }
            }
          }
        }
      },
      "Error409": {
        "description": "Codes: idempotency_conflict, upload_session_consumed, conflict.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "idempotency_conflict": {
                "summary": "This Idempotency-Key was already used with a different request body.",
                "value": {
                  "type": "https://fylane.dev/errors/idempotency-conflict",
                  "code": "idempotency_conflict",
                  "message": "This Idempotency-Key was already used with a different request body.",
                  "retryable": false,
                  "request_id": "req_01JBQ8Z5T7WXK9MNP2RSTVA3C4",
                  "docs_url": "https://fylane.dev/docs/errors/idempotency-conflict"
                }
              },
              "upload_session_consumed": {
                "summary": "This upload session has already been used.",
                "value": {
                  "type": "https://fylane.dev/errors/upload-session-consumed",
                  "code": "upload_session_consumed",
                  "message": "This upload session has already been used.",
                  "retryable": false,
                  "request_id": "req_01JBQ8Z5T7WXK9MNP2RSTVA3C4",
                  "docs_url": "https://fylane.dev/docs/errors/upload-session-consumed"
                }
              },
              "conflict": {
                "summary": "The resource is in a state that does not allow this operation.",
                "value": {
                  "type": "https://fylane.dev/errors/conflict",
                  "code": "conflict",
                  "message": "The resource is in a state that does not allow this operation.",
                  "retryable": false,
                  "request_id": "req_01JBQ8Z5T7WXK9MNP2RSTVA3C4",
                  "docs_url": "https://fylane.dev/docs/errors/conflict"
                }
              }
            }
          }
        }
      },
      "Error413": {
        "description": "Codes: payload_too_large.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "payload_too_large": {
                "summary": "The request body is too large.",
                "value": {
                  "type": "https://fylane.dev/errors/payload-too-large",
                  "code": "payload_too_large",
                  "message": "The request body is too large.",
                  "retryable": false,
                  "request_id": "req_01JBQ8Z5T7WXK9MNP2RSTVA3C4",
                  "docs_url": "https://fylane.dev/docs/errors/payload-too-large"
                }
              }
            }
          }
        }
      },
      "Error422": {
        "description": "Codes: malware_detected, policy_violation, validation_failed, output_validation_failed, unsupported_input.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "malware_detected": {
                "summary": "The file was rejected because it failed a security scan.",
                "value": {
                  "type": "https://fylane.dev/errors/malware-detected",
                  "code": "malware_detected",
                  "message": "The file was rejected because it failed a security scan.",
                  "retryable": false,
                  "request_id": "req_01JBQ8Z5T7WXK9MNP2RSTVA3C4",
                  "docs_url": "https://fylane.dev/docs/errors/malware-detected"
                }
              },
              "policy_violation": {
                "summary": "The file violates the acceptable use policy.",
                "value": {
                  "type": "https://fylane.dev/errors/policy-violation",
                  "code": "policy_violation",
                  "message": "The file violates the acceptable use policy.",
                  "retryable": false,
                  "request_id": "req_01JBQ8Z5T7WXK9MNP2RSTVA3C4",
                  "docs_url": "https://fylane.dev/docs/errors/policy-violation"
                }
              },
              "validation_failed": {
                "summary": "The file does not meet the validation policy supplied with the upload.",
                "value": {
                  "type": "https://fylane.dev/errors/validation-failed",
                  "code": "validation_failed",
                  "message": "The file does not meet the validation policy supplied with the upload.",
                  "retryable": false,
                  "request_id": "req_01JBQ8Z5T7WXK9MNP2RSTVA3C4",
                  "docs_url": "https://fylane.dev/docs/errors/validation-failed"
                }
              },
              "output_validation_failed": {
                "summary": "Processing produced output that failed verification, so it was discarded.",
                "value": {
                  "type": "https://fylane.dev/errors/output-validation-failed",
                  "code": "output_validation_failed",
                  "message": "Processing produced output that failed verification, so it was discarded.",
                  "retryable": false,
                  "request_id": "req_01JBQ8Z5T7WXK9MNP2RSTVA3C4",
                  "docs_url": "https://fylane.dev/docs/errors/output-validation-failed"
                }
              },
              "unsupported_input": {
                "summary": "This file is not something the requested operation can process.",
                "value": {
                  "type": "https://fylane.dev/errors/unsupported-input",
                  "code": "unsupported_input",
                  "message": "This file is not something the requested operation can process.",
                  "retryable": false,
                  "request_id": "req_01JBQ8Z5T7WXK9MNP2RSTVA3C4",
                  "docs_url": "https://fylane.dev/docs/errors/unsupported-input"
                }
              }
            }
          }
        }
      },
      "Error429": {
        "description": "Rate limited or out of credits. Retry after the interval in `Retry-After`. Codes: key_spend_limit, rate_limited, concurrency_limit, credits_exhausted.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          },
          "Retry-After": {
            "$ref": "#/components/headers/Retry-After"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "key_spend_limit": {
                "summary": "This API key has reached its credit spend cap for the current window.",
                "value": {
                  "type": "https://fylane.dev/errors/key-spend-limit",
                  "code": "key_spend_limit",
                  "message": "This API key has reached its credit spend cap for the current window.",
                  "retryable": false,
                  "request_id": "req_01JBQ8Z5T7WXK9MNP2RSTVA3C4",
                  "docs_url": "https://fylane.dev/docs/errors/key-spend-limit",
                  "retry_after": 30
                }
              },
              "rate_limited": {
                "summary": "Too many requests. Retry after the interval in the Retry-After header.",
                "value": {
                  "type": "https://fylane.dev/errors/rate-limited",
                  "code": "rate_limited",
                  "message": "Too many requests. Retry after the interval in the Retry-After header.",
                  "retryable": true,
                  "request_id": "req_01JBQ8Z5T7WXK9MNP2RSTVA3C4",
                  "docs_url": "https://fylane.dev/docs/errors/rate-limited",
                  "retry_after": 30
                }
              },
              "concurrency_limit": {
                "summary": "Too many jobs are running concurrently for this organization.",
                "value": {
                  "type": "https://fylane.dev/errors/concurrency-limit",
                  "code": "concurrency_limit",
                  "message": "Too many jobs are running concurrently for this organization.",
                  "retryable": true,
                  "request_id": "req_01JBQ8Z5T7WXK9MNP2RSTVA3C4",
                  "docs_url": "https://fylane.dev/docs/errors/concurrency-limit",
                  "retry_after": 30
                }
              },
              "credits_exhausted": {
                "summary": "This organization has no processing credits remaining.",
                "value": {
                  "type": "https://fylane.dev/errors/credits-exhausted",
                  "code": "credits_exhausted",
                  "message": "This organization has no processing credits remaining.",
                  "retryable": false,
                  "request_id": "req_01JBQ8Z5T7WXK9MNP2RSTVA3C4",
                  "docs_url": "https://fylane.dev/docs/errors/credits-exhausted",
                  "retry_after": 30
                }
              }
            }
          }
        }
      },
      "Error500": {
        "description": "Codes: internal_error.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "internal_error": {
                "summary": "An unexpected error occurred on our side.",
                "value": {
                  "type": "https://fylane.dev/errors/internal-error",
                  "code": "internal_error",
                  "message": "An unexpected error occurred on our side.",
                  "retryable": true,
                  "request_id": "req_01JBQ8Z5T7WXK9MNP2RSTVA3C4",
                  "docs_url": "https://fylane.dev/docs/errors/internal-error"
                }
              }
            }
          }
        }
      },
      "Error503": {
        "description": "Codes: processing_unavailable, dependency_unavailable.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "processing_unavailable": {
                "summary": "Processing capacity is temporarily unavailable.",
                "value": {
                  "type": "https://fylane.dev/errors/processing-unavailable",
                  "code": "processing_unavailable",
                  "message": "Processing capacity is temporarily unavailable.",
                  "retryable": true,
                  "request_id": "req_01JBQ8Z5T7WXK9MNP2RSTVA3C4",
                  "docs_url": "https://fylane.dev/docs/errors/processing-unavailable"
                }
              },
              "dependency_unavailable": {
                "summary": "A required service is temporarily unavailable.",
                "value": {
                  "type": "https://fylane.dev/errors/dependency-unavailable",
                  "code": "dependency_unavailable",
                  "message": "A required service is temporarily unavailable.",
                  "retryable": true,
                  "request_id": "req_01JBQ8Z5T7WXK9MNP2RSTVA3C4",
                  "docs_url": "https://fylane.dev/docs/errors/dependency-unavailable"
                }
              }
            }
          }
        }
      },
      "Error504": {
        "description": "Codes: timeout.",
        "headers": {
          "X-Request-Id": {
            "$ref": "#/components/headers/X-Request-Id"
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "examples": {
              "timeout": {
                "summary": "The operation took too long and was stopped.",
                "value": {
                  "type": "https://fylane.dev/errors/timeout",
                  "code": "timeout",
                  "message": "The operation took too long and was stopped.",
                  "retryable": true,
                  "request_id": "req_01JBQ8Z5T7WXK9MNP2RSTVA3C4",
                  "docs_url": "https://fylane.dev/docs/errors/timeout"
                }
              }
            }
          }
        }
      }
    },
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "fy_live_… or fy_test_…",
        "description": "A project API key. `fy_test_` keys never touch production data and never bill. The SDK refuses to run if it finds an `fy_live_` key in a browser bundle — so should yours."
      }
    }
  }
}
