Errors
A request that fails returns a 4xx or 5xx status and a JSON body with a single error object. The object always has a code your program can branch on and a message a person can read.
This page lists the codes any request can return. Each operation lists the other codes it can return in its own Errors block.
#Error responses
Every error body has the same shape, described in full as the error object:
error.codeis always present. It is a stable, lowercase identifier such asrecord_not_found. Codes don’t change once published.error.messageis always present. It is English you can show to a person as it is. Messages may be reworded, so don’t match on them.error.fieldsappears only when the failure is tied to particular fields. It maps each field to its own code and message; see Field errors.
The same code always comes with the same HTTP status.
curl https://api.carom.io/deals \
-H "Authorization: Bearer $CAROM_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"deal": {
"pipeline_id": "3d9e1b7a-5c2f-4a8d-b6e0-7f1c4a9d2e58",
"contact_id": "1f3c9a52-7b0e-4d4a-9c1e-2a6f0d8b3e41",
"amount": -500
}
}'
{
"error": {
"code": "invalid_request",
"message": "Please provide the required parameters for this endpoint.",
"fields": {
"name": {
"code": "required",
"message": "Is missing"
},
"amount": {
"code": "invalid",
"message": "Must be greater than or equal to 0"
}
}
}
}
#HTTP statuses
Success
| 200 | The request succeeded. The body holds the record or result. Operations that return an existing record when one already matches, such as Create an organization, answer 200 whether or not they created one. |
| 201 | A record was created. The body holds the new record. |
| 202 | Carom accepted the request and started work that finishes later, such as generating a briefing or an answer. The operation’s page says how to get the result. |
| 204 | The request succeeded and there is no body. Most deletes answer 204. |
Failure
| 400 | The request is malformed or a parameter failed validation. |
| 401 | The API key is missing or no longer valid. See Authentication. |
| 403 | The key or your key’s user isn’t allowed to do this: the key’s tier, the account’s API access setting, the user’s permissions, or terms the user hasn’t accepted. |
| 404 | The record doesn’t exist or isn’t visible to your key, the id isn’t a valid UUID, or no operation exists at that path. |
| 405 | The path doesn’t accept that HTTP method. |
| 406 | The Accept header excludes application/json. |
| 409 | The request conflicts with the record’s current state, for example a proposal that has already been answered or a tag name that is already taken. |
| 413 | The request body, or a file the request asks for, is too large. |
| 415 | The request’s Content-Type header names a type other than application/json. |
| 422 | The request is well formed, but the record’s state doesn’t allow it, for example deleting a pipeline that still has deals. |
| 429 | Too many requests. Wait the number of seconds in the Retry-After header before trying again. See Rate limits. |
| 500 | Something failed inside Carom. |
| 502 | A service Carom depends on, such as file storage, billing, or its job queue, failed. |
#Codes any request can return
Resource pages leave these out of their Errors blocks. Some, such as record_not_found, also appear there when an operation gives them a specific cause.
Codes
- 400
invalid_requestA parameter is missing, malformed, or out of range, or a body key names a path parameter with a different value.error.fieldsnames each one. - 401
unauthorizedNo API key was sent, or the key is unknown, revoked, or expired, or its user or account no longer has access. - 403
api_key_insufficientThe operation needs a read/write key, or accepts no API key at all. - 403
api_access_disabledAn admin has turned API access off for the account. See Account API access. - 403
api_access_read_onlyA read/write key called a Write key operation while the account’s API access is read-only. - 403
terms_acceptance_requiredYour key’s user must sign in to the app and accept Carom’s current terms. - 403
permission_deniedYour key’s user can see the record but isn’t allowed to do this to it. - 404
record_not_foundNo record with that id exists, it isn’t visible to your key, or the id isn’t a valid UUID. The cases return the same response. - 406
not_acceptableTheAcceptheader doesn’t allowapplication/json. SendAccept: application/json, or leave the header out. - 415
unsupported_media_typeTheContent-Typeheader names a type other thanapplication/json. Send the body as JSON, or leave the header out on a request without a body. - 429
rate_limitedToo many requests. TheRetry-Afterheader gives the seconds to wait. - 500
errorAn unexpected failure inside Carom. The request may succeed if you try again later.
Rate limits. Each API key can make 600 requests a minute; each key has its own limit. Over it, requests get 429 rate_limited, and the Retry-After header gives the number of seconds to wait. Some operations that start AI work, such as sending a message to Ask Carom and generating a meeting brief, have lower limits of their own, so handle 429 rate_limited on any request.
A few operations report a 500 with a more specific code than error. Handle every 500 the same way.
#Field errors
When a request fails because of particular fields, error.fields maps each field to an object with its own code and message. Each field reports only its first problem.
Field names are the parameter names you sent, without the resource wrapper: a bad name inside {"deal": {…}} is reported as name, not deal.name. Deeper fields, and bodies with more than one nested object, use dotted paths in the form parent.child. An entry in an array is named by its index, as in employments.0.organization.id.
A body key can’t name a different record from the path. If a top-level body key has the name of a path parameter, such as id in PATCH /contacts/{id}, and a different value, the request fails with 400 invalid_request and that key’s field code is not_allowed. The same value is accepted.
Field codes come from a small fixed set, so you can mark the input and show the message without special cases.
Field codes
-
requiredThe field is missing or empty.
-
invalid_formatThe value isn’t in the expected format, such as a malformed UUID, date, or email address.
-
too_longThe value is longer than allowed.
-
too_shortThe value is shorter than allowed.
-
takenThe value is already in use.
-
not_allowedThe field can’t be set in this request, for example a body key that names a path parameter with a different value.
-
invalidThe value isn’t valid for another reason. The message says why.
#Handling errors
- Branch on
error.code, not on the status or the message. Several codes share a status. - Don’t retry a 4xx unchanged. Fix the request, the key, or the record’s state first. The exception is
429: wait the number of seconds in theRetry-Afterheader, then try again. On400 invalid_cursor, start the list again from the first page. - Retry a 5xx later.
500and502responses, including502 rejectedand502 billing_unavailable, describe failures on Carom’s side or at a service it depends on. Wait before retrying, and wait longer after each failure. - Know which requests are safe to retry. GET and PATCH are safe to retry. A retried DELETE returns
404 record_not_foundif the first attempt deleted the record, so treat that 404 as success. Create a group, Create a milestone, Create a pipeline, and Create a space accept anIdempotency-Keyheader of 1 to 255 printable ASCII characters. For 24 hours after a successful create, a request from the same user to the same path with the same key returns the first response without creating another record, whatever its body. While the first request is still running, the retry gets409 idempotency_key_in_use. Before retrying any other POST that creates a record, check whether the first attempt created it. - Treat 404 as “not visible”. A record your key can’t see answers the same way as one that doesn’t exist.