Sync a catalogue
Push suppliers, colours and products from another system, and keep them in step.
A catalogue sync writes in a fixed order, because products reference the other two: suppliers by id, colours by name.
Create the suppliers
A variant’s supplierIds must name suppliers that already exist, so send those
first and keep the ids you get back.
curl -X POST "https://app.bloomcount.com/api/v1/suppliers" \
-H "Authorization: Bearer bloomcount_..." \
-H "Content-Type: application/json" \
-d '{ "name": "Marilliam Flowers", "contactEmail": "orders@marilliam.example" }'Suppliers are never deleted, because products keep pointing at them. Retire one
with POST /api/v1/suppliers/{id}/archive, which leaves history intact and
stops it taking new lines, and bring it back with unarchive.
Check the colours
Variants carry colours by name, not by id, so a name that does not exist is simply a tag nothing else shares. Read the palette first and create what is missing:
curl "https://app.bloomcount.com/api/v1/colors" \
-H "Authorization: Bearer bloomcount_..."{
"items": [
{ "colorId": "col_...", "name": "Blush", "hex": "#f4c2c2", "isPreset": true }
]
}Preset colours cannot be edited or deleted; your own can. Categories work the
same way and are also referenced by name from a product’s category.
Upsert the products
One call carries up to 500 rows, each a product with one variant. Omitting
productId creates; supplying it updates, patching only the fields you send.
curl -X POST "https://app.bloomcount.com/api/v1/products" \
-H "Authorization: Bearer bloomcount_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: catalogue-2026-09-06-batch-1" \
-d '{
"items": [
{
"name": "Pink Mondial",
"category": "Roses",
"productType": "flower",
"variant": {
"name": "50cm",
"stemLength": 50,
"unitsPerPack": 10,
"colors": ["Blush"],
"supplierIds": ["sup_..."],
"supplierMinPacks": [{ "supplierId": "sup_...", "minPacks": 5 }]
}
}
]
}'The response reports each row separately:
{
"results": [
{ "index": 0, "action": "created", "productId": "prod_...", "variantId": "var_..." }
],
"errors": []
}A row that fails lands in errors with its index and a code, and the rest of
the batch still lands. That is the whole point of the bulk shape: one bad
supplier reference does not cost you the other 499 rows.
Retire what has gone
Deletes are per resource, not part of the batch:
curl -X DELETE "https://app.bloomcount.com/api/v1/products/prod_..." \
-H "Authorization: Bearer bloomcount_..."If the product is on an event, this returns 409 with
product_on_event_items:3 rather than quietly removing those lines. Add
?force=true when removing it along with its history is what you mean. Deleting
a single variant works the same way, at
DELETE /api/v1/products/{id}/variants/{variantId}.
Running it again
Two things make a repeated sync safe.
Send an Idempotency-Key per batch. A replay within 24 hours returns the
first response verbatim instead of writing again, so a timeout you retry cannot
double-create.
Update by id, not by name. Keep your own map from your system’s identifier
to the prod_/var_ ids the first sync returned. Matching on name instead
means a rename in either system creates a duplicate.