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

# Copy one external image into Batch Relay-managed fulfillment storage

> Requires print_orders:write and an Idempotency-Key up to 200 characters. Supply exactly one source: an externally routable HTTPS url is downloaded with SSRF protections, content-validated, SHA-256-addressed, and written immutably to the fulfillment R2 scope; an asset_id is resolved as a ready unexpired same-account external asset or a finalized same-studio platform image and is never copied. Source URLs are not fulfillment URLs and are not retained in the managed asset record.



## OpenAPI

````yaml https://api.batchrelay.com/openapi.json post /v1/assets/ingest
openapi: 3.1.0
info:
  title: Batch Relay API
  version: 1.0.0
  description: >-
    Provider-neutral print catalog and prepared-media validation. This document
    covers only routes owned by the Go service; it deliberately excludes
    migration-proxied order lifecycle routes and all provider payloads.
servers:
  - url: https://api.batchrelay.com
security: []
tags:
  - name: System
  - name: Catalog
  - name: Print orders
  - name: Managed assets
  - name: Template renders
  - name: API accounts
paths:
  /v1/assets/ingest:
    post:
      tags:
        - Managed assets
      summary: Copy one external image into Batch Relay-managed fulfillment storage
      description: >-
        Requires print_orders:write and an Idempotency-Key up to 200 characters.
        Supply exactly one source: an externally routable HTTPS url is
        downloaded with SSRF protections, content-validated, SHA-256-addressed,
        and written immutably to the fulfillment R2 scope; an asset_id is
        resolved as a ready unexpired same-account external asset or a finalized
        same-studio platform image and is never copied. Source URLs are not
        fulfillment URLs and are not retained in the managed asset record.
      operationId: ingestManagedAsset
      parameters:
        - name: Idempotency-Key
          in: header
          required: true
          schema:
            type: string
            minLength: 1
            maxLength: 200
          description: >-
            Required stable key for the managed ingestion request; reuse with a
            different URL/source is rejected.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AssetIngestionRequest'
      responses:
        '201':
          description: Managed immutable asset
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/IngestedAsset'
        '400':
          $ref: '#/components/responses/InvalidRequestJSON'
        '401':
          $ref: '#/components/responses/APIKeyAuthenticationRequired'
        '403':
          $ref: '#/components/responses/APIKeyForbidden'
        '409':
          $ref: '#/components/responses/IdempotencyConflict'
        '413':
          $ref: '#/components/responses/AssetTooLarge'
        '422':
          $ref: '#/components/responses/InvalidAssetSource'
        '503':
          $ref: '#/components/responses/ManagedAssetUnavailable'
      security:
        - bearerAuth: []
components:
  schemas:
    AssetIngestionRequest:
      type: object
      properties:
        url:
          type: string
          format: uri
          pattern: ^https://
          description: >-
            One external source. It is downloaded and never retained as the
            fulfillment URL.
        asset_id:
          type: string
          description: >-
            One already-finalized Batch Relay or platform image asset. It is
            resolved and reused without a byte copy.
        original_filename:
          type: string
          maxLength: 255
      oneOf:
        - required:
            - url
        - required:
            - asset_id
      additionalProperties: false
    IngestedAsset:
      type: object
      required:
        - asset_id
        - storage_scope
        - byte_size
        - content_type
        - format
        - pixel_width
        - pixel_height
        - sha256
        - original_filename
        - reused
      properties:
        asset_id:
          type: string
          description: Opaque identifier for later managed-asset attachment.
        storage_scope:
          type: string
          enum:
            - studio
            - fulfillment
          description: >-
            External URL ingress always returns fulfillment; private delivery
            uses this scope plus object-key lookup.
        byte_size:
          type: integer
          minimum: 1
        content_type:
          type: string
          enum:
            - image/jpeg
            - image/png
            - image/tiff
        format:
          type: string
          enum:
            - jpeg
            - png
            - tiff
        pixel_width:
          type: integer
          minimum: 1
        pixel_height:
          type: integer
          minimum: 1
        sha256:
          type: string
          pattern: ^[a-f0-9]{64}$
        original_filename:
          type: string
        reused:
          type: boolean
          description: >-
            True when the same immutable object already existed or a finalized
            asset_id was reused.
      additionalProperties: false
    Error:
      type: object
      required:
        - error
        - code
        - message
        - docs_url
        - request_id
      properties:
        error:
          type: string
          description: Backward-compatible human-readable error.
        code:
          type: string
          description: Stable machine-readable error code.
        message:
          type: string
        docs_url:
          type: string
          format: uri
        request_id:
          type: string
      additionalProperties: false
  responses:
    InvalidRequestJSON:
      description: >-
        The body was malformed, contained unknown fields, or was not exactly one
        JSON object.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    APIKeyAuthenticationRequired:
      description: A valid Batch Relay API key is required.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    APIKeyForbidden:
      description: The API key cannot perform this operation.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    IdempotencyConflict:
      description: The idempotency key has already been used with a different request.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    AssetTooLarge:
      description: The source image exceeded the managed-ingestion size limit.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    InvalidAssetSource:
      description: >-
        The source was missing, ambiguous, unsafe, unsupported, or could not be
        decoded as an allowed image.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    ManagedAssetUnavailable:
      description: Private R2 storage or managed asset resolution is not configured.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: Batch Relay API key
      description: >-
        Batch Relay scoped API key, not OAuth. Scope names document required key
        capabilities.

````