bruxel.ai

Bruxel Public API · v1

Integrate Bruxel into your stack

A REST API to generate SEO- and GEO-optimized product descriptions straight from your backend, PIM or e-commerce. Push your catalog, generate descriptions and export — without touching the UI.

Base URLhttps://bruxel.ai/api/public/v1Authentication Bearer bxk_…Format JSON
Start

Getting started

Create an API key, learn the base URL and make your first authenticated call.

Go to Quickstart →
Security

Authentication

Per-organization bxk_ keys, scope-based permissions and one-time delivery.

See authentication →
OpenAPI

API reference

Explore the endpoints with their schemas, parameters and example responses.

See the reference →
Code

Examples

End-to-end flows in curl, Python and JavaScript, ready to copy.

See examples →

Quickstart

From zero to a generated description. Replace bxk_your_key with the one you created in the console.

# 1) Verifica que tu llave funciona
curl -s https://bruxel.ai/api/public/v1/me \
  -H "Authorization: Bearer bxk_tu_llave"

# 2) Empuja productos a tu catálogo (upsert por SKU)
curl -s -X POST https://bruxel.ai/api/public/v1/products \
  -H "Authorization: Bearer bxk_tu_llave" \
  -H "Content-Type: application/json" \
  -d '{"products":[{"sku":"MRN-CREW-01","name":"Suéter de lana merino"}]}'

# 3) Genera la descripción (síncrono — cobra créditos)
curl -s -X POST https://bruxel.ai/api/public/v1/generations \
  -H "Authorization: Bearer bxk_tu_llave" \
  -H "Content-Type: application/json" \
  -d '{"product_id":"prod_8f2","languages":["es"]}'

# 4) Exporta tu catálogo timbrado a CSV
curl -s "https://bruxel.ai/api/public/v1/export/catalog.csv?language=es" \
  -H "Authorization: Bearer bxk_tu_llave"

Latest changes

What's new in the public API.

2026-10-09
videocambio

Video: ai_model_id and extra_ref_ids are retired

POST /v1/products/{id}/ai-video and its script no longer accept ai_model_id or extra_ref_ids (“the model in the cuts”): the provider rejects the model's face. Sending them returns 422 (X-Bruxel-Reason: params) without reserving credits. The shot-by-shot video, the one that accepts the model, is only available from the app for now.

2026-10-08
reportesbi

Reports, now through the API too

With the new reports:read scope your BI reads the same reports as the section: GET /v1/reports gives the catalog with how many rows each one has in the period, GET /v1/reports/:id/rows a page as JSON (up to the first 10,000 rows) and GET /v1/reports/:id.csv the full file. The period goes with from, to and tz; brand and status filter where the report uses them.

2026-10-08
flujoswebhooks

Flows notify you: your turn, stopped, finished

Three new webhook events: flow.step.ready (products that reached a step), flow.run.blocked (journeys stopped by an error) and flow.run.completed (journeys that finished). They arrive grouped, one per step or per flow, with a count and a sample of up to 20; the full list comes from GET /v1/flows/:slug/steps/:key/products and GET /v1/flows/:slug/runs, with a cursor. Activating a flow wakes you once per step that has products.

2026-10-07
flujossistemas

Your systems' steps: notifications and calls

A flow step can also be marked done when Bruxel delivers a notification to a system (a webhook answered with a 2xx) or when that system's key reads or writes the product record (checking its journey or history doesn't count). In GET /v1/products/:id/journey, `cause` can be `delivery` or `api_call`, and `event_system` and `event_type` tell the system and what it was: the notification or the operation (`read` or `write`). An event without `system` takes the connected system the key is linked to.

2026-10-06
flujoseventos

Report what happens outside, and read a product's journey

POST /v1/events (scope `events:write`) takes up to 100 events per call: which system, what happened, whether it worked and when. A flow step can be marked done by that event. GET /v1/products/:id/journey (scope `catalog:read`) returns where the product is in each flow and what systems reported about it.

2026-09-27
catálogoatributos

Derived attributes: `derived` and the `derived` 422

`GET /v1/attributes` returns `derived`: `true` when the system sets the value from another field (another attribute, the category or the brand) according to a customer rule. On a product where the derivation applies, `PUT /v1/products/{id}/attributes/{code}` answers 422 with `X-Bruxel-Reason: derived` and writes nothing: you change the source and the derived value is recalculated in the same write.

2026-09-25
atributos

Shared lists: `value_list` on each attribute

`GET /v1/attributes` returns `value_list`: the code of the shared list the attribute's options come from, or `null`. A linked attribute's options are read and assigned as always; what changes is that they're edited in the list, from the app.

2026-09-25
catálogokits

Kits: a product's composition, by SKU

`GET` and `PUT /v1/products/{product_id}/components` (scopes `catalog:read`/`catalog:write`) read and write a kit's composition: the products it is made of, with their quantity and in order, plus `part_of` (how many kits the product is in). An unknown SKU is a 422 that names it; a kit has a single level. The upsert and the import also build kits from `associated_skus` or `kit_components` (`sku=quantity`), and channels emit them with the `kit.components` column.

2026-09-25
catálogoatributoscanales

A value per channel: `is_channel_scoped` and `channel`

`GET /v1/attributes` says which attributes have their own value per channel (`is_channel_scoped`). For those, `PUT /v1/products/{id}/attributes/{code}` accepts `channel` (the channel's `slug`) to write THAT channel's value without touching the master, and `GET …/attributes?channel=` reads it with a fallback to the master: the same value the channel's CSV carries. An attribute that is not per channel or a channel that does not exist is a 422.

2026-09-25
canalesatributos

Each option's value in each channel: `map` and `strict`

`GET /v1/channels` now returns, per column, `map` (the value THAT file writes for each option of a list: “Rojo” → “Red” for Amazon, “001” for the ERP) and `strict` (an option with no value for the channel comes out empty and the product counts as incomplete in `readiness`). The key is the text the cell would have without the map, in the channel's language. With `null`/`false` the cell comes out as always.

2026-09-24
catálogocanales

Is this product complete for the channel? — per product

`GET /v1/products/{id}/channels/readiness` (scope `channels:read`) says, for each configured channel, whether the product would come out complete in its CSV: `published` (a sheet is published in the channel's language), `ready`, and `missing` with each missing column and its source (`output.{field}`, `attribute.{code}`, `media.*`). It is what a middleware checks before creating the product in the store. Same resolver as the file; no charge.

2026-09-24
mediawebhooks

Upload and link images through the API (media:write)

New `media:write` scope and four routes: `POST /v1/media/presign` reserves quota and returns the S3 signed POST; `POST /v1/media/{asset_id}/complete` confirms by binary signature and real size; `POST /v1/products/{id}/media` links the asset with its role and `DELETE /v1/products/{id}/media/{asset_id}` unlinks it. It is the same DAM door the app uses: the server never sees the bytes, quota is reserved before signing and a file that lies about its type is discarded.

2026-09-24
catálogoatributosidiomas

Write an attribute value in a language

`PUT /v1/products/{id}/attributes/{code}` accepts `language`: it writes the value IN THAT LANGUAGE for a localizable attribute; without it, the value goes to the global slot as always. `GET /v1/attributes` says up front which attributes accept it (`is_localizable`). An unsupported language, or a non-localizable attribute, is a 422 naming the code — and `GET …/attributes?language=xx` is now 422 too, instead of a 200 with the global values.

2026-09-24
catálogoatributos

Attribute groups in GET /v1/attributes

Each attribute carries `group`: the code of the group (the section of the sheet) it belongs to, or `null`. It is the code and not an id because this API speaks in codes: an integrator building one team's row filters by it without a second trip. Groups are defined in the app or with the `group` column of the definitions CSV.

2026-09-24
catálogoatributos

Filter the catalog by an attribute value

`GET /v1/products` accepts `attr=code:value` (repeatable, up to five, ANDed): the option text for a list, Yes/No for a boolean, exact text, number or date; an empty `code:` keeps the products WITHOUT a value. It is the same question a team's queue asks in the app — for instance `attr=ready_to_master_data:Sí&attr=ready_to_sap:` — and whatever cannot be applied is a 422 with the reason, never an empty list.

2026-09-24
catálogowebhookshistorial

A change feed for product records, and a `product.updated` that says what changed

`GET /v1/products/deltas` walks the history of all your product records with a resumable cursor: for each version it tells you the product, the reason and WHAT changed (fields, attribute codes, categories, family), never the values; `attribute=` and `reason=` let you listen to a single flag or reason. And the `product.updated` webhook now fires on any real change to the record — including writing an attribute value, which never notified before — with `version`, `reason` and `changed`. Treat it as a wake-up call: re-read the record or the feed, don't rely on `changed` alone.

2026-09-23
catálogohistorial

Product history records catalog changes

Each product's history now records what happens to it when the catalog changes: deleting an attribute, an option, a category or a family, or renaming an option or a code, leaves a version with `reason` `structure`. And `changes` can carry two new keys: `replaced` (the code now belongs to a different attribute) and `recoded` (the same attribute under another code).

2026-09-20
catálogocanales

Units of measure on attributes and channels

A measure attribute now states which measure it is and which units it takes: `GET /v1/attributes` returns `dimension` and `accepted_units`, and the value `PUT` accepts “1.7 fl oz” on an attribute in ml (it converts on its own). And each channel column can ask for a different unit, different decimals, or the bare number: `GET /v1/channels` exposes it in `columns[]` so you know how to read the cell.

2026-09-17
catálogocanales

Related products via API

You can now read and write which other products each one is offered with — related, cross-sell and upsell — by SKU, and download them in your channel CSV with the column names your store expects.

2026-09-12
contenidopublicación

Publish, approve and unpublish via API

Three new endpoints with their own `content:manage` scope: publish a specific version, approve the editorial workflow candidate, and stop serving a listing. The scope is separate from `catalog:write` on purpose — writing the listing and deciding what your store serves are different things.

2026-09-12
catálogohistorial

Product record history: what changed in a product, and when

GET /v1/products/{product_id}/revisions returns a record's versions, newest first, with what changed in each one: fields, attributes (by code and language), categories and family. From imported data it reports the names of the columns that changed, never their contents. For the integrator who syncs and wants to know what to look at without downloading the whole record again.

2026-09-11
canalesexport

Channels: which columns are required and how many products are ready

GET /v1/channels now returns `columns[].required` and `skip_incomplete` (whether the channel drops incomplete rows from its CSV and feed). And GET /v1/channels/{slug}/readiness tells you how many products would come out complete in the channel, how many have no published content, and which required columns are missing most — with the same criterion the file uses.

2026-09-10
imágenes3D

The product's 3D object, in the same media endpoint

GET /v1/products/{id}/media can now return a fourth role: model_3d. Its url is the GLB and the new usdz_url field is the same model in USDZ, the only thing Quick Look opens on iPhone; on an image or a video it is null. Additive: if you already integrate, your contract does not change — but pick the base image by role, not by index 0.

2026-09-10
atributosexport

The attribute's code in your store

GET /v1/attributes returns external_code: the attribute_code Adobe Commerce uses for each attribute, as entered in Bruxel. When present, /v1/export/catalog.csv names the column that way instead of code. null = no external code; nothing changes.

2026-09-07
video

Video by API: clips up to 30 seconds, with a script

POST /v1/products/{id}/ai-video starts a clip from an image already saved to the product: five seconds at a flat rate, or up to 30 with audio charged per second × quality. With ai_model_id the same model appears in every cut. POST /v1/products/{id}/ai-video/script writes the shot-by-shot script with AI (3 credits, charged on success).

2026-09-07
imágenes

The look of each photo: hairstyle, makeup and wardrobe

POST /v1/products/{id}/ai-media accepts hairstyle, makeup and outfit to direct how the model looks in THAT photo, without touching her identity. All three default to as_model, and with that value nothing changes from before; the job returns them in look.

2026-09-04
imágenes

Clothing and expression in image generation

POST /v1/products/{id}/ai-media accepts subject (product | garment) so the model wears the garment instead of holding it, and expression (natural, smile, laugh, neutral, serious, playful) to pick her face. Both are optional and their defaults reproduce the previous behavior; the job returns them in subject and expression.

2026-09-03
imágeneswebhooks

Images: change feed, webhook and generation by API

GET /v1/media/deltas tells you which products changed their gallery (deletions included); the media.updated event notifies in real time; GET /v1/products/{id}/media carries the real role (swatch shows up), the position and the provenance (origin, synthetic_performer); and with the new media:generate scope you open AI photo sessions from your system (POST /v1/products/{id}/ai-media, GET /v1/ai-media/jobs/{id}, GET /v1/ai-models).

2026-07-21
llavesauth

Per-organization API keys

New bxk_ keys with scope-based permissions (catalog:read, generate, export…), one-time delivery and instant revocation from the console.

2026-07-21
generación

Async batches with idempotency

The POST /v1/batches endpoint for high-volume generation: credit reservation, a required Idempotency-Key and charge-per-result on read.

2026-07-13
export

Catalog export by language

GET /v1/export/catalog.csv?language=es returns your generated catalog in the requested language, in Adobe Commerce format.

See the full changelog →

Bruxel for developers — Public API