> ## Documentation Index
> Fetch the complete documentation index at: https://docs.voyager.so/llms.txt
> Use this file to discover all available pages before exploring further.

# Upload or import a file

> An upload (``status: pending`` with ``upload_url``: PUT the bytes with ``headers``, then
``POST /v1/files/{id}/complete``), or with ``url`` an import, ready at once. With ``Idempotency-Key``, a retry
returns the same file (an upload still pending gets a fresh ``upload_url``).



## OpenAPI

````yaml /openapi/voyager-api.yaml post /files
openapi: 3.1.0
info:
  title: Voyager API
  description: >-
    Generate images, video, speech, music, sound effects and 3D with your
    Voyager workspace's credits. Authenticate with `Authorization: Bearer
    vy_live_…`. Every response carries `X-Request-ID` and the
    `X-RateLimit-Limit` / `X-RateLimit-Remaining` / `X-RateLimit-Reset` (seconds
    until the window resets) headers; a 429 adds `Retry-After`. Errors share one
    envelope (`ErrorResponse`).
  version: 1.0.0
  x-voyager-agent-prompt: >
    Set up the Voyager API in this project.


    Voyager generates images, video, speech, music, sound effects, 3D and object
    masks with the same models

    and credits as the Voyager app. Docs: https://docs.voyager.so/api 
    (agent-readable:

    https://docs.voyager.so/llms.txt, OpenAPI:
    https://api.voyager.so/v1/openapi.json)


    1. Key: read it from the env var VOYAGER_API_KEY. Never hard-code it or
    commit it; add
       VOYAGER_API_KEY= to .env.example and make sure .env is git-ignored. If it's missing,
       ask me to create one at https://voyager.so/dashboard/api (paid plans).
    2. Base URL https://api.voyager.so/v1, header: Authorization: Bearer
    $VOYAGER_API_KEY

    3. Pick a model: GET
    /models?type=image|video|speech|music|sound_effect|3d|segment. Read
       GET /models/{id} for its input_schema before building a request.
    4. Optional cost check: POST /estimate with the same body as step 5; it
    returns
       estimated_charge (credits) and can_run.
    5. Create: POST /jobs {"model": "<id>", "operation": "<op>", "input": {...}}
    with an Idempotency-Key header
       (a new UUID per logical request; reuse it when retrying the same request).
    6. Wait: GET /jobs/{id}?wait=25 until status is succeeded, failed or
    canceled
       (honor Retry-After / poll_after_ms). Or pass "webhook_url" and verify the
       Standard Webhooks signature with the secret from the dashboard.
    7. Results: job.outputs[].url is a signed link valid for 1 hour; download it
    right
       away, or GET /jobs/{id} again for a fresh link. Files are kept 30 days.
    8. Errors are {"error": {"code","message","retryable",...}}. 402 = out of
    credits or
       not on a paid plan (show error.details.upgrade_url to me). 429 = slow down: wait
       Retry-After seconds. Retry only when retryable is true, with the same Idempotency-Key.

    Minimal example (curl):
      curl -s https://api.voyager.so/v1/jobs \
        -H "Authorization: Bearer $VOYAGER_API_KEY" -H "Content-Type: application/json" \
        -H "Idempotency-Key: $(uuidgen)" \
        -d '{"model":"nano-banana-2.1","operation":"text-to-image","input":{"prompt":"a red fox in snow","aspect_ratio":"16:9"}}'
      curl -s "https://api.voyager.so/v1/jobs/<job id>?wait=25" -H "Authorization: Bearer $VOYAGER_API_KEY"

    Write a small typed client (create → wait → download) with retries as above,
    then

    generate one test image to prove it works and tell me what it cost
    (job.credits.charged;

    if credits.state is still "pending", report job.credits.estimated and say
    it's an estimate).
servers:
  - url: https://api.voyager.so/v1
    description: Production
security: []
paths:
  /files:
    post:
      tags:
        - Files
      summary: Upload or import a file
      description: >-
        An upload (``status: pending`` with ``upload_url``: PUT the bytes with
        ``headers``, then

        ``POST /v1/files/{id}/complete``), or with ``url`` an import, ready at
        once. With ``Idempotency-Key``, a retry

        returns the same file (an upload still pending gets a fresh
        ``upload_url``).
      operationId: createFile
      parameters:
        - name: Idempotency-Key
          in: header
          required: false
          schema:
            anyOf:
              - type: string
              - type: 'null'
            description: >-
              A new UUID per logical request; resend it on retries to get the
              same file, never a duplicate.
            title: Idempotency-Key
          description: >-
            A new UUID per logical request; resend it on retries to get the same
            file, never a duplicate.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/FileCreate'
      responses:
        '201':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FileObject'
          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'
            Idempotent-Replayed:
              $ref: '#/components/headers/Idempotent-Replayed'
        '400':
          description: Invalid request.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          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':
          description: Missing, invalid or revoked API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          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'
        '402':
          description: >-
            Billing wall: a paid plan or more credits is needed
            (details.upgrade_url).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          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'
        '422':
          description: The request is well formed but cannot be processed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          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'
        '429':
          description: Rate limited. Retry after Retry-After seconds.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          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'
            Retry-After:
              $ref: '#/components/headers/Retry-After'
        '503':
          description: Temporarily unavailable. Retry with backoff.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
          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'
      security:
        - bearerAuth: []
components:
  schemas:
    FileCreate:
      title: FileCreate
      description: >-
        Either an upload (PUT the bytes to upload_url, then complete) or an
        import from a URL.
      oneOf:
        - title: FileUpload
          type: object
          properties:
            file_name:
              title: File Name
              type: string
              maxLength: 200
              minLength: 1
            content_type:
              title: Content Type
              description: image/png, image/jpeg, video/mp4, audio/mpeg…
              type: string
              maxLength: 100
            size_bytes:
              title: Size Bytes
              description: Exact size of the file you will PUT.
              type: integer
              minimum: 1
          required:
            - file_name
            - content_type
            - size_bytes
          additionalProperties: false
        - title: FileImport
          type: object
          properties:
            url:
              title: Url
              description: Import a public https file instead.
              type: string
              maxLength: 4096
            file_name:
              title: File Name
              type: string
              maxLength: 200
              minLength: 1
          required:
            - url
          additionalProperties: false
    FileObject:
      properties:
        id:
          type: string
          title: Id
          description: file_…; pass it in a job's input (image, images, video, audio…).
        object:
          type: string
          const: file
          title: Object
          default: file
          description: Always "file".
        status:
          type: string
          enum:
            - pending
            - ready
            - failed
          title: Status
          description: pending until the upload completes, then ready (or failed).
        type:
          anyOf:
            - type: string
            - type: 'null'
          title: Type
          description: image, video, audio, model3d or other (ready files).
        file_name:
          type: string
          title: File Name
          description: The file's name.
        content_type:
          type: string
          title: Content Type
          description: The file's media type, e.g. image/png.
        size_bytes:
          type: integer
          title: Size Bytes
          description: Size in bytes.
        url:
          anyOf:
            - type: string
            - type: 'null'
          title: Url
          description: 'Ready files: signed download link, valid one hour.'
        url_expires_at:
          anyOf:
            - type: string
              format: date-time
            - type: 'null'
          title: Url Expires At
          description: >-
            When `url` stops working (ISO 8601); read the object again for a
            fresh link.
        expires_at:
          anyOf:
            - type: string
              format: date-time
            - type: 'null'
          title: Expires At
          description: >-
            When the file is deleted: references 7 days, outputs 30 days, Cast
            never; pending uploads 24 h.
        retention:
          anyOf:
            - type: string
              enum:
                - reference
                - output
                - cast
                - pending
            - type: 'null'
          title: Retention
          description: How long the file is kept.
        created_at:
          anyOf:
            - type: string
              format: date-time
            - type: 'null'
          title: Created At
          description: When it was created (ISO 8601).
        error:
          anyOf:
            - $ref: '#/components/schemas/JobError'
            - type: 'null'
          description: Why the upload failed, when status is failed.
        width:
          anyOf:
            - type: integer
            - type: 'null'
          title: Width
          description: Width in pixels, when known.
        height:
          anyOf:
            - type: integer
            - type: 'null'
          title: Height
          description: Height in pixels, when known.
        duration_seconds:
          anyOf:
            - type: number
            - type: 'null'
          title: Duration Seconds
          description: Length in seconds, when known.
        upload_url:
          anyOf:
            - type: string
            - type: 'null'
          title: Upload Url
          description: PUT the bytes here with `headers`, then complete.
        method:
          anyOf:
            - type: string
              const: PUT
            - type: 'null'
          title: Method
          description: The HTTP method to upload the bytes with, while pending.
        headers:
          anyOf:
            - additionalProperties:
                type: string
              type: object
            - type: 'null'
          title: Headers
          description: Headers to send with the upload, while pending.
        upload_expires_at:
          anyOf:
            - type: string
              format: date-time
            - type: 'null'
          title: Upload Expires At
          description: When upload_url stops accepting the bytes (ISO 8601).
      type: object
      required:
        - id
        - status
        - file_name
        - content_type
        - size_bytes
        - expires_at
      title: FileObject
    ErrorResponse:
      properties:
        error:
          $ref: '#/components/schemas/ErrorBody'
          description: What went wrong.
      type: object
      required:
        - error
      title: ErrorResponse
      description: Every non-2xx answer.
    JobError:
      properties:
        code:
          type: string
          title: Code
          description: A stable machine code (see x-voyager-errors).
        message:
          type: string
          title: Message
          description: For developers. Not localized.
        details:
          anyOf:
            - additionalProperties: true
              type: object
            - type: 'null'
          title: Details
          description: Code-specific detail.
      type: object
      required:
        - code
        - message
      title: JobError
    ErrorBody:
      properties:
        type:
          $ref: '#/components/schemas/ErrorType'
          description: Stable category to branch on.
        code:
          type: string
          enum:
            - invalid_request
            - invalid_input
            - content_policy
            - upstream_unavailable
            - rate_limited
            - queue_full
            - model_unavailable
            - model_not_found
            - insufficient_credits
            - paid_plan_required
            - model_not_in_plan
            - model_access_required
            - outcome_unknown
            - expired
            - not_found
            - conflict
            - forbidden
            - internal_error
            - catalog_unavailable
            - billing_unavailable
            - no_voyager_seat
            - credits_reloading
            - cast_not_supported
            - voice_cloning_unavailable
            - too_many_references
            - payload_too_large
            - upload_too_large
            - intake_full
            - invalid_url
            - fetch_failed
            - unsupported_file
            - invalid_api_key
            - idempotency_conflict
            - idempotency_in_flight
            - invalid_idempotency_key
            - invalid_cursor
            - invalid_webhook_url
            - method_not_allowed
            - account_unavailable
            - plan_required_for_faces
            - version_conflict
            - slug_taken
            - already_referenced
            - asset_in_use
            - upload_incomplete
            - cancel_unsupported
            - unsupported_model
            - invalid_job_id
            - voice_required
            - observation_timeout
            - reference_unavailable
            - queue_timeout
            - reference_missing
          title: Code
          description: >-
            Stable machine code; never renamed once published (x-voyager-errors
            lists each).
        message:
          type: string
          title: Message
          description: For developers. Not localized.
        doc_url:
          type: string
          title: Doc Url
          description: https://docs.voyager.so/api/errors#<code>
        request_id:
          anyOf:
            - type: string
            - type: 'null'
          title: Request Id
          description: Also in the X-Request-ID header; quote it to support.
        retryable:
          anyOf:
            - type: boolean
            - type: 'null'
          title: Retryable
          description: Whether the same request can succeed later.
        retry_after_ms:
          anyOf:
            - type: integer
            - type: 'null'
          title: Retry After Ms
          description: How long to wait before retrying.
        param:
          anyOf:
            - type: string
            - type: 'null'
          title: Param
          description: >-
            The request field at fault, as a path (e.g. input.resolution); set
            on invalid_input.
        details:
          anyOf:
            - additionalProperties: true
              type: object
            - type: 'null'
          title: Details
          description: Code-specific detail, e.g. upgrade_url on a billing wall.
      type: object
      required:
        - type
        - code
        - message
        - doc_url
      title: ErrorBody
    ErrorType:
      type: string
      enum:
        - invalid_request
        - authentication
        - permission
        - not_found
        - conflict
        - rate_limited
        - idempotency_conflict
        - unprocessable
        - upstream_error
        - internal_error
      title: ErrorType
      description: Closed set of high-level categories SDKs branch on.
  headers:
    X-Request-ID:
      description: This request's id (also error.request_id).
      schema:
        type: string
    X-RateLimit-Limit:
      description: >-
        Requests allowed per 60 seconds in this bucket (120 writes or 600 reads
        per minute per key).
      schema:
        type: integer
    X-RateLimit-Remaining:
      description: Requests left in this window.
      schema:
        type: integer
    X-RateLimit-Reset:
      description: Seconds until the window resets.
      schema:
        type: integer
    Idempotent-Replayed:
      description: >-
        "true" when this answer replays an earlier request under the same
        Idempotency-Key.
      schema:
        type: string
        enum:
          - 'true'
    Retry-After:
      description: Seconds to wait before retrying.
      schema:
        type: integer
  securitySchemes:
    bearerAuth:
      type: http
      description: A Voyager API key from https://voyager.so/dashboard/api.
      scheme: bearer
      bearerFormat: vy_live_…

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.