Errors
Every error the integration API can answer with, and what to do about each.
Every failure has the same shape:
{
"error": {
"code": "conflict",
"message": "This code is already in use",
"details": { "field": "code" }
}
}code is stable and is what your code should switch on. message is for a person reading a
log, and may change. details, when present, says which field or what state.
| HTTP | code | When | What to do |
|---|---|---|---|
400 | validation_error | A field is missing, the wrong type, or out of range. details lists each issue with its path | Fix the request. Nothing was written |
401 | unauthorized | No key, a malformed key, an unknown key, or a revoked one | Check the Authorization header; make a new key if this one was revoked |
403 | forbidden | details.reason: "suspended": the workspace is suspended | Contact Maalam |
404 | not_found | No project with that slug, or no unit with that code in it | Create the project first; check the code |
409 | conflict | details.field names the collision: a frozen price on a held unit, a derived status, a duplicate code | See Catalogue. Leave status out of re-syncs |
429 | rate_limited | More than the rate limit in a minute | Wait for the window; back off |
500 | internal | Something failed on Maalam's side | Retry with backoff; if it persists, tell Maalam and quote the x-request-id response header |
Batches
A batch (POST /units/batch) answers 200 even when items fail. Each item carries its own
outcome, and a failed one carries the same error object a single request would have returned.