Skip to content
Bloomcount API
Esc
navigateopen⌘Jpreview
On this page

Conventions

Bulk requests, resource-prefixed IDs, and per-supplier minimum order quantities.

Bulk operations

Create and update endpoints accept arrays, so a single request covers up to 500 items. A row that fails is reported in the errors array rather than failing the whole call.

Paging

Every list endpoint returns the same envelope: the rows in items, a boolean isDone, and a cursor.

{ "items": [], "isDone": false, "cursor": "..." }

Ask for a page size with limit, between 1 and 100, defaulting to 25. To continue, send the cursor you were given back as a query parameter. Stop when isDone is true rather than when a page comes back short: a page can be smaller than limit and still not be the last one, because filters apply as the cursor walks.

Dates and times

Every timestamp in and out of the API is milliseconds since the Unix epoch, as a number. That covers event dates, hire windows and the timestamp on a webhook delivery. The one exception is a price list’s date, which is a calendar day as a YYYY-MM-DD string, because a price list belongs to a date rather than a moment.

Asking for fewer fields

Every read endpoint takes a fields parameter: a comma-separated list of what you want back.

curl "https://app.bloomcount.com/api/v1/products?fields=name,category" \
  -H "Authorization: Bearer bloomcount_..."

A dotted path reaches inside a collection, so fields=variants.sku keeps the variants array with only each variant’s SKU, and fields=groups.items.quantity walks two levels into an event. Naming a collection on its own, as in fields=variants, returns it whole.

Three rules worth knowing:

  • The id always comes back, listed or not, so a trimmed object is still something you can fetch or update later.
  • A name that doesn’t exist is an error, not an empty object. The response is 422 with unknown_fields: and the names that didn’t resolve, so a typo tells you rather than looking like missing data.
  • Omitting the parameter changes nothing. The full object is still the default, so existing integrations are unaffected.

On a paginated endpoint the parameter shapes each item; isDone and cursor are the envelope and always come back.

Some reads do less work rather than merely sending less: an event fetched without groups never loads its lines, and without imageUrls never resolves them; a price list fetched without entries skips the price table entirely; an event whose selection names no money never loads the price lists behind it.

Money

Event reads carry the figures the app itself works with: what each line costs, what the customer pays for it, a subtotal per group, and the event’s own totals. Four things are worth knowing before you invoice from them.

Cost and customer price are different numbers. costIncTax is what the stock costs with tax and no markup. customerPriceIncTax is that with the event’s per-unit uplift and the markup for the line’s product type — and hire lines are marked up on their own multiplier, set separately from flowers and materials.

Tax is in the name. A field ending ExclTax has none applied, one ending IncTax has the tax multiplier of the price list that line is priced from. Nothing is ambiguous on purpose.

Waste is a real cost, kept separate. costIncTax covers only the units a design consumes, but stock is bought in whole packs, so the leftovers are money spent too. They are wasteCostIncTax, and what the florist actually pays out is costOfGoodsIncTax — the two added together, already worked out for you. Never sum all three.

The figures are unrounded floats. 28.799999999999997 rather than 28.80, because that is what the arithmetic produces. Round at the point you present or bill.

An event’s totals is the rollup the app maintains, so it excludes hidden groups and includes labour, additional fees and travel. That last part is why it is larger than the sum of the line prices. It rides along on the events list too, at no extra cost, so a report over many events needs one call rather than one per event.

curl "https://app.bloomcount.com/api/v1/events?fields=name,totals" \
  -H "Authorization: Bearer bloomcount_..."

IDs

Resource IDs are resource-prefixed strings. The prefix says what the ID refers to, so a variant ID can’t be passed where a product ID is expected.

PropType
prod_?Product

A catalogue product — the thing a variant belongs to.

TypeProduct
var_?Product variant

A specific variety of a product; what an event line and a price entry point at.

TypeProduct variant
sup_?Supplier

Who a variant can be bought from.

TypeSupplier
pl_?Price list

A dated set of prices for one product type.

TypePrice list
pe_?Price entry

One variant's price on one price list, optionally per supplier.

TypePrice entry
tax_?Tax group

A named tax rate applied to an event's totals.

TypeTax group
evt_?Event

A customer event, with its dates, venue and pricing.

TypeEvent
evtgrp_?Event group

A section within an event, such as a bouquet or a table.

TypeEvent group
evti_?Event item

One line on an event: a variant, a supplier and a quantity.

TypeEvent item
whsub_?Webhook subscription

A URL receiving event deliveries.

TypeWebhook subscription

Treat the part after the underscore as opaque. Passing the wrong type of ID returns 422 wrong_id_type:expected_<table>_got_<table>.

Suppliers and minimum order quantities

Variants can be scoped to specific suppliers (supplierIds) and carry per-supplier minimum order quantities (supplierMinPacks). When an event’s order sheet is generated, packs for a given variant and supplier are rounded up to minPacks, so a supplier insisting on five packs per order is never under-ordered.

Both fields reference suppliers by ID. List a tenant’s suppliers with GET /api/v1/suppliers (scope suppliers:read) and use the returned supplierId on the products bulk upsert. The hidden Default supplier is not returned there and cannot be assigned through the API.

Was this page helpful?