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

# List assets

> Paginated list of your assets, newest first.



## OpenAPI

````yaml https://roughy-api-staging.fly.dev/v1/openapi.json get /v1/assets
openapi: 3.1.0
info:
  title: Roughy Public API
  version: 0.0.0
servers:
  - url: https://roughy-api-staging.fly.dev
    description: Staging
security: []
tags:
  - name: Projects
    description: >-
      Workflow containers. Every asset lives inside a project; a project groups
      everything for one piece of work (an episode, an interview, a talk).
  - name: Assets
    description: >-
      The unit of media. `POST /assets` creates one and opens its upload in a
      single call: the response body is the asset (its `upload_url` carries the
      transfer URL while `pending_upload`), and the TUS transfer URL also comes
      back in the `Location` header. List, detail (with signed download URL),
      and delete round it out. Each asset belongs to exactly one project;
      `language` is set at create and immutable.
  - name: Uploads
    description: >-
      Drive the TUS-resumable transfer that streams an asset's bytes. `POST
      /assets` opens the upload; `PATCH`/`HEAD` move and resume the bytes,
      `DELETE` cancels (removing the asset). Lost the transfer URL? Re-`GET` the
      asset — while it's `pending_upload`, `upload_url` carries it.
  - name: Cuts
    description: >-
      A kept-segment plan for an asset, computed automatically by Roughy. Create
      one with `POST /cuts {asset_id, script?}`, list with `GET /cuts`, read a
      cut, export it to NLE formats, and export its generated subtitle (SRT /
      VTT).
  - name: Renders
    description: >-
      Renders an asset to a video (`mp4`/`mov`) or audio (`m4a`/`mp3`) output,
      optionally applying a `cut_id`. `POST /renders {asset_id, cut_id?,
      output}` creates one; list, read (with a signed download URL), and cancel
      as a top-level `/renders` resource.
  - name: Webhooks
    description: >-
      Register endpoints to receive signed terminal-state events (cut / render /
      asset) instead of polling.
  - name: Credits
    description: Credit balance and usage history.
  - name: Users
    description: Your account and profile.
  - name: OAuth
    description: >-
      Device authorization grant (RFC 8628), served at app.roughy.ai/oauth/* for
      the editor plugin. Internal surface — not part of the public /v1 SDK.
  - name: billing
    description: Stripe customer portal, invoices, subscriptions.
  - name: api-keys
    description: Mint and revoke API keys.
  - name: auth
    description: Sign up, log in, log out, and OAuth flows for the dashboard.
  - name: webhooks
    description: Inbound provider webhooks (Stripe, Resend, ElevenLabs).
  - name: email-preferences
    description: Per-category transactional-email opt-out preferences.
  - name: sse
    description: Server-sent live updates per asset.
  - name: health
    description: Liveness and readiness probes.
paths:
  /v1/assets:
    get:
      tags:
        - Assets
      summary: List assets
      description: Paginated list of your assets, newest first.
      operationId: list_assets_v1_assets_get
      parameters:
        - name: project_id
          in: query
          required: false
          schema:
            anyOf:
              - type: string
                format: uuid
              - type: 'null'
            description: >-
              Restrict the listing to one [project](/v1/projects). Omit to
              return assets across every project you own.
            title: Project Id
          description: >-
            Restrict the listing to one [project](/v1/projects). Omit to return
            assets across every project you own.
        - name: state
          in: query
          required: false
          schema:
            anyOf:
              - enum:
                  - pending_upload
                  - processing
                  - pending_payment
                  - ready
                  - failed
                type: string
              - type: 'null'
            description: Filter by lifecycle state. Omit to return every state.
            title: State
          description: Filter by lifecycle state. Omit to return every state.
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            maximum: 100
            minimum: 1
            description: Maximum number of assets to return (1-100). Defaults to 20.
            default: 20
            title: Limit
          description: Maximum number of assets to return (1-100). Defaults to 20.
        - name: offset
          in: query
          required: false
          schema:
            type: integer
            minimum: 0
            description: >-
              Number of assets to skip from the start of the listing. Defaults
              to 0.
            default: 0
            title: Offset
          description: >-
            Number of assets to skip from the start of the listing. Defaults to
            0.
      responses:
        '200':
          description: One page of assets, newest first.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AssetListResponse'
        '404':
          description: Project not found.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error:
                  code: not_found
                  message: Project not found.
      security:
        - HTTPBearer: []
components:
  schemas:
    AssetListResponse:
      properties:
        items:
          items:
            $ref: '#/components/schemas/AssetSummary'
          type: array
          title: Items
          description: Page of assets, newest first.
        total:
          type: integer
          title: Total
          description: >-
            Total number of assets in this project matching the current filter
            across all pages — not the size of `items`.
        limit:
          type: integer
          title: Limit
          description: The `limit` echoed back from the request.
        offset:
          type: integer
          title: Offset
          description: The `offset` echoed back from the request.
      type: object
      required:
        - items
        - total
        - limit
        - offset
      title: AssetListResponse
      description: Paginated list of assets, newest first.
      example:
        items:
          - content_type: audio/mpeg
            created_at: '2026-05-02T08:54:52.423000Z'
            duration_seconds: '324.412'
            extension: mp3
            id: 019de7f7-3a52-74d1-9eeb-323e0bfb7bb9
            language: de
            project_id: 019de7f5-7e21-7402-92a8-1c0e1fa84411
            size_bytes: 4823572
            state: ready
            type: audio
        total: 1
        limit: 50
        offset: 0
    ErrorResponse:
      properties:
        error:
          $ref: '#/components/schemas/ErrorBody'
      type: object
      required:
        - error
      title: ErrorResponse
      description: |-
        The shape every error response returns.

        Request-correlation lives in the `X-Request-Id` response
        header — include that header value in a support ticket to
        correlate with server-side logs.
      example:
        error:
          code: validation_error
          fields:
            - field: body.name
              message: Field required
          message: Upload-Length must be at least 64 bytes.
    AssetSummary:
      properties:
        id:
          type: string
          format: uuid
          title: Id
          description: Stable unique identifier for the asset.
        project_id:
          type: string
          format: uuid
          title: Project Id
          description: Identifier of the project that owns this asset.
        type:
          anyOf:
            - $ref: '#/components/schemas/AssetType'
            - type: 'null'
          description: >-
            What kind of media this asset holds: `audio` or `video`, derived
            from the uploaded bytes once the first chunk is received. `null`
            before then.
        state:
          $ref: '#/components/schemas/AssetState'
          description: >-
            Lifecycle state: `pending_upload` while bytes are uploading,
            `processing` while Roughy prepares the asset (the per-minute charge
            settles here, once preparation finishes), `pending_payment` if your
            balance didn't cover the charge (bytes kept; auto-activates on your
            next top-up), `ready` once prepared and paid for (cuttable and
            renderable), and `failed` if preparation permanently failed (see
            `error_code` / `error_message`; nothing is charged on a failure).
        extension:
          anyOf:
            - type: string
            - type: 'null'
          title: Extension
          description: >-
            File extension without the leading dot (e.g. `mp3`, `mp4`), derived
            from the uploaded bytes once the first chunk is received. `null`
            before then.
        size_bytes:
          anyOf:
            - type: integer
            - type: 'null'
          title: Size Bytes
          description: Total stored size in bytes. `null` while in `pending_upload`.
        content_type:
          anyOf:
            - type: string
            - type: 'null'
          title: Content Type
          description: >-
            MIME type of the stored bytes (e.g. `audio/mpeg`, `video/mp4`),
            derived from the uploaded bytes once the first chunk is received.
            `null` before then.
        duration_seconds:
          anyOf:
            - type: string
              pattern: ^(?!^[-+.]*$)[+-]?0*\d*\.?\d*$
            - type: 'null'
          title: Duration Seconds
          description: >-
            Duration in seconds, measured while the asset is `processing`.
            `null` while `pending_upload` or `processing` (before it is
            measured) and on a `failed` asset; always present once `ready` or
            `pending_payment`.
        language:
          anyOf:
            - type: string
            - type: 'null'
          title: Language
          description: >-
            Optional ISO 639-1 hint for the spoken language of the media, set at
            create and immutable. `null` means auto-detect.
        error_code:
          anyOf:
            - type: string
            - type: 'null'
          title: Error Code
          description: >-
            Machine-readable failure reason. Set only when `state` is `failed`;
            `null` otherwise.
        error_message:
          anyOf:
            - type: string
            - type: 'null'
          title: Error Message
          description: Human-readable failure detail. Set only when `state` is `failed`.
        created_at:
          type: string
          format: date-time
          title: Created At
          description: When the asset row was created.
        upload_url:
          anyOf:
            - type: string
            - type: 'null'
          title: Upload Url
          description: >-
            TUS transfer URL for resuming this asset's source upload. Present
            only while `state` is `pending_upload`; `null` once the upload
            finalises. Hand it to a TUS client (`tus-js-client` /
            `tus-py-client`) as the upload URL — the client issues a `HEAD`
            against it to read the authoritative `Upload-Offset`, then resumes.
            While `pending_upload`, this is the same transfer URL the create
            response carries in its `Location` header.
      type: object
      required:
        - id
        - project_id
        - type
        - state
        - extension
        - size_bytes
        - content_type
        - duration_seconds
        - language
        - created_at
      title: AssetSummary
      description: Asset metadata.
      example:
        id: 019de7f7-3a52-74d1-9eeb-323e0bfb7bb9
        project_id: 019de7f5-7e21-7402-92a8-1c0e1fa84411
        type: audio
        state: ready
        extension: mp3
        size_bytes: 4823572
        content_type: audio/mpeg
        duration_seconds: '324.412'
        language: de
        error_code: null
        error_message: null
        created_at: '2026-05-02T08:54:52.423000Z'
        upload_url: null
    ErrorBody:
      properties:
        code:
          $ref: '#/components/schemas/ErrorCode'
          description: >-
            Stable enum classifier. Clients should switch on this to drive retry
            / display logic; `message` is for humans.
        message:
          type: string
          title: Message
          description: >-
            One-line human-readable description of the failure. Safe to show end
            users; never carries backend internals.
        fields:
          anyOf:
            - items:
                $ref: '#/components/schemas/ErrorFieldDetail'
              type: array
            - type: 'null'
          title: Fields
          description: >-
            Per-field validation errors, set only for `code: validation_error`
            responses. Omitted in every other case.
      type: object
      required:
        - code
        - message
      title: ErrorBody
      description: Inner `error` object — the body of `ErrorResponse.error`.
    AssetType:
      type: string
      enum:
        - audio
        - video
      title: AssetType
    AssetState:
      type: string
      enum:
        - pending_upload
        - processing
        - pending_payment
        - ready
        - failed
      title: AssetState
      description: |-
        Lifecycle state of an asset.

        `pending_upload` — row exists, bytes still uploading.
        `processing` — bytes finalized, but the duration/attributes probe
        (and, for direct uploads, the asset-bound debit) has not completed
        yet. The asset is not usable while in this state.
        `pending_payment` — probed, but the asset-bound debit failed because
        of insufficient credits. Bytes stay in storage; the row stays in
        this state until a Stripe-Topup recovery succeeds or the 30-day TTL
        cron sweeps the asset. The asset is not usable while in this state.
        `ready` — probed and (for direct uploads) debit settled; asset is
        consumable.
        `failed` — the probe permanently failed (e.g. corrupt media that
        ffprobe cannot read). Terminal; the user re-uploads. `error_code` /
        `error_message` on the asset carry the reason.
    ErrorCode:
      type: string
      enum:
        - bad_request
        - unauthenticated
        - forbidden
        - not_found
        - conflict
        - gone
        - precondition_failed
        - payload_too_large
        - unsupported_media_type
        - validation_error
        - account_locked
        - rate_limited
        - insufficient_credits
        - internal_error
        - upstream_error
        - upstream_unavailable
        - asset_processing
        - asset_payment_required
        - asset_failed
        - asset_not_ready
        - modifier_not_ready
        - unsupported_format
        - frame_rate_required
        - cut_not_proposed
      title: ErrorCode
      description: |-
        Stable, client-facing error-classifier enum.

        New entries require an ADR amendment so SDK consumers can rely
        on the list being closed. Numeric HTTP status code stays in the
        response status line — `code` is the orthogonal *category*
        clients switch on without parsing the message.
    ErrorFieldDetail:
      properties:
        field:
          type: string
          title: Field
          description: >-
            Dotted path to the offending field, rooted at the request location
            (e.g. `body.name`, `query.limit`, `path.project_id`).
        message:
          type: string
          title: Message
          description: Human-readable explanation for this field.
      type: object
      required:
        - field
        - message
      title: ErrorFieldDetail
      description: Per-field validation failure on a single request field.
  securitySchemes:
    HTTPBearer:
      type: http
      description: >-
        Pass your API key as `Authorization: Bearer sk_…`. Mint a key from your
        dashboard's API-keys page.
      scheme: bearer
      bearerFormat: sk_…

````