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.
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
1-based page number.
11 <= valueItems per page. Default 20. Maximum 100. Larger values are capped to 100.
201 <= value <= 100Case-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.
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
Binary file (≤10 MB)
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.
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
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.
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
Asset ID
Header Parameters
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 Conflictwithcode: "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
GETreturns400withcode: "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.
length <= 255Accessibility alt text for the asset.
length <= 1000Alt 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).
Display name / title of the asset in the media library.
length <= 255curl -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.
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
Asset ID
Header Parameters
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 Conflictwithcode: "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
GETreturns400withcode: "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.
length <= 255curl -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"
}