Images

Flint stores image bytes for every image attached through the API. You provide an HTTPS source URL on a catalog or branding write. Flint fetches, validates, normalizes, and stores the image, then returns an immutable Flint URL with its dimensions. Buyer-facing responses use that stored URL instead of depending on the source host.

Attach an image#

Use an ImageInput wherever a resource accepts an image or image gallery:

JSON
{
  "source_url": "https://assets.example.com/products/travel-mug.png",
  "alt": "Matte black travel mug with a closed lid",
  "external_reference_id": "media_8421"
}
  • source_url is required and may contain up to 8,192 characters.
  • alt is optional and may contain up to 512 characters. Use meaningful text when the image communicates information.
  • external_reference_id is optional and may contain 1 to 255 characters without leading or trailing whitespace. It is an opaque, case-sensitive value. Use it to associate the attachment with a stable media ID in your system.

The source must use HTTPS on port 443. URLs with credentials or fragments are rejected. Query strings are accepted for signed URLs, but Flint never returns the source URL or includes it in a public error.

JPEG, PNG, and WebP are supported. The source image must be no larger than 5 MB or 40 megapixels, with each dimension between 1 and 12,000 pixels. Animated images are rejected. Delivery variants may still be encoded as AVIF for compatible browsers.

Note: Use an idempotency key for safe retries

Idempotency-Key is optional, including for writes with an external source_url. Generate and persist one key before the first request when you need safe timeout or transport retries. Reuse that key only for the same logical mutation.

When you omit the header, Flint generates an effective key and returns it in the Idempotency-Key response header. That returned key can replay the result after you receive the first response, but it cannot protect a retry when the first response is lost. Supply your own key for that case.

This example replaces a product's gallery after reading version 7:

cURL
curl -X PATCH https://api.withflintpay.com/v1/products/prod_01JXYZ1234567890ABCDEFGHJK \
  -H "Authorization: Bearer flint_test_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 638d2293-0637-4704-adf0-89c47277e01b" \
  -d '{
    "expected_version": 7,
    "images": [
      {
        "source_url": "https://assets.example.com/products/travel-mug-front.png",
        "alt": "Front view of a matte black travel mug",
        "external_reference_id": "media_8421"
      },
      {
        "source_url": "https://assets.example.com/products/travel-mug-open.png",
        "alt": "Travel mug with the lid open",
        "external_reference_id": "media_8422"
      }
    ]
  }'

The resource response contains canonical image data:

JSON
{
  "url": "https://images.withflintpay.com/canonical/mer_01JXYZ1234567890ABCDEFGHJK/live/img_01JXYZ1234567890ABCDEFGHJK/original.png",
  "alt": "Front view of a matte black travel mug",
  "external_reference_id": "media_8421",
  "width": 1600,
  "height": 1200
}

Store the returned url when you need to reference the same bytes on a transaction input. Do not reconstruct or modify Flint image URLs.

Products, variants, bundles, and subscription plans accept ordered galleries of up to eight images on create and update. Gallery updates use full replacement:

  • Include images only when you want to replace the gallery.
  • Include the resource's current version as expected_version when images is present in an update.
  • Send images: [] to clear the gallery.
  • Send a non-empty array to replace the complete gallery in that order.
  • Reorder images by sending the complete array in the new order.

Send the array to the owning resource's POST or PATCH endpoint. Product variant images belong on PATCH /v1/products/{product_id}/variants/{variant_id}.

The first image is the primary image used for line-item thumbnails and transaction snapshots.

Payment links and merchant logos are single-image fields. Omit the field to keep it unchanged, send null to clear it, or send an ImageInput to replace it.

Variant inheritance#

A product variant can inherit its product gallery or replace it completely:

  • images is the authored variant override.
  • effective_images is the display-ready gallery.
  • images_inherited tells you whether effective_images came from the product.

A non-empty variant gallery replaces the product gallery. The two galleries are not merged. Send images: [] with expected_version in a variant update to remove the override and resume inheritance. Buyer-facing renderers should use effective_images; catalog synchronization should read and write images.

Avoid fetching an unchanged source#

Flint must fetch and decode an external URL before it can determine whether the bytes already exist. If your media record has not changed, skip the gallery replacement request. external_reference_id gives your synchronization process a stable value to compare without relying on signed URL text.

Within one gallery, both source_url and external_reference_id must be unique. One API mutation may ingest at most 32 external images and 100 megapixels in total. For larger catalog updates, create or update resources in smaller requests.

Images on orders and invoices#

Catalog-backed line items automatically snapshot the resolved primary image. Later catalog edits do not change an existing order, invoice, or subscription snapshot.

Transaction creation does not fetch external image hosts. An ad hoc line item may omit its image or use ImageReferenceInput with a canonical Flint URL previously returned for the same merchant and environment:

JSON
{
  "image": {
    "url": "https://images.withflintpay.com/canonical/mer_01JXYZ1234567890ABCDEFGHJK/live/img_01JXYZ1234567890ABCDEFGHJK/original.png",
    "alt": "Front view of a matte black travel mug"
  }
}

An external URL on an order or invoice input is rejected. Attach it to a catalog or branding resource first, then use the returned Flint URL.

Concurrent updates#

Products, variants, bundles, and subscription plans return a resource version. Send the version you read as expected_version whenever you replace a gallery. A stale value returns 409 with error.code: CONCURRENT_MODIFICATION and does not change the resource or its gallery. Payment links and merchant logos keep their documented image-specific concurrency behavior.

Errors and retries#

Image failures use the standard error envelope. The param points to the exact input, including nested paths such as variants[3].images[1].source_url. Each error includes remediation that indicates whether retrying is appropriate.

Common codes include:

Storage quota errors include machine-readable byte totals and, when available, the next ready-but-unattached image expiration time. Do not loop on quota failures. Remove unused attachments or wait for the stated expiration before retrying.

An idempotent retry can replay a completed image result, but it does not take ownership of an attempt that is still processing. IMAGE_INGESTION_IN_PROGRESS is the only image-ingestion error that asks you to retry the same key. Once an owned attempt returns an error, that key replays the stable error. When remediation.next_actions contains start_new_image_ingestion, wait for any stated retry delay and submit the logical mutation again with a new idempotency key.

Environment isolation#

Catalog and checkout images belong to one merchant and one environment. A test or sandbox image URL cannot attach to a live resource, and one merchant cannot attach another merchant's URL. Merchant logos and icons are global to that merchant. A logo URL is accepted only by logo, and an icon URL only by icon. Icons must be square and at least 128 by 128 pixels after processing.

Environment copies do not carry image assets across the boundary. Attach images separately in the target environment.

Next steps#

Was this helpful?