API ReferenceEndpointsMedia

Media

Media assets — product images, brand logos, variant photos. Endpoints here issue presigned upload URLs, list/move/delete assets, and manage folders. Storage backend is configurable (S3, R2, local).

Every operation below is on the public API surface — both paths are under /v1 and take Authorization: Bearer brainerce_*.

POST /v1/media takes multipart/form-data with either a binary file part (≤ 10 MB) or a sourceUrl field to ingest a remote image, fetched server-side through the SSRF-safe downloader. On this surface the bytes go through that one endpoint — there is no presigned-upload or folder-management operation in the public API. (The dashboard’s own media screens use a separate internal flow that is not part of the public contract and is not documented here.)

List media library assets

Lists the store media library, newest first, 20 per page by default.

GET
/v1/media
AuthorizationBearer <token>

Admin API key (server-to-server). Issue via the dashboard at Settings → API keys. Plain-text key is shown once — store in a secret manager. Format: Authorization: Bearer brainerce_xxxxxxxxx.

In: header

Query Parameters

page?integer

1-based page number.

Default1
Range1 <= value
limit?integer

Items per page. Default 20. Maximum 100. Larger values are capped to 100.

Default20
Range1 <= value <= 100
search?string

Case-insensitive match on the asset name or file name.

curl -X GET "https://api.brainerce.com/api/v1/media?page=1&limit=20&search=string"
{
  "data": [
    {
      "id": "clx1a2b3c4d5e6f7g8h9",
      "url": "https://cdn.example.com/media/tshirt.jpg",
      "key": "media/tshirt.jpg",
      "thumbnailUrl": "https://cdn.example.com/media/tshirt-thumb.jpg",
      "mimeType": "image/webp",
      "width": 1200,
      "height": 800,
      "size": 245123,
      "name": "tshirt.jpg",
      "alt": "Black t-shirt on a hanger",
      "altTranslations": {},
      "createdAt": "2019-08-24T14:15:22Z"
    }
  ],
  "meta": {
    "page": 1,
    "limit": 20,
    "total": 137,
    "totalPages": 7
  }
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/media"
}

{
  "statusCode": 403,
  "code": "INSUFFICIENT_SCOPE",
  "message": "API key does not have the required scope: products:write",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/media"
}

{
  "statusCode": 429,
  "code": "RATE_LIMITED",
  "message": "Too many requests",
  "details": {
    "retryAfterSeconds": 12
  },
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/store"
}

Upload or ingest a media asset

Send multipart/form-data with either a binary file part (≤10 MB) or a sourceUrl field to ingest a remote image by URL (fetched server-side through the SSRF-safe downloader). Returns the created asset.

POST
/v1/media
AuthorizationBearer <token>

Admin API key (server-to-server). Issue via the dashboard at Settings → API keys. Plain-text key is shown once — store in a secret manager. Format: Authorization: Bearer brainerce_xxxxxxxxx.

In: header

file?string

Binary file (≤10 MB)

sourceUrl?string

Remote http(s) image URL to ingest (alternative to file)

curl -X POST "https://api.brainerce.com/api/v1/media"
{
  "id": "clx1a2b3c4d5e6f7g8h9",
  "url": "https://cdn.example.com/media/tshirt.jpg",
  "key": "media/tshirt.jpg",
  "thumbnailUrl": "https://cdn.example.com/media/tshirt-thumb.jpg",
  "mimeType": "image/webp",
  "width": 1200,
  "height": 800,
  "size": 245123,
  "name": "tshirt.jpg",
  "alt": "Black t-shirt on a hanger",
  "altTranslations": {},
  "createdAt": "2019-08-24T14:15:22Z"
}

{
  "statusCode": 400,
  "code": "VALIDATION_FAILED",
  "message": "name should not be empty, price must be a positive number",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/media"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/media"
}

{
  "statusCode": 403,
  "code": "INSUFFICIENT_SCOPE",
  "message": "API key does not have the required scope: products:write",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/media"
}

{
  "statusCode": 429,
  "code": "RATE_LIMITED",
  "message": "Too many requests",
  "details": {
    "retryAfterSeconds": 12
  },
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/media"
}

Get a media asset by ID

Returns one media asset of this store.

GET
/v1/media/{id}
AuthorizationBearer <token>

Admin API key (server-to-server). Issue via the dashboard at Settings → API keys. Plain-text key is shown once — store in a secret manager. Format: Authorization: Bearer brainerce_xxxxxxxxx.

In: header

Path Parameters

idstring

Asset ID

curl -X GET "https://api.brainerce.com/api/v1/media/string"
{
  "id": "clx1a2b3c4d5e6f7g8h9",
  "url": "https://cdn.example.com/media/tshirt.jpg",
  "key": "media/tshirt.jpg",
  "thumbnailUrl": "https://cdn.example.com/media/tshirt-thumb.jpg",
  "mimeType": "image/webp",
  "width": 1200,
  "height": 800,
  "size": 245123,
  "name": "tshirt.jpg",
  "alt": "Black t-shirt on a hanger",
  "altTranslations": {},
  "createdAt": "2019-08-24T14:15:22Z"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/media/{id}"
}

{
  "statusCode": 403,
  "code": "INSUFFICIENT_SCOPE",
  "message": "API key does not have the required scope: products:write",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/media/{id}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/media/{id}"
}

{
  "statusCode": 429,
  "code": "RATE_LIMITED",
  "message": "Too many requests",
  "details": {
    "retryAfterSeconds": 12
  },
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/store"
}

Update media asset metadata

Update the asset’s alt text and/or display name. Supports Idempotency-Key.

PATCH
/v1/media/{id}
AuthorizationBearer <token>

Admin API key (server-to-server). Issue via the dashboard at Settings → API keys. Plain-text key is shown once — store in a secret manager. Format: Authorization: Bearer brainerce_xxxxxxxxx.

In: header

Path Parameters

idstring

Asset ID

Header Parameters

Idempotency-Key?string

Client-supplied key (a UUID v4 is the recommended form, max 255 characters) that makes this mutation safe to retry.

  • Replay window: 24 hours. The first response for a key is cached; a retry inside the window with the same request body returns the original status and body without re-running the handler.
  • Reusing a key with a different body returns 409 Conflict with code: "IDEMPOTENCY_KEY_REUSED". The request fingerprint (method + path + body hash) is compared against the stored one; a mismatch is refused rather than served the old response.
  • Keys are scoped to the calling credential and store — two API keys may safely use the same key value.
  • Error responses are cached too, so a retry of a request that failed validation returns the same 400 immediately.
  • Sending this header on a GET returns 400 with code: "IDEMPOTENCY_KEY_NOT_SUPPORTED" — GETs are already idempotent.
  • Support is per-route and this parameter is the authoritative signal: a key sent to a route that does not declare it is accepted and silently ignored.

See /docs/api/idempotency.

Lengthlength <= 255
alt?string

Accessibility alt text for the asset.

Lengthlength <= 1000
altTranslations?object | null

Alt text per language, keyed by locale code (e.g. { "he": "...", "fr": "..." }); alt stays the default language. Replaces the whole map: send null or {} to clear it, an empty string to drop one language. Storefronts return the entry for the requested locale as alt, falling back to the default. The store's default language is alt itself: a key for it is not stored (it is used as alt when you send none).

name?string

Display name / title of the asset in the media library.

Lengthlength <= 255
curl -X PATCH "https://api.brainerce.com/api/v1/media/string" \  -H "Idempotency-Key: string" \  -H "Content-Type: application/json" \  -d '{}'
{
  "id": "clx1a2b3c4d5e6f7g8h9",
  "url": "https://cdn.example.com/media/tshirt.jpg",
  "key": "media/tshirt.jpg",
  "thumbnailUrl": "https://cdn.example.com/media/tshirt-thumb.jpg",
  "mimeType": "image/webp",
  "width": 1200,
  "height": 800,
  "size": 245123,
  "name": "tshirt.jpg",
  "alt": "Black t-shirt on a hanger",
  "altTranslations": {},
  "createdAt": "2019-08-24T14:15:22Z"
}

{
  "statusCode": 400,
  "code": "VALIDATION_FAILED",
  "message": "name should not be empty, price must be a positive number",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/media/{id}"
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/media/{id}"
}

{
  "statusCode": 403,
  "code": "INSUFFICIENT_SCOPE",
  "message": "API key does not have the required scope: products:write",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/media/{id}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/media/{id}"
}

{
  "statusCode": 429,
  "code": "RATE_LIMITED",
  "message": "Too many requests",
  "details": {
    "retryAfterSeconds": 12
  },
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/store"
}

Delete a media asset

Soft-deletes the asset and removes its key from every product/variant/category/brand/store/modifier that referenced it. Supports Idempotency-Key.

DELETE
/v1/media/{id}
AuthorizationBearer <token>

Admin API key (server-to-server). Issue via the dashboard at Settings → API keys. Plain-text key is shown once — store in a secret manager. Format: Authorization: Bearer brainerce_xxxxxxxxx.

In: header

Path Parameters

idstring

Asset ID

Header Parameters

Idempotency-Key?string

Client-supplied key (a UUID v4 is the recommended form, max 255 characters) that makes this mutation safe to retry.

  • Replay window: 24 hours. The first response for a key is cached; a retry inside the window with the same request body returns the original status and body without re-running the handler.
  • Reusing a key with a different body returns 409 Conflict with code: "IDEMPOTENCY_KEY_REUSED". The request fingerprint (method + path + body hash) is compared against the stored one; a mismatch is refused rather than served the old response.
  • Keys are scoped to the calling credential and store — two API keys may safely use the same key value.
  • Error responses are cached too, so a retry of a request that failed validation returns the same 400 immediately.
  • Sending this header on a GET returns 400 with code: "IDEMPOTENCY_KEY_NOT_SUPPORTED" — GETs are already idempotent.
  • Support is per-route and this parameter is the authoritative signal: a key sent to a route that does not declare it is accepted and silently ignored.

See /docs/api/idempotency.

Lengthlength <= 255
curl -X DELETE "https://api.brainerce.com/api/v1/media/string" \  -H "Idempotency-Key: string"
{
  "success": true
}

{
  "statusCode": 401,
  "code": "UNAUTHORIZED",
  "message": "Invalid or expired credential",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/media/{id}"
}

{
  "statusCode": 403,
  "code": "INSUFFICIENT_SCOPE",
  "message": "API key does not have the required scope: products:write",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/media/{id}"
}

{
  "statusCode": 404,
  "code": "RESOURCE_NOT_FOUND",
  "message": "Resource not found",
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/media/{id}"
}

{
  "statusCode": 429,
  "code": "RATE_LIMITED",
  "message": "Too many requests",
  "details": {
    "retryAfterSeconds": 12
  },
  "timestamp": "2026-08-23T10:15:00.000Z",
  "path": "/api/v1/store"
}