Errors
Every status and code the API returns, what each means, and what to do about it.
A failed request returns a JSON body with a single error field holding a
snake_case code. Codes are stable and safe to branch on; the wording of any
prose around them is not.
{ "error": "missing_scope" }
Some codes carry a detail after a colon, such as hire_unavailable:10 or
wrong_id_type:expected_products_got_productVariants. Split on the first colon
and treat the left side as the code.
Statuses
200?OK
The call succeeded. Bulk writes return 200 when every row was an update or errored.
OK201?Created
A bulk write created at least one row.
Created400?Bad request
The envelope was wrong before any authentication ran, such as a body that is not an array where one was required.
Bad request401?Unauthorized
No key, or a key that is unknown, revoked or expired.
Unauthorized403?Forbidden
The key is valid but lacks the scope, or the tenant has no API access.
Forbidden404?Not found
The addressed resource does not exist on this tenant.
Not found409?Conflict
A delete would cascade past the resource in the URL. Retry with force=true if that is what you want.
Conflict413?Payload too large
The request body exceeded the size limit.
Payload too large422?Unprocessable
The body parsed but failed validation, including malformed ids and unknown field names.
Unprocessable429?Too many requests
The per-minute rate limit or the monthly cap was exceeded.
Too many requests500?Server error
Something failed on our side. The response carries internal_error and the detail is in our logs.
Server errorAuthentication and limits
| Code | Meaning | What to do |
|---|---|---|
missing_authorization |
No Authorization header. |
Send Authorization: Bearer bloomcount_…. |
invalid_api_key |
The key is not recognised. | Check the key was copied whole. |
revoked |
The key was revoked in the app. | Issue a new key. |
expired |
The key passed its expiry date. | Issue a new key. |
api_access_revoked |
The tenant’s plan no longer includes API access. | Speak to the account owner. |
missing_scope |
The key lacks the scope this endpoint needs. | Add the scope in Settings → API, or use a key that has it. |
rate_limited |
More than 60 requests in a minute on this key. | Back off and retry. |
monthly_limit_exceeded |
The tenant’s monthly call cap is spent. | Wait for the reset or upgrade the plan. |
Request shape
| Code | Meaning | What to do |
|---|---|---|
invalid_json |
The body is not valid JSON. | Check the serialiser and the content type. |
invalid_body |
The body parsed but is not the shape the endpoint takes. | Compare against the endpoint page. |
items_must_be_array / groups_must_be_array |
A bulk endpoint was sent something other than an array. | Wrap the rows in an array. |
too_many_items |
More than 500 rows in one call. | Split the batch. |
payload_too_large |
The body exceeded the size limit. | Send fewer rows, or smaller ones. |
invalid_id / invalid_event_id / invalid_id_format |
An id in the path or body is not a prefixed id. | Send the id exactly as the API returned it. |
wrong_id_type:expected_<table>_got_<table> |
A valid id of the wrong kind. | Check which prefix the field wants. |
unknown_fields:<names> |
fields named paths that do not exist. |
Fix the names; the response lists every one that failed. |
Rows in a bulk write
A bulk endpoint does not fail the whole call for one bad row. It returns the
successful rows in results and the rest in errors, each carrying the row’s
index and one of these codes.
References
| Code | Meaning |
|---|---|
product_not_found, variant_not_found, supplier_not_found |
The referenced row is not on this tenant, or does not exist. |
price_list_not_found, tax_group_not_found, event_not_found |
As above, for the named resource. |
group_not_found, item_not_found |
The group or line is not on the event in the URL. |
variant_not_on_product |
The variant exists but belongs to a different product. |
supplier_not_on_variant |
The supplier does not sell that variant. |
supplier_archived |
The supplier is archived and cannot take new lines. |
supplier_default_not_allowed |
The hidden Default supplier cannot be assigned through the API. |
move_target_not_found, move_target_is_removed_group |
moveItemsTo points at a group that is missing, or at one the same call removes. |
Missing or invalid values
| Code | Meaning |
|---|---|
missing_required_fields_on_create |
A create is missing a field that only creates require. |
name_required, price_required |
The named field was empty. |
variant_id_required, item_id_required, group_id_required, supplier_id_required |
The row omitted an id the operation needs. |
variantId_required_for_variant_patch |
Variant fields were supplied without a variantId. |
invalid_quantity, invalid_group_quantity |
A quantity was not a positive whole number. |
supplierMinPacks_supplier_mismatch |
A minimum-order entry names a supplier that is not on the variant. |
duplicate |
The same row appears twice in one batch. |
Hire
| Code | Meaning |
|---|---|
hire_unavailable:<n> |
Fewer units are free across those dates than the row asked for. The number is what is actually available. |
hire_dates_required |
A hire line needs hireStart and hireEnd. |
hire_end_before_start |
The hire window ends before it begins. |
not_a_hire_line |
Hire dates were sent for a consumable line. |
variant_not_hireable |
The variant is not marked hireable. |
hireable_requires_material_product |
Only material products can be hireable. |
supplier_not_allowed_on_hire |
Hire lines come from your own stock, so they take no supplier. |
Reference data
| Code | Meaning |
|---|---|
color_not_found, category_not_found, venue_not_found, staff_not_found |
No such row on this tenant. |
color_name_required, category_name_required, venue_name_required, staff_name_required, supplier_name_required |
The name was empty. |
color_name_taken, category_name_taken, supplier_name_taken |
Another row already uses that name. |
invalid_hex_format |
A colour’s hex is not a valid hex colour. |
preset_color_immutable, preset_category_immutable |
Built-in colours and categories cannot be edited or deleted. |
default_supplier_immutable |
The hidden Default supplier cannot be changed. |
staff_has_account |
That staff member has a user account, so remove them from Settings instead. |
Deleting something in use
A delete whose cascade would reach past the resource in the URL returns 409
with a count, rather than quietly taking the rest with it.
| Code | Meaning |
|---|---|
product_on_event_items:<n> |
That many event lines use the product. |
variant_on_event_items:<n> |
That many event lines use the variant. |
price_list_in_use:<n> |
That many events point at the price list. |
tax_group_in_use:<n> |
That many price lists use the tax group. |
Adding ?force=true performs the same delete the app performs, cascade
included, and the response reports what it took. Events have no force: their
groups, lines and versions belong to them and go with them.
Webhooks
| Code | Meaning |
|---|---|
url_invalid |
The URL did not parse. |
url_must_be_https |
Only https:// endpoints are accepted. |
url_credentials_not_allowed |
The URL carried a username or password. |
url_port_not_allowed |
Only port 443 is accepted. |
url_host_not_allowed |
The host is loopback, private, link-local or a metadata address. |
at_least_one_event_required |
A subscription must name at least one event type. |
subscription_not_found |
No such subscription on this tenant. |