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

# Attach a product image

> Step three of three. A product holds at most 5 images. The content type is checked again here, so a file uploaded as a document is rejected by this endpoint even though the upload session accepted it. Attaching media returns an approved product to review, taking it off sale until it is approved again.



## OpenAPI

````yaml /api-reference/commerce-v1.openapi.json post /v1/commerce/products/{product_id}/images
openapi: 3.0.3
info:
  description: >-
    Public REST API for COPE vendor integrations. Authenticate with a COPE API
    key or Clerk bearer token and call the endpoints described below.
  title: COPE Public API
  version: v1
servers:
  - description: Production
    url: https://api.cope.com
security:
  - cope_sk: []
paths:
  /v1/commerce/products/{product_id}/images:
    post:
      tags:
        - Products
      summary: Attach a product image
      description: >-
        Step three of three. A product holds at most 5 images. The content type
        is checked again here, so a file uploaded as a document is rejected by
        this endpoint even though the upload session accepted it. Attaching
        media returns an approved product to review, taking it off sale until it
        is approved again.
      operationId: product_media.images.create
      parameters:
        - description: product_id public identifier.
          example: prod_A1b2C3d4
          in: path
          name: product_id
          required: true
          schema:
            example: prod_A1b2C3d4
            pattern: ^prod_[A-Za-z0-9]{8,32}$
            type: string
      requestBody:
        content:
          application/json:
            examples:
              attach_image:
                summary: Attach the uploaded image to the product — step three of three
                value:
                  upload_token: <upload_token from POST /v1/commerce/uploads>
            schema:
              properties:
                upload_token:
                  description: >-
                    Token returned by the upload session, after the bytes have
                    been uploaded.
                  type: string
              required:
                - upload_token
              type: object
      responses:
        '201':
          content:
            application/json:
              schema:
                additionalProperties: false
                properties:
                  data:
                    additionalProperties: false
                    properties:
                      byte_size:
                        nullable: true
                        type: integer
                      content_type:
                        nullable: true
                        type: string
                      filename:
                        type: string
                      id:
                        example: img_A1b2C3d4E5f6G7h8
                        pattern: ^img_[A-Za-z0-9]{8,32}$
                        type: string
                      kind:
                        type: string
                      thumbnail_url:
                        nullable: true
                        type: string
                      url:
                        nullable: true
                        type: string
                    required:
                      - id
                      - kind
                      - filename
                      - content_type
                      - byte_size
                      - url
                      - thumbnail_url
                    type: object
                required:
                  - data
                type: object
          description: Successful response
        '400':
          content:
            application/problem+json:
              examples:
                upload_token_rejected:
                  summary: Rejected upload token
                  value:
                    code: invalid_request
                    detail: null
                    errors:
                      - code: invalid
                        detail: upload_token has already been consumed
                        param: upload_token
                    request_id: req_123
                    status: 400
                    title: Invalid Request
                    type: https://docs.cope.com/errors/invalid_request
                unparsable_request:
                  summary: Unparsable request
                  value:
                    code: invalid_request
                    detail: >-
                      The request could not be parsed. Check the query string
                      and the request body.
                    request_id: req_123
                    status: 400
                    title: Invalid Request
                    type: https://docs.cope.com/errors/invalid_request
              schema:
                $ref: '#/components/schemas/PublicProblemDetail'
          description: Invalid request
        '401':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/PublicProblemDetail'
          description: Missing or invalid bearer token
        '403':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/PublicProblemDetail'
          description: Bearer token is not authorized for this route
        '404':
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/PublicProblemDetail'
          description: Resource not found
        '422':
          content:
            application/problem+json:
              examples:
                validation_failed:
                  summary: Validation failed
                  value:
                    code: validation_failed
                    detail: null
                    errors:
                      - code: blank
                        detail: Name can't be blank
                        param: name
                    request_id: req_123
                    status: 422
                    title: Validation Failed
                    type: https://docs.cope.com/errors/validation_failed
              schema:
                $ref: '#/components/schemas/PublicProblemDetail'
          description: Public commerce validation problem
      security:
        - cope_sk: []
components:
  schemas:
    PublicProblemDetail:
      additionalProperties: false
      properties:
        code:
          type: string
        detail:
          nullable: true
          type: string
        errors:
          items:
            additionalProperties: false
            properties:
              code:
                type: string
              detail:
                type: string
              param:
                type: string
            required:
              - code
              - detail
            type: object
          type: array
        request_id:
          type: string
        status:
          type: integer
        title:
          type: string
        type:
          type: string
      required:
        - type
        - title
        - status
        - code
        - request_id
      type: object
  securitySchemes:
    cope_sk:
      description: >-
        Bearer credential for the public API. Vendor integrations should send a
        live COPE API key (`ck_live_*`; keys issued earlier as `cope_sk_live_*`
        keep working). Clerk bearer tokens are also accepted when paired with
        `X-Cope-Business-Id`.
      scheme: bearer
      type: http

````