openapi: 3.1.0
info:
  title: GetFileURL API
  version: 1.0.0
  summary: Upload files and return controlled public file URLs.
servers:
  - url: https://api.getfileurl.com
paths:
  /v1/files:
    get:
      operationId: listFiles
      summary: List file metadata for the authenticated workspace.
      security:
        - bearerApiKey: []
      parameters:
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 50
        - name: cursor
          in: query
          required: false
          schema:
            type: string
          description: Cursor returned as `next_cursor` from the previous response.
      responses:
        "200":
          description: File metadata page.
          headers:
            X-GetFileURL-API-Version:
              $ref: "#/components/headers/ApiVersion"
            Deprecation:
              $ref: "#/components/headers/Deprecation"
            Sunset:
              $ref: "#/components/headers/Sunset"
            Link:
              $ref: "#/components/headers/Link"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/FileListResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "429":
          $ref: "#/components/responses/RateLimited"
        "410":
          $ref: "#/components/responses/ApiVersionRetired"
        "500":
          $ref: "#/components/responses/InternalError"
    post:
      operationId: uploadFile
      summary: Upload a file and receive a controlled public file URL.
      security:
        - bearerApiKey: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              required:
                - file
              properties:
                file:
                  type: string
                  format: binary
                visibility:
                  type: string
                  enum:
                    - public
                    - signed
                  default: public
                  description: Use `signed` when the returned CDN URL must include a delivery token.
      responses:
        "201":
          description: File accepted and available through GetFileURL delivery.
          headers:
            X-GetFileURL-API-Version:
              $ref: "#/components/headers/ApiVersion"
            Deprecation:
              $ref: "#/components/headers/Deprecation"
            Sunset:
              $ref: "#/components/headers/Sunset"
            Link:
              $ref: "#/components/headers/Link"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/FileUploadResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "413":
          $ref: "#/components/responses/FileTooLarge"
        "415":
          $ref: "#/components/responses/UnsupportedFileType"
        "429":
          $ref: "#/components/responses/RateLimited"
        "410":
          $ref: "#/components/responses/ApiVersionRetired"
        "500":
          $ref: "#/components/responses/InternalError"
  /v1/files/from-url:
    post:
      operationId: uploadFileFromUrl
      summary: Fetch a remote HTTPS URL into GetFileURL and return a controlled public file URL.
      description: Remote URL uploads require HTTPS, no URL credentials, standard port 443, an allowed public host, and a valid Content-Length header before quota is reserved.
      security:
        - bearerApiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - url
              properties:
                url:
                  type: string
                  format: uri
                  description: HTTPS source URL to fetch. Redirects are followed only when each target passes the same outbound URL policy.
                filename:
                  type: string
                  description: Optional filename override used for file policy and metadata.
                visibility:
                  type: string
                  enum:
                    - public
                    - signed
                  default: public
      responses:
        "201":
          description: Remote file fetched, accepted, and available through GetFileURL delivery.
          headers:
            X-GetFileURL-API-Version:
              $ref: "#/components/headers/ApiVersion"
            Deprecation:
              $ref: "#/components/headers/Deprecation"
            Sunset:
              $ref: "#/components/headers/Sunset"
            Link:
              $ref: "#/components/headers/Link"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/FileUploadResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "413":
          $ref: "#/components/responses/FileTooLarge"
        "415":
          $ref: "#/components/responses/UnsupportedFileType"
        "429":
          $ref: "#/components/responses/RateLimited"
        "410":
          $ref: "#/components/responses/ApiVersionRetired"
        "500":
          $ref: "#/components/responses/InternalError"
  /v1/upload-sessions:
    post:
      operationId: createUploadSession
      summary: Create a bounded multipart upload session for files larger than the standard upload limit.
      description: Creates a Convex-controlled upload session and an R2 multipart upload. The API key remains server-side; clients upload each part through GetFileURL's Upload/API Worker rather than receiving raw public R2 credentials or URLs.
      security:
        - bearerApiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UploadSessionCreateRequest"
      responses:
        "201":
          description: Upload session created. Upload every part with `PUT /v1/upload-sessions/{upload_session_id}/parts/{part_number}`, then call complete.
          headers:
            X-GetFileURL-API-Version:
              $ref: "#/components/headers/ApiVersion"
            Deprecation:
              $ref: "#/components/headers/Deprecation"
            Sunset:
              $ref: "#/components/headers/Sunset"
            Link:
              $ref: "#/components/headers/Link"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UploadSessionCreateResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "402":
          $ref: "#/components/responses/QuotaExceeded"
        "413":
          $ref: "#/components/responses/FileTooLarge"
        "415":
          $ref: "#/components/responses/UnsupportedFileType"
        "429":
          $ref: "#/components/responses/RateLimited"
        "410":
          $ref: "#/components/responses/ApiVersionRetired"
        "500":
          $ref: "#/components/responses/InternalError"
  /v1/upload-sessions/{upload_session_id}/parts/{part_number}:
    put:
      operationId: uploadUploadSessionPart
      summary: Upload one part for a multipart upload session.
      description: Uploads a single binary part through the Upload/API Worker. The request must include a valid `Content-Length`; all non-final parts must match the session `part_size`, and the final part may be smaller.
      security:
        - bearerApiKey: []
      parameters:
        - name: upload_session_id
          in: path
          required: true
          schema:
            type: string
          examples:
            sessionId:
              value: us_01hxyz
        - name: part_number
          in: path
          required: true
          schema:
            type: integer
            minimum: 1
          examples:
            firstPart:
              value: 1
        - name: Content-Length
          in: header
          required: true
          schema:
            type: integer
            minimum: 1
          description: Exact byte length of this part.
      requestBody:
        required: true
        content:
          application/octet-stream:
            schema:
              type: string
              format: binary
      responses:
        "200":
          description: Part accepted and recorded.
          headers:
            X-GetFileURL-API-Version:
              $ref: "#/components/headers/ApiVersion"
            Deprecation:
              $ref: "#/components/headers/Deprecation"
            Sunset:
              $ref: "#/components/headers/Sunset"
            Link:
              $ref: "#/components/headers/Link"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UploadSessionPartUploadResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/FileNotFound"
        "429":
          $ref: "#/components/responses/RateLimited"
        "410":
          $ref: "#/components/responses/ApiVersionRetired"
        "500":
          $ref: "#/components/responses/InternalError"
  /v1/upload-sessions/{upload_session_id}/complete:
    post:
      operationId: completeUploadSession
      summary: Complete a multipart upload session and return the controlled file URL.
      description: Completes the R2 multipart upload, verifies the completed object size against the declared session size, finalizes Convex file state, and returns the same file response shape as standard upload.
      security:
        - bearerApiKey: []
      parameters:
        - name: upload_session_id
          in: path
          required: true
          schema:
            type: string
          examples:
            sessionId:
              value: us_01hxyz
      responses:
        "201":
          description: Upload session completed and file accepted by GetFileURL delivery.
          headers:
            X-GetFileURL-API-Version:
              $ref: "#/components/headers/ApiVersion"
            Deprecation:
              $ref: "#/components/headers/Deprecation"
            Sunset:
              $ref: "#/components/headers/Sunset"
            Link:
              $ref: "#/components/headers/Link"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/FileUploadResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/FileNotFound"
        "429":
          $ref: "#/components/responses/RateLimited"
        "410":
          $ref: "#/components/responses/ApiVersionRetired"
        "500":
          $ref: "#/components/responses/InternalError"
  /v1/diagnostics/url:
    post:
      operationId: diagnoseUrl
      summary: Check whether a remote HTTPS URL can be used by GetFileURL remote ingest.
      description: URL diagnostics use the same outbound URL policy as remote URL ingest. The submitted URL is returned sanitized to the caller, but Convex diagnostic history stores only hostnames and URL hashes.
      security:
        - bearerApiKey: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - url
              properties:
                url:
                  type: string
                  format: uri
                  description: HTTPS URL to check. Redirects are followed only when each target passes outbound URL policy.
      responses:
        "200":
          description: URL diagnostic result. Target failures are represented by the `status` field, not by a non-2xx API response.
          headers:
            X-GetFileURL-API-Version:
              $ref: "#/components/headers/ApiVersion"
            Deprecation:
              $ref: "#/components/headers/Deprecation"
            Sunset:
              $ref: "#/components/headers/Sunset"
            Link:
              $ref: "#/components/headers/Link"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UrlDiagnosticResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "429":
          $ref: "#/components/responses/RateLimited"
        "410":
          $ref: "#/components/responses/ApiVersionRetired"
        "500":
          $ref: "#/components/responses/InternalError"
  /v1/files/{file_id}/signed-url:
    post:
      operationId: createSignedDeliveryUrl
      summary: Create a signed CDN delivery URL and require signed delivery for the file.
      security:
        - bearerApiKey: []
      parameters:
        - name: file_id
          in: path
          required: true
          schema:
            type: string
          examples:
            publicId:
              value: file_01hxyz
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                ttl_seconds:
                  type: integer
                  minimum: 60
                  maximum: 604800
                  default: 86400
      responses:
        "201":
          description: Signed delivery URL created. The raw token is returned once in the URL and is not stored by GetFileURL.
          headers:
            X-GetFileURL-API-Version:
              $ref: "#/components/headers/ApiVersion"
            Deprecation:
              $ref: "#/components/headers/Deprecation"
            Sunset:
              $ref: "#/components/headers/Sunset"
            Link:
              $ref: "#/components/headers/Link"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/SignedDeliveryUrlResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/FileNotFound"
        "429":
          $ref: "#/components/responses/RateLimited"
        "410":
          $ref: "#/components/responses/ApiVersionRetired"
        "500":
          $ref: "#/components/responses/InternalError"
  /v1/files/{file_id}:
    get:
      operationId: getFile
      summary: Retrieve file metadata for one file in the authenticated workspace.
      security:
        - bearerApiKey: []
      parameters:
        - name: file_id
          in: path
          required: true
          schema:
            type: string
          examples:
            publicId:
              value: file_01hxyz
      responses:
        "200":
          description: File metadata.
          headers:
            X-GetFileURL-API-Version:
              $ref: "#/components/headers/ApiVersion"
            Deprecation:
              $ref: "#/components/headers/Deprecation"
            Sunset:
              $ref: "#/components/headers/Sunset"
            Link:
              $ref: "#/components/headers/Link"
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/FileMetadata"
                  - type: object
                    required:
                      - request_id
                    properties:
                      request_id:
                        type: string
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/FileNotFound"
        "429":
          $ref: "#/components/responses/RateLimited"
        "410":
          $ref: "#/components/responses/ApiVersionRetired"
        "500":
          $ref: "#/components/responses/InternalError"
    delete:
      operationId: deleteFile
      summary: Delete a file and revoke its public file URL.
      security:
        - bearerApiKey: []
      parameters:
        - name: file_id
          in: path
          required: true
          schema:
            type: string
          examples:
            publicId:
              value: file_01hxyz
      responses:
        "204":
          description: File was deleted, the public URL was revoked, and private bytes were purged or were already unavailable.
          headers:
            X-GetFileURL-API-Version:
              $ref: "#/components/headers/ApiVersion"
            Deprecation:
              $ref: "#/components/headers/Deprecation"
            Sunset:
              $ref: "#/components/headers/Sunset"
            Link:
              $ref: "#/components/headers/Link"
        "202":
          description: Public access was revoked, but private byte purge is pending the scheduled retry sweep.
          headers:
            X-GetFileURL-API-Version:
              $ref: "#/components/headers/ApiVersion"
            Deprecation:
              $ref: "#/components/headers/Deprecation"
            Sunset:
              $ref: "#/components/headers/Sunset"
            Link:
              $ref: "#/components/headers/Link"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DeletePendingResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/FileNotFound"
        "429":
          $ref: "#/components/responses/RateLimited"
        "410":
          $ref: "#/components/responses/ApiVersionRetired"
        "500":
          $ref: "#/components/responses/InternalError"
  /v1/usage:
    get:
      operationId: getWorkspaceUsage
      summary: Retrieve sanitized usage and limits for the authenticated workspace.
      description: Returns Convex source-of-truth usage rollups, workspace usage state, plan limits, grace limits, and support target for the API key's workspace. It does not expose Stripe identifiers, billing provider records, private object keys, raw customer file URLs, or customer file contents.
      security:
        - bearerApiKey: []
      responses:
        "200":
          description: Workspace usage snapshot.
          headers:
            X-GetFileURL-API-Version:
              $ref: "#/components/headers/ApiVersion"
            Deprecation:
              $ref: "#/components/headers/Deprecation"
            Sunset:
              $ref: "#/components/headers/Sunset"
            Link:
              $ref: "#/components/headers/Link"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WorkspaceUsageResponse"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/WorkspaceNotFound"
        "429":
          $ref: "#/components/responses/RateLimited"
        "410":
          $ref: "#/components/responses/ApiVersionRetired"
        "500":
          $ref: "#/components/responses/InternalError"
webhooks:
  getfileurlEvent:
    post:
      operationId: receiveGetFileURLEvent
      summary: Receive a signed GetFileURL lifecycle or usage event.
      parameters:
        - name: getfileurl-event-id
          in: header
          required: true
          schema:
            type: string
        - name: getfileurl-delivery-id
          in: header
          required: true
          schema:
            type: string
        - name: getfileurl-timestamp
          in: header
          required: true
          schema:
            type: string
        - name: getfileurl-signature
          in: header
          required: true
          schema:
            type: string
          description: HMAC-SHA256 signature over `timestamp.payload` with the endpoint signing secret.
        - name: getfileurl-schema-version
          in: header
          required: true
          schema:
            type: string
            enum:
              - v1
        - name: getfileurl-api-version
          in: header
          required: true
          schema:
            type: string
            enum:
              - v1
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/WebhookEvent"
      responses:
        "200":
          description: Event accepted.
        "204":
          description: Event accepted with no response body.
components:
  headers:
    ApiVersion:
      description: Public API major version that handled the request.
      schema:
        type: string
        enum:
          - v1
    Deprecation:
      description: Present with value "true" when the API version is deprecated.
      schema:
        type: string
        examples:
          - "true"
    Sunset:
      description: HTTP date when the deprecated API version is scheduled to stop serving.
      schema:
        type: string
        format: http-date
    Link:
      description: Migration or deprecation documentation link.
      schema:
        type: string
  securitySchemes:
    bearerApiKey:
      type: http
      scheme: bearer
      description: GetFileURL API key, not a Clerk session token.
  schemas:
    FileUploadResponse:
      type: object
      required:
        - file_id
        - url
        - delete_url
        - content_type
        - size
        - status
        - file_handling_class
        - delivery_disposition
        - visibility
        - upload_source
        - scan_policy
        - scan_status
        - expires_at
        - request_id
      properties:
        file_id:
          type: string
          examples:
            - file_01hxyz
        url:
          type: string
          format: uri
          examples:
            - https://cdn.getfileurl.com/f/file_01hxyz
        delete_url:
          type: string
          format: uri
          examples:
            - https://api.getfileurl.com/v1/files/file_01hxyz
        content_type:
          type: string
          examples:
            - image/png
        size:
          type: integer
          minimum: 0
        status:
          type: string
          enum:
            - ready
            - scanning
          description: Delivery lifecycle state after upload finalization.
        file_handling_class:
          type: string
          enum:
            - inline
            - attachment
            - scan_gated
        delivery_disposition:
          type: string
          enum:
            - inline
            - attachment
        visibility:
          type: string
          enum:
            - public
            - signed
        upload_source:
          type: string
          enum:
            - direct_upload
            - remote_url
        source_url_host:
          type: string
          description: Present only for `remote_url` uploads. Raw source URLs are not returned.
        scan_policy:
          type: string
          enum:
            - ready_immediately
            - scan_required
        scan_status:
          type: string
          enum:
            - not_required
            - pending
          description: Scan-gated files are not deliverable until scan_status becomes clean.
        expires_at:
          type: string
          format: date-time
        delivery_token_expires_at:
          type: string
          format: date-time
          description: Present only when upload visibility is `signed`.
        request_id:
          type: string
    UploadSessionCreateRequest:
      type: object
      required:
        - filename
        - content_type
        - size
      properties:
        filename:
          type: string
          minLength: 1
          maxLength: 255
          examples:
            - archive.zip
        content_type:
          type: string
          examples:
            - application/zip
        size:
          type: integer
          minimum: 1
          description: Total upload size in bytes.
        visibility:
          type: string
          enum:
            - public
            - signed
          default: public
        part_size:
          type: integer
          minimum: 1
          maximum: 99614720
          description: Desired part size in bytes. When the session has more than one part, this must be at least 5 MiB. If omitted, GetFileURL chooses a bounded default.
    UploadSessionCreateResponse:
      type: object
      required:
        - upload_session_id
        - file_id
        - status
        - part_size
        - expected_part_count
        - upload_part_url_template
        - complete_url
        - expires_at
        - file_expires_at
        - visibility
        - request_id
      properties:
        upload_session_id:
          type: string
          examples:
            - us_01hxyz
        file_id:
          type: string
          examples:
            - file_01hxyz
        status:
          type: string
          enum:
            - active
        part_size:
          type: integer
          minimum: 1
        expected_part_count:
          type: integer
          minimum: 1
        upload_part_url_template:
          type: string
          format: uri-template
          description: URL template containing `{part_number}` for upload-session part PUT requests.
          examples:
            - https://api.getfileurl.com/v1/upload-sessions/us_01hxyz/parts/{part_number}
        complete_url:
          type: string
          format: uri
          examples:
            - https://api.getfileurl.com/v1/upload-sessions/us_01hxyz/complete
        expires_at:
          type: string
          format: date-time
          description: Upload-session deadline. Incomplete sessions are expired and their multipart uploads are aborted by the scheduled cleanup path.
        file_expires_at:
          type: string
          format: date-time
          description: File delivery expiry after successful completion.
        visibility:
          type: string
          enum:
            - public
            - signed
        request_id:
          type: string
    UploadSessionPartUploadResponse:
      type: object
      required:
        - upload_session_id
        - part_number
        - etag
        - size
        - request_id
      properties:
        upload_session_id:
          type: string
        part_number:
          type: integer
          minimum: 1
        etag:
          type: string
          description: R2 multipart part ETag recorded for completion.
        size:
          type: integer
          minimum: 1
        request_id:
          type: string
    FileListResponse:
      type: object
      required:
        - files
        - has_more
        - next_cursor
        - request_id
      properties:
        files:
          type: array
          items:
            $ref: "#/components/schemas/FileMetadata"
        has_more:
          type: boolean
        next_cursor:
          type:
            - string
            - "null"
        request_id:
          type: string
    FileMetadata:
      type: object
      required:
        - file_id
        - workspace_id
        - url
        - filename
        - content_type
        - size
        - status
        - file_handling_class
        - delivery_disposition
        - visibility
        - upload_source
        - source_url_host
        - scan_policy
        - scan_status
        - scan_result
        - expires_at
        - deleted_at
        - expired_at
        - quarantined_at
        - quarantine_reason
        - purge_status
        - purged_at
        - external_fetch_count
        - delivery_bytes
        - first_external_fetch_at
        - created_at
        - updated_at
      properties:
        file_id:
          type: string
          examples:
            - file_01hxyz
        workspace_id:
          type: string
        url:
          type: string
          format: uri
          description: Base CDN file URL. Signed files require a token from `POST /v1/files/{file_id}/signed-url`.
        filename:
          type:
            - string
            - "null"
        content_type:
          type: string
        size:
          type: integer
          minimum: 0
        status:
          type: string
          enum:
            - uploading
            - scanning
            - ready
            - failed
            - deleted
            - expired
            - quarantined
        file_handling_class:
          type: string
          enum:
            - inline
            - attachment
            - scan_gated
            - blocked
        delivery_disposition:
          type: string
          enum:
            - inline
            - attachment
            - blocked
        visibility:
          type: string
          enum:
            - public
            - signed
        upload_source:
          type: string
          enum:
            - direct_upload
            - remote_url
        source_url_host:
          type:
            - string
            - "null"
          description: Hostname of the remote source when uploaded through `POST /v1/files/from-url`.
        scan_policy:
          type: string
          enum:
            - ready_immediately
            - scan_required
            - blocked
        scan_status:
          type: string
          enum:
            - not_required
            - pending
            - scanning
            - clean
            - infected
            - failed
        scan_result:
          type:
            - string
            - "null"
          enum:
            - clean
            - infected
            - error
            - null
        expires_at:
          type: string
          format: date-time
        deleted_at:
          type:
            - string
            - "null"
          format: date-time
        expired_at:
          type:
            - string
            - "null"
          format: date-time
        quarantined_at:
          type:
            - string
            - "null"
          format: date-time
        quarantine_reason:
          type:
            - string
            - "null"
        purge_status:
          type:
            - string
            - "null"
          enum:
            - pending
            - in_progress
            - purged
            - failed
            - null
        purged_at:
          type:
            - string
            - "null"
          format: date-time
        external_fetch_count:
          type: integer
          minimum: 0
        delivery_bytes:
          type: integer
          minimum: 0
        first_external_fetch_at:
          type:
            - string
            - "null"
          format: date-time
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    SignedDeliveryUrlResponse:
      type: object
      required:
        - file_id
        - url
        - visibility
        - delivery_token_expires_at
        - expires_at
        - request_id
      properties:
        file_id:
          type: string
          examples:
            - file_01hxyz
        url:
          type: string
          format: uri
          examples:
            - https://cdn.getfileurl.com/f/file_01hxyz?token=gfu_signed_token
        visibility:
          type: string
          enum:
            - signed
        delivery_token_expires_at:
          type: string
          format: date-time
        expires_at:
          type: string
          format: date-time
        request_id:
          type: string
    UrlDiagnosticResponse:
      type: object
      required:
        - url
        - final_url
        - reachable
        - status
        - status_reason
        - http_status
        - content_type
        - content_length
        - filename
        - redirects
        - max_standard_upload_size
        - policy
        - checked_at
        - request_id
      properties:
        url:
          type:
            - string
            - "null"
          format: uri
          description: Sanitized submitted URL with fragments removed. Null when the submitted URL cannot be safely echoed.
        final_url:
          type:
            - string
            - "null"
          format: uri
          description: Final sanitized URL after allowed redirects.
        reachable:
          type: boolean
          description: True only when the URL can be fetched and satisfies remote ingest requirements.
        status:
          type: string
          enum:
            - ok
            - blocked
            - fetch_failed
            - bad_status
            - missing_content_length
            - too_large
            - redirect_policy_failed
        status_reason:
          type: string
        http_status:
          type:
            - integer
            - "null"
        content_type:
          type:
            - string
            - "null"
        content_length:
          type:
            - integer
            - "null"
          minimum: 0
        filename:
          type:
            - string
            - "null"
        redirects:
          type: array
          items:
            type: object
            required:
              - url
              - http_status
            properties:
              url:
                type: string
                format: uri
              http_status:
                type: integer
        max_standard_upload_size:
          type:
            - integer
            - "null"
          minimum: 0
        policy:
          type: object
          required:
            - https_required
            - public_hosts_only
            - standard_port_only
            - credentials_forbidden
            - redirects_revalidated
            - raw_source_url_stored
          properties:
            https_required:
              type: boolean
              const: true
            public_hosts_only:
              type: boolean
              const: true
            standard_port_only:
              type: boolean
              const: true
            credentials_forbidden:
              type: boolean
              const: true
            redirects_revalidated:
              type: boolean
              const: true
            raw_source_url_stored:
              type: boolean
              const: false
        checked_at:
          type: string
          format: date-time
        request_id:
          type: string
    WorkspaceUsageResponse:
      type: object
      required:
        - workspace_id
        - plan_key
        - period
        - usage
        - limits
        - grace
        - support
        - request_id
      properties:
        workspace_id:
          type: string
        plan_key:
          $ref: "#/components/schemas/PlanKey"
        period:
          type: object
          required:
            - start
            - end
          properties:
            start:
              type: string
              format: date-time
            end:
              type: string
              format: date-time
        usage:
          type: object
          required:
            - uploads_reserved
            - upload_bytes_reserved
            - delivery_bytes
            - active_storage_bytes
          properties:
            uploads_reserved:
              type: integer
              minimum: 0
            upload_bytes_reserved:
              type: integer
              minimum: 0
            delivery_bytes:
              type: integer
              minimum: 0
            active_storage_bytes:
              type: integer
              minimum: 0
        limits:
          type: object
          required:
            - max_file_bytes
            - max_standard_upload_bytes
            - max_storage_bytes
            - max_uploads_per_month
            - max_delivery_bytes
            - retention_seconds
            - max_api_keys
            - max_team_seats
          properties:
            max_file_bytes:
              type: integer
              minimum: 0
            max_standard_upload_bytes:
              type: integer
              minimum: 0
            max_storage_bytes:
              type: integer
              minimum: 0
            max_uploads_per_month:
              type: integer
              minimum: 0
            max_delivery_bytes:
              type: integer
              minimum: 0
            retention_seconds:
              type: integer
              minimum: 0
            max_api_keys:
              type: integer
              minimum: 0
            max_team_seats:
              type: integer
              minimum: 0
        grace:
          type: object
          required:
            - buffer_percent
            - max_storage_bytes
            - max_uploads_per_month
            - max_delivery_bytes
          properties:
            buffer_percent:
              type: integer
              minimum: 0
            max_storage_bytes:
              type: integer
              minimum: 0
            max_uploads_per_month:
              type: integer
              minimum: 0
            max_delivery_bytes:
              type: integer
              minimum: 0
        support:
          type: object
          required:
            - target
            - sla_tier
          properties:
            target:
              type: string
              enum:
                - none
                - best_effort
                - two_business_days
                - one_business_day
                - same_or_next_business_day
                - contractual
            sla_tier:
              type: string
              enum:
                - none
                - optional
                - contractual
        request_id:
          type: string
    PlanKey:
      type: string
      enum:
        - free
        - starter
        - pro
        - agency
        - scale
        - enterprise
    ErrorEnvelope:
      type: object
      required:
        - error
      properties:
        error:
          type: object
          required:
            - code
            - message
            - request_id
            - docs_url
          properties:
            code:
              type: string
              enum:
                - bad_request
                - unauthorized
                - forbidden
                - file_not_found
                - workspace_not_found
                - file_too_large
                - unsupported_file_type
                - quota_exceeded
                - rate_limited
                - upload_failed
                - unsupported_api_version
                - api_version_retired
                - internal_error
            message:
              type: string
            request_id:
              type: string
            docs_url:
              type: string
              format: uri
    DeletePendingResponse:
      type: object
      required:
        - file_id
        - status
        - request_id
      properties:
        file_id:
          type: string
        status:
          type: string
          enum:
            - delete_pending
        request_id:
          type: string
    WebhookEvent:
      type: object
      required:
        - id
        - type
        - api_version
        - schema_version
        - created_at
        - data
      properties:
        id:
          type: string
          examples:
            - evt_01hxyz
        type:
          type: string
          enum:
            - file.uploaded
            - file.ready
            - file.failed
            - file.deleted
            - file.expired
            - file.quarantined
            - file.first_external_fetch
            - usage.limit_warning
            - usage.limit_exceeded
        api_version:
          type: string
          enum:
            - v1
        schema_version:
          type: string
          enum:
            - v1
        created_at:
          type: string
          format: date-time
        data:
          $ref: "#/components/schemas/WebhookEventData"
    WebhookEventData:
      type: object
      additionalProperties: false
      properties:
        workspace_id:
          type: string
        file_id:
          type: string
        content_type:
          type: string
        size:
          type: integer
          minimum: 0
        status:
          type: string
        scan_status:
          type: string
        file_handling_class:
          type: string
        delivery_disposition:
          type: string
        visibility:
          type: string
          enum:
            - public
            - signed
        expires_at:
          type: string
          format: date-time
  responses:
    BadRequest:
      description: Request shape is invalid.
      headers:
        X-GetFileURL-API-Version:
          $ref: "#/components/headers/ApiVersion"
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorEnvelope"
    Unauthorized:
      description: Missing or invalid GetFileURL API key.
      headers:
        X-GetFileURL-API-Version:
          $ref: "#/components/headers/ApiVersion"
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorEnvelope"
    Forbidden:
      description: The authenticated API key does not have the required scope.
      headers:
        X-GetFileURL-API-Version:
          $ref: "#/components/headers/ApiVersion"
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorEnvelope"
    QuotaExceeded:
      description: Workspace upload count, storage, or delivery quota is exhausted.
      headers:
        X-GetFileURL-API-Version:
          $ref: "#/components/headers/ApiVersion"
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorEnvelope"
    FileNotFound:
      description: File does not exist or does not belong to the authenticated workspace.
      headers:
        X-GetFileURL-API-Version:
          $ref: "#/components/headers/ApiVersion"
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorEnvelope"
    WorkspaceNotFound:
      description: Workspace usage does not exist or does not belong to the authenticated API key.
      headers:
        X-GetFileURL-API-Version:
          $ref: "#/components/headers/ApiVersion"
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorEnvelope"
    FileTooLarge:
      description: File exceeds the plan limit.
      headers:
        X-GetFileURL-API-Version:
          $ref: "#/components/headers/ApiVersion"
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorEnvelope"
    UnsupportedFileType:
      description: File type is blocked by policy.
      headers:
        X-GetFileURL-API-Version:
          $ref: "#/components/headers/ApiVersion"
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorEnvelope"
    RateLimited:
      description: The API key or workspace exceeded the route rate limit.
      headers:
        X-GetFileURL-API-Version:
          $ref: "#/components/headers/ApiVersion"
        Retry-After:
          description: Seconds to wait before retrying.
          schema:
            type: integer
        X-RateLimit-Limit:
          description: Effective request limit for the limiting subject and route.
          schema:
            type: integer
        X-RateLimit-Remaining:
          description: Remaining requests in the current window.
          schema:
            type: integer
        X-RateLimit-Reset:
          description: Unix timestamp when the current rate-limit window resets.
          schema:
            type: integer
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorEnvelope"
    ApiVersionRetired:
      description: Requested API version has passed its sunset date and no longer serves traffic.
      headers:
        X-GetFileURL-API-Version:
          $ref: "#/components/headers/ApiVersion"
        Deprecation:
          $ref: "#/components/headers/Deprecation"
        Sunset:
          $ref: "#/components/headers/Sunset"
        Link:
          $ref: "#/components/headers/Link"
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorEnvelope"
    InternalError:
      description: Unexpected server error.
      headers:
        X-GetFileURL-API-Version:
          $ref: "#/components/headers/ApiVersion"
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorEnvelope"
