Skip to content
Bloomcount API
Esc
navigateopen⌘Jpreview

Bulk create or update products + variants

Each item is either a create or an update:

  • Create: omit productId. name, category, and variant.name are required.
  • Update product: pass productId. Only the supplied fields are patched.
  • Update variant: pass both productId and variantId. Variant fields (under variant.*) are patched; supplying any without variantId errors with variantId_required_for_variant_patch.

Returns 201 if any row was created, 200 if every row was an update or errored. Per-row failures are listed in errors; successful rows in results.

A row that fails does not roll back the rows around it, so a batch of 500 with two bad references still writes 498. Match errors[].index back to the row you sent. Send the batch again with Idempotency-Key to retry safely: a replay returns the first response rather than writing twice.

Errors (per row, in errors)

  • missing_required_fields_on_create — a create row without name, category or variant.name.
  • product_not_foundproductId is unknown or belongs to another tenant.
  • variant_not_foundvariantId isn’t on the product named by productId.
  • supplier_not_found / supplier_archived — a supplierIds entry that can’t be used.
  • supplier_default_not_allowed — the hidden Default supplier can’t be assigned explicitly.
  • supplierMinPacks_supplier_mismatch — a minimum names a supplier that isn’t on the variant after the patch.
  • hireable_requires_material_producthireable: true on a flower.

Whole-request failures: 422 too_many_items above 500 rows, and 422 wrong_id_type:… or 422 invalid_id_format when a prefixed ID in the batch doesn’t decode.

POST/api/v1/products
Authorization
AuthorizationBearer token (bloomcount_...) · headerrequired
Header parameters
Idempotency-Keystring
Replays return the cached response. 24h TTL, scoped per API key.
Request body
requiredapplication/json
itemsProductItem[]required
max items 500
Show properties
Array of ProductItem
productIdstring
variantIdstring
namestring
categorystring
productTypestringrequired
- `flower` — fresh stems. Priced per-stem or per-pack via `pricingMethod`. SKUs are not used; identify variants by ID. - `material` — sundries (foam, ribbon, vases). Carry a SKU and are typically priced per unit.
default: "flower"
Allowed:flowermaterial
seasonalityobject | null
Show properties
startMonthinteger
min 1 · max 12
endMonthinteger
min 1 · max 12
variantVariant
Variant fields. Some apply only when the parent product is `flower`, others only when `material`. Common fields apply to both. Use the Flower / Material / All filter above to focus on the relevant set.
Show properties
namestring
Both types. Display label for the variant (e.g. `50cm`, `Standard`, `Large`).
stemLengthinteger | null
Flower only. Length of a single stem, in cm.
unitsPerPackinteger | null
Both types. How many physical units a supplier pack contains. For flowers this is stems-per-bunch; for materials it's units-per-pack. Used to convert between per-stem and per-pack pricing.
colorsstring[]
Flower only. Colour tags used for filtering and palette matching.
gradestring | null
Flower only. Supplier grade (e.g. `A1`, `Premium`).
brandstring | null
Material only. Manufacturer / supplier brand.
materialstring | null
Material only. What the item is made of (e.g. `glass`, `ceramic`, `oasis foam`).
dimensionLengthnumber | null
Material only. Physical length, in cm.
dimensionWidthnumber | null
Material only. Physical width, in cm.
dimensionHeightnumber | null
Material only. Physical height, in cm.
urlstring | null
Both types. External product URL (supplier page, catalogue link).
skustring | null
Material only. Stock-keeping unit. Flowers don't carry SKUs — identify flower variants by ID instead.
supplierIdsstring[]
Both types. Suppliers that sell this specific variant, referenced by `supplierId` (see `GET /api/v1/suppliers`). When omitted on create, the variant is attached to the hidden Default supplier so it satisfies the ≥1-supplier invariant. When omitted on update, the variant's existing supplier list is preserved. Unknown / cross-tenant / archived IDs return the row error `supplier_not_found` (or `supplier_archived`). The hidden Default supplier id can't be passed and returns `supplier_default_not_allowed`.
supplierMinPacksobject[]
Both types. Per-supplier minimum order quantity (MOQ), in packs. When the order sheet is built for an event, packs for a given (variant, supplier) line are rounded up to the supplier's `minPacks` — used when a supplier insists on (e.g.) "minimum 5 packs per order" regardless of how few stems you actually need. Each entry references a supplier on this variant's `supplierIds`. An entry pointing at a supplier that isn't on the list (after the patch is applied) returns `supplierMinPacks_supplier_mismatch`. Entries with `minPacks <= 1` are dropped server-side (they're a no-op constraint).
Show properties
Array of object
supplierIdstringrequired
minPacksintegerrequired
min 2
hireableboolean
Material only. When `true`, the variant is studio-owned and rentable by the day. Daily fees aren't set here — they live on a `productType: "hire"` price list (see the Price Lists endpoints). Setting `hireable: true` on a non-material product (or patching a non-material variant to be hireable) returns the row error `hireable_requires_material_product`.
inventoryQtyinteger | null
Material only. Number of units owned. Read responses return `null` when `hireable: false`. Only persisted on write when `hireable: true`.
min 0
Responses
200Update-only batch
resultsobject[]required
Rows that were written.
Show properties
Array of object
indexintegerrequired
Position of this row in the items array you sent, zero-based.
actionstringrequired
Whether the row created a product or patched an existing one.
Allowed:createdupdated
productIdstringrequired
The product written. New on a create, echoed back on an update.
variantIdstring
The variant written, when the row touched one.
errorsobject[]required
Rows that failed. Every other row still applied.
Show properties
Array of object
indexintegerrequired
Position of the failing row in the array you sent, zero-based.
errorstringrequired
201At least one row created
resultsobject[]required
Rows that were written.
Show properties
Array of object
indexintegerrequired
Position of this row in the items array you sent, zero-based.
actionstringrequired
Whether the row created a product or patched an existing one.
Allowed:createdupdated
productIdstringrequired
The product written. New on a create, echoed back on an update.
variantIdstring
The variant written, when the row touched one.
errorsobject[]required
Rows that failed. Every other row still applied.
Show properties
Array of object
indexintegerrequired
Position of the failing row in the array you sent, zero-based.
errorstringrequired
401Missing or invalid API key, or revoked / expired.
errorstringrequired
403Authenticated, but the key lacks the required scope or the tenant has no API access.
errorstringrequired
422Request body or arguments failed validation.
errorstringrequired
429Per-minute rate limit (60/min) or per-month cap exceeded.
errorstringrequired
Try it
Server
Authorization
Parameters
Bodyapplication/json
Request
curl -X POST "https://app.bloomcount.com/api/v1/products" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "items": [
    {
      "productId": "string",
      "variantId": "string",
      "name": "Pink Mondial",
      "category": "Roses",
      "productType": "flower",
      "seasonality": {
        "startMonth": 5,
        "endMonth": 10
      },
      "variant": {
        "name": "50cm",
        "stemLength": 50,
        "unitsPerPack": 10,
        "colors": [
          "string"
        ],
        "grade": "string",
        "brand": "string",
        "material": "string",
        "dimensionLength": 0,
        "dimensionWidth": 0,
        "dimensionHeight": 0,
        "url": "string",
        "sku": "string",
        "supplierIds": [
          "string"
        ],
        "supplierMinPacks": [
          {
            "supplierId": "sup_77p3v2k6m4t1r5z9c8w0y7b3",
            "minPacks": 5
          }
        ],
        "hireable": true,
        "inventoryQty": 24
      }
    }
  ]
}'
Response
{
  "results": [
    {
      "index": 0,
      "action": "created",
      "productId": "string",
      "variantId": "string"
    }
  ],
  "errors": [
    {
      "index": 0,
      "error": "string"
    }
  ]
}