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

# Submit a bulk label-export job

> Queue a bulk label-export job and return the job id. Polling endpoint (`GET /labels/export/{job_id}/`) returns status and a temporary signed download URL once complete. Small jobs (count <= `LabelExportJob.SYNC_THRESHOLD`) run synchronously inside the request — the very first poll will already report `completed`. Larger jobs queue to Celery and progress through `processing` → `completed`.



## OpenAPI

````yaml /openapi/openapi-products.json post /products/api/v1/labels/export/
openapi: 3.1.0
info:
  title: Products API
  version: 1.0.0
  description: >
    Look up, claim, and browse GTINs in the Closient product repository.


    ## Authentication


    All endpoints require an API key passed via the `X-API-Key` HTTP header,
    unless otherwise noted.


    ```

    X-API-Key: csb_<body>_<checksum>

    ```


    Generate API keys in **Settings > API Keys** in your dashboard, or via the
    Account API.

    Session-based (cookie) authentication is also accepted for browser-based
    access.


    ## Rate Limits


    | Tier        | Requests / minute | Requests / day |

    |-------------|-------------------|----------------|

    | Default     | 300               | 10,000         |

    | Custom      | Contact us        | Contact us     |


    Rate-limit headers are included on every response so callers can
    self-throttle without

    hitting our 429s ("informed governor"):


    - `RateLimit-Policy` — every active window, e.g. `300;w=60, 10000;w=86400`

    - `RateLimit-Limit` — quota for the **most-restrictive** currently-active
    window

    - `RateLimit-Remaining` — requests left in that window

    - `RateLimit-Reset` — seconds until that window resets (relative; clock-skew
    safe)


    Legacy `X-RateLimit-*` aliases are also emitted for back-compat.
    `X-RateLimit-Reset`

    keeps the absolute Unix-timestamp shape to avoid breaking existing
    consumers.


    When rate-limited, you receive `429 Too Many Requests` with a
    `retry_after_seconds` field

    in the error envelope and a `Retry-After` header.


    ## Pagination


    List endpoints return paginated results in this envelope:


    ```json

    {
      "data": [...],
      "pagination": {
        "page": 1,
        "page_size": 25,
        "total_count": 342,
        "total_pages": 14,
        "has_next": true,
        "has_previous": false
      }
    }

    ```


    Use `?page=2&page_size=50` query parameters. Maximum page size is 100.


    ## Error Responses


    All errors conform to [RFC 9457 Problem
    Details](https://www.rfc-editor.org/rfc/rfc9457)

    with `Content-Type: application/problem+json`:


    ```json

    {
      "type": "https://closient.com/docs/errors/not_found",
      "title": "Not Found",
      "status": 404,
      "detail": "The requested resource was not found.",
      "error_code": "not_found",
      "retryable": false,
      "timestamp": "2026-03-31T12:00:00+00:00"
    }

    ```


    Common error codes: `unauthorized` (401), `forbidden` (403), `not_found`
    (404),

    `validation_error` (422), `rate_limited` (429), `internal_error` (500).
  termsOfService: https://www.closient.com/terms/
servers:
  - url: https://www.closient.com
security: []
tags:
  - name: Products
    description: Look up, claim, and browse products and trade items.
  - name: Product Group
    description: Manage GDSN packaging hierarchy relationships.
  - name: Import
    description: Bulk import products from CSV files.
  - name: QR
    description: Generate QR codes encoding GS1 Digital Link URLs.
  - name: Codes
    description: Generate GS1 DataMatrix and 1D barcodes (EAN/UPC/ITF-14/Code128).
  - name: Digital Link
    description: >-
      Parse GS1 Digital Link URIs into structured AIs (GTIN, lot, expiry,
      serial).
  - name: Labels
    description: >-
      Bulk label-export jobs: ZIPs of QR / DataMatrix / 1D symbols and multi-up
      sheet PDFs, async via Celery with status polling.
externalDocs:
  description: Closient Documentation
  url: https://docs.closient.com
paths:
  /products/api/v1/labels/export/:
    post:
      tags:
        - Labels
      summary: Submit a bulk label-export job
      description: >-
        Queue a bulk label-export job and return the job id. Polling endpoint
        (`GET /labels/export/{job_id}/`) returns status and a temporary signed
        download URL once complete. Small jobs (count <=
        `LabelExportJob.SYNC_THRESHOLD`) run synchronously inside the request —
        the very first poll will already report `completed`. Larger jobs queue
        to Celery and progress through `processing` → `completed`.
      operationId: apps_products_api_labels_export_api_submit_label_export
      parameters: []
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/LabelExportSubmitIn'
        required: true
      responses:
        '202':
          description: Accepted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LabelExportAcceptedOut'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorOut'
        '422':
          description: Unprocessable Content
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorOut'
      security:
        - APIKeyHeaderAuth: []
        - OAuthTokenAuth: []
        - SessionAuth: []
components:
  schemas:
    LabelExportSubmitIn:
      description: |-
        Request body for `POST /products/api/v1/labels/export/`.

        Mirrors the field set the Dashboard's bulk-export form posts. The
        combination ``(export_type, file_format)`` must be coherent — sheet
        PDFs require ``file_format='pdf'``; the symbology ZIP exports
        (``qr_codes`` / ``datamatrix``) accept ``svg``/``png``; ``barcodes_1d``
        accepts ``svg``/``eps``.
      examples:
        - export_type: datamatrix
          file_format: svg
          filters:
            gtins:
              - '00012345678905'
            never_printed: true
          scope: lots
        - asset_config:
            sheet_template: 63up_1in_letter
          export_type: sheet_pdf
          file_format: pdf
          filters:
            product_id: abc123def456
          scope: lots
      properties:
        scope:
          $ref: '#/components/schemas/LabelExportScopeEnum'
          description: >-
            Which entity table the filters resolve against. `lots` and `serials`
            are supported today; `products` is reserved for a future per-product
            export and currently rejected at the renderer layer.
        export_type:
          $ref: '#/components/schemas/LabelExportTypeEnum'
          description: >-
            What asset the job produces. `qr_codes` / `datamatrix` /
            `barcodes_1d` produce ZIPs of one symbol per entity; `sheet_pdf`
            produces a multi-up label sheet PDF; `codes_combined` produces ZIPs
            containing both QR and 1D variants per entity.
        file_format:
          $ref: '#/components/schemas/LabelExportFileFormatEnum'
          description: >-
            Output container/format. Must be coherent with `export_type`:
            `sheet_pdf` requires `pdf`; `qr_codes`/`datamatrix` accept
            `svg`/`png`; `barcodes_1d` accepts `svg`/`eps`.
        filters:
          anyOf:
            - $ref: '#/components/schemas/LabelExportFiltersIn'
            - type: 'null'
          description: >-
            Optional selection filters. Omit to export every active lot/serial
            in the caller's organization at the chosen scope. Use a narrowing
            key (`gtins`, `product_id`, `lot_ids`/`serial_ids`, `lot_run_id`,
            etc.) to bound the result set.
        asset_config:
          anyOf:
            - $ref: '#/components/schemas/LabelExportAssetConfigIn'
            - type: 'null'
          description: >-
            Optional renderer-side knobs (sheet template choice, etc.). Defaults
            are renderer-specific; see field descriptions on
            `LabelExportAssetConfigIn`.
      required:
        - scope
        - export_type
        - file_format
      title: LabelExportSubmitIn
      type: object
    LabelExportAcceptedOut:
      description: |-
        Response for an accepted bulk-label-export job — always HTTP 202.

        Even when the underlying job runs synchronously (count below
        ``LabelExportJob.SYNC_THRESHOLD``), the response shape is the
        receipt-only envelope: callers always poll the status endpoint to
        fetch the download URL. This keeps the API contract identical
        regardless of which side of the cutover the job lands on.
      examples:
        - job_id: AbCdEfGhIjKlMnOpQrStUv
          poll_url: /products/api/v1/labels/export/AbCdEfGhIjKlMnOpQrStUv/
          status: pending
      properties:
        job_id:
          description: >-
            Identifier of the queued `LabelExportJob` (22-char shortuuid). Use
            to poll the status endpoint at `GET /labels/export/{job_id}/`.
          title: Job Id
          type: string
        status:
          $ref: '#/components/schemas/LabelExportStatusEnum'
          description: >-
            Initial lifecycle state at submission. Typically `pending`; for very
            small jobs that ran inline this may already be `completed` or
            `failed`.
        poll_url:
          description: >-
            Relative URL of the polling endpoint for this job — combine with the
            API host to retrieve status. Embeds the same `job_id` for
            convenience.
          title: Poll Url
          type: string
      required:
        - job_id
        - status
        - poll_url
      title: LabelExportAcceptedOut
      type: object
    ErrorOut:
      description: |-
        RFC 9457 Problem Details response.

        All API errors are returned in this format with Content-Type:
        application/problem+json.
      examples:
        - detail: The requested resource was not found.
          error_code: not_found
          retryable: false
          status: 404
          timestamp: '2026-03-31T12:00:00+00:00'
          title: Not Found
          type: https://closient.com/docs/errors/not_found
        - detail: Validation error.
          details:
            - loc:
                - body
                - name
              msg: Field required
              type: missing
          error_code: validation_error
          retryable: false
          status: 422
          timestamp: '2026-03-31T12:00:00+00:00'
          title: Validation Error
          type: https://closient.com/docs/errors/validation_error
        - detail: Rate limit exceeded. Please try again later.
          error_code: rate_limited
          retry_after: 31
          retryable: true
          status: 429
          timestamp: '2026-03-31T12:00:00+00:00'
          title: Rate Limited
          type: https://closient.com/docs/errors/rate_limited
      properties:
        type:
          description: URI reference identifying the error type.
          title: Type
          type: string
        title:
          description: Short human-readable summary of the error.
          title: Title
          type: string
        status:
          description: HTTP status code.
          title: Status
          type: integer
        detail:
          description: Human-readable explanation of this specific occurrence.
          title: Detail
          type: string
        error_code:
          description: Machine-readable error code (e.g. not_found, unauthorized).
          title: Error Code
          type: string
        retryable:
          default: false
          description: Whether retrying the same request can succeed.
          title: Retryable
          type: boolean
        timestamp:
          description: ISO 8601 timestamp of when the error occurred.
          title: Timestamp
          type: string
        retry_after:
          anyOf:
            - type: integer
            - type: 'null'
          description: Seconds to wait before retrying (when applicable).
          title: Retry After
        owner_action_required:
          anyOf:
            - type: boolean
            - type: 'null'
          description: Whether the error requires account owner intervention.
          title: Owner Action Required
        details:
          description: Additional context (validation errors, etc.).
          title: Details
      required:
        - type
        - title
        - status
        - detail
        - error_code
        - timestamp
      title: ErrorOut
      type: object
    LabelExportScopeEnum:
      description: |-
        Which entities the export resolves against.

        Mirrors :class:`apps.products.models.LabelExportScope`. ``products``
        renders one GTIN-level label per product (a single product, or every
        product the org owns); ``lots`` / ``serials`` render per lot / serial.
      enum:
        - products
        - lots
        - serials
      title: LabelExportScopeEnum
      type: string
    LabelExportTypeEnum:
      description: >-
        What kind of asset a bulk label-export job produces.


        Mirrors :class:`apps.products.models.LabelExportType`. Drives renderer

        dispatch inside
        :func:`apps.products.services.bulk_label_export.process_label_export_job`.
      enum:
        - qr_codes
        - datamatrix
        - barcodes_1d
        - codes_combined
        - sheet_pdf
      title: LabelExportTypeEnum
      type: string
    LabelExportFileFormatEnum:
      description: |-
        Output container/format produced by a bulk label-export job.

        Mirrors :class:`apps.products.models.LabelExportFileFormat`. ``svg`` /
        ``png`` / ``eps`` are ZIPs of one symbol per entity; ``pdf`` is a
        multi-up label sheet (only valid with ``export_type='sheet_pdf'``).
      enum:
        - svg
        - png
        - pdf
        - eps
      title: LabelExportFileFormatEnum
      type: string
    LabelExportFiltersIn:
      description: >-
        Optional selection filters narrowing the entities included in the
        export.


        Every field is optional; omitting all of them resolves to "every active

        lot/serial in the caller's org" within the chosen scope. The combination

        is AND across keys (a request with both ``gtins`` and ``never_printed``

        matches lots whose product GTIN is in the list **and** that have never

        been emitted in a PRINTED ``LifecycleEvent``). See

        :func:`apps.products.services.bulk_label_export._resolve_lots` for the

        full enumeration of recognized keys.
      examples:
        - gtins:
            - '00012345678905'
          never_printed: true
        - lot_ids:
            - abc123def456
      properties:
        gtins:
          anyOf:
            - items:
                type: string
              maxItems: 5000
              type: array
            - type: 'null'
          description: >-
            Filter to entities whose product GTIN is in this list. GTINs are
            matched against the canonical 14-digit form stored on
            `Product.gtin`.
          title: Gtins
        product_id:
          anyOf:
            - type: string
            - type: 'null'
          description: >-
            Filter to entities of a single Product, identified by its database
            id (UUID or shortuuid). Mutually compatible with `never_printed`
            filtering.
          title: Product Id
        lot_ids:
          anyOf:
            - items:
                type: string
              maxItems: 5000
              type: array
            - type: 'null'
          description: >-
            Explicit list of `ProductLot` ids (UUID or shortuuid) to include.
            Only meaningful when `scope='lots'`.
          title: Lot Ids
        serial_ids:
          anyOf:
            - items:
                type: string
              maxItems: 5000
              type: array
            - type: 'null'
          description: >-
            Explicit list of `ProductSerial` ids (UUID or shortuuid) to include.
            Only meaningful when `scope='serials'`.
          title: Serial Ids
        lot_run_id:
          anyOf:
            - type: string
            - type: 'null'
          description: >-
            Include every lot generated by this `LotRun` (id as UUID or
            shortuuid). Only meaningful when `scope='lots'`.
          title: Lot Run Id
        never_printed:
          anyOf:
            - type: boolean
            - type: 'null'
          description: >-
            When `true`, restrict to entities that have never been emitted in a
            `PRINTED` LifecycleEvent — useful for re-running export of fresh
            stock.
          title: Never Printed
        created_after:
          anyOf:
            - format: date-time
              type: string
            - type: 'null'
          description: Include only entities created at or after this ISO-8601 timestamp.
          title: Created After
        created_before:
          anyOf:
            - format: date-time
              type: string
            - type: 'null'
          description: Include only entities created at or before this ISO-8601 timestamp.
          title: Created Before
      title: LabelExportFiltersIn
      type: object
    LabelExportAssetConfigIn:
      description: |-
        Renderer-side knobs (sheet template choice, etc.).

        Optional; omit for renderer defaults. The handful of keys recognized
        today are mirrored from what the Dashboard already supports — see
        :func:`apps.products.services.bulk_label_export._render_sheet_pdf` for
        the SHEET_PDF template selection logic.
      examples:
        - sheet_template: 63up_1in_letter
      properties:
        sheet_template:
          anyOf:
            - maxLength: 64
              type: string
            - type: 'null'
          description: >-
            Identifier of a registered label sheet template (only meaningful
            when `export_type='sheet_pdf'`). Defaults to the configured
            `DEFAULT_TEMPLATE_ID` when omitted.
          title: Sheet Template
      title: LabelExportAssetConfigIn
      type: object
    LabelExportStatusEnum:
      description: |-
        Lifecycle state of a bulk label-export job.

        Mirrors :class:`apps.products.models.LabelExportStatus`. ``pending``
        and ``processing`` are in-flight; ``completed`` and ``failed`` are
        terminal. The polling endpoint surfaces this directly.
      enum:
        - pending
        - processing
        - completed
        - failed
      title: LabelExportStatusEnum
      type: string
  securitySchemes:
    APIKeyHeaderAuth:
      type: apiKey
      in: header
      name: X-API-Key
    OAuthTokenAuth:
      type: http
      scheme: bearer
    SessionAuth:
      type: apiKey
      in: cookie
      name: sessionid

````