Errors & Troubleshooting
Every response from the SMBcrm API uses a standard HTTP status code, and most error responses add a small JSON body describing what went wrong. This page lists the status codes you’ll see, the shape of that body, and the fixes for the most common problems.
HTTP status codes
Section titled “HTTP status codes”| Status | Meaning | Typical cause |
|---|---|---|
200 OK / 201 Created |
Success | The request completed. Many creates return 201, but some return 200 (for example POST /calendars/, POST /invoices/ and POST /locations/{locationId}/tags), and a search such as POST /social-media-posting/{locationId}/posts/list returns 201. Treat any 2xx as success instead of checking for one exact code. |
204 No Content |
Success, no body | Some deletes return an empty body. Don’t parse JSON from a 204. |
400 Bad Request |
Malformed request | Invalid JSON, a parameter of the wrong type, a malformed query string, or a Version value the API doesn’t support. |
401 Unauthorized |
Not authenticated | Missing, invalid, or expired token, or a missing or malformed Version header (for example V3 instead of v3). |
403 Forbidden |
Not authorized | The token is valid but isn’t allowed to do this: it doesn’t have access to the locationId you sent, or it lacks permission for the resource or the scope the endpoint requires. |
404 Not Found |
No such resource | The ID doesn’t exist, belongs to a different account/location, or was deleted. |
409 Conflict |
Conflicting state | The request conflicts with the resource’s current state, for example, a duplicate or a concurrent action. |
413 Payload Too Large |
File too big | An uploaded file is over the endpoint’s size limit. |
415 Unsupported Media Type |
Wrong content type | The Content-Type doesn’t match what the endpoint accepts: JSON, multipart, or form-encoded. |
422 Unprocessable Entity |
Validation error | The JSON is well-formed but fails validation: a required field is missing or invalid. |
429 Too Many Requests |
Rate limited | You’ve exceeded the burst or daily rate limit. |
5xx |
Server error | Something went wrong on SMBcrm’s side. Retry reads with backoff; see Other statuses at a glance for writes. |
The error response body
Section titled “The error response body”Most errors are JSON with a statusCode and a message. Many also include an error name,
and 422 responses add a traceId. Here is a validation failure:
{ "statusCode": 422, "message": [ "locationId should not be empty", "locationId must be a string" ], "error": "Unprocessable Entity", "traceId": "<trace_id>"}Not every endpoint uses exactly this shape. The OAuth token endpoint returns error and
error_description:
{ "error": "UnAuthorized!", "error_description": "Invalid refresh token"}A few endpoint families nest the details under an error object instead. Read the HTTP
status first, then look for message (a string or an array), then error_description or
error.message.
Common problems and fixes
Section titled “Common problems and fixes”401 Unauthorized
Section titled “401 Unauthorized”Start with the two things every request needs:
- The
Authorizationheader is present and correctly formatted:Authorization: Bearer <token>, with a real token in place of the placeholder and no extra quotes or surrounding whitespace. - The
Versionheader is present and valid:Version: v3, in lowercase. The version check runs before your token is checked, so a missing or malformedVersionheader fails every request. See Versioning & Stability.
curl -i https://services.smbcrm.com/contacts/<contact_id> \ -H "Authorization: Bearer <token>" \ -H "Version: v3"The message in the response tells you which check failed:
| Message | Cause | Fix |
|---|---|---|
version header was not found. |
The request has no Version header. |
Add Version: v3. |
version header is invalid |
The value is malformed, for example V3 or 3. |
Send v3. |
No Authorization header found for authentication! |
The request has no Authorization header. |
Add Authorization: Bearer <token>. |
Invalid JWT |
The token is malformed, revoked, or expired. | Get a new token. See the tip below. |
If both headers are correct, the token itself is the problem.
403 Forbidden
Section titled “403 Forbidden”A 403 means authentication succeeded but authorization didn’t. The token is valid, but it
isn’t allowed to do what you asked. The common causes are:
- Wrong location. The response says
The token does not have access to this location. The token belongs to a different Sub-Account than thelocationId(oraltId) in your request. Use a token issued for that Sub-Account, or send the ID that matches your token. - Missing scope or permission. The token wasn’t issued with the scope this endpoint
requires, or it lacks permission for the resource. Every endpoint in this reference lists
its required scope in the
scopefield of its endpoint block.
If you get a 401 or 403 and the token itself is valid, check the scope on the endpoint’s
reference page first.
Fix a missing scope at the source of the token:
- Private Integration Token: edit the integration in Settings, then Private Integrations, and add the missing scope. The existing token keeps working, so you don’t need to redeploy.
- OAuth: re-run authorization requesting the additional scope; the user sees it on the consent screen.
See Scopes for the full list of scopes and what each one grants.
422 Unprocessable Entity
Section titled “422 Unprocessable Entity”The request reached the API and parsed as JSON, but failed validation. Read message first:
it lists each failed check. The most common causes are:
- A required field is missing, most often
locationId. - A field has the wrong type or format, for example a date in a format the endpoint doesn’t
accept, or a phone number without a country code. Date formats vary by endpoint: ISO 8601
for most body timestamps, epoch milliseconds for calendar slot and event queries,
YYYY-MM-DDfor form, survey and payments filters, andmm-dd-yyyyfor opportunity search. - A value doesn’t match what the endpoint expects, such as an invalid ID reference or an out-of-range option.
Compare your request body against the example on the endpoint’s own page and confirm every required field is present before you retry.
429 Too Many Requests
Section titled “429 Too Many Requests”You’ve exceeded your integration’s rate limit. Back off before retrying: resending immediately only keeps you over the limit.
Wait at least the burst interval reported in X-RateLimit-Interval-Milliseconds (10
seconds) before you retry. If X-RateLimit-Daily-Remaining is 0, retries keep failing
until the daily allowance resets. If the response includes a Retry-After header, wait that
many seconds.
Every response carries your current rate-limit status in its headers, so you can slow down
proactively instead of reacting after a 429. See
Base URL & Headers for the full list of rate-limit headers and how
to read them. Where an endpoint supports it, batch requests, cache values that rarely change,
and prefer webhooks over polling.
CORS errors in the browser
Section titled “CORS errors in the browser”The API accepts cross-origin requests from any origin. Before the real request, the browser
sends a preflight check, and the API allows these request headers: Authorization, Version,
Content-Type, Accept, locationId, and the MCP headers MCP-Protocol-Version,
Mcp-Session-Id and Last-Event-ID. If a browser request fails with a CORS error, look for
a header outside that list, or for credentials: 'include' on the fetch. The API doesn’t use
cookies, so leave credentials at the default.
Even when the browser call works, make it from your server in production. A token in client-side code is exposed to anyone who opens the page. See Token Safety.
Other statuses at a glance
Section titled “Other statuses at a glance”400 Bad Request: usually malformed JSON or a parameter of the wrong type; validate the request body before it’s sent. If the body is{"error":"Unsupported API version: ..."}, theVersionheader isn’t a supported value: sendv3.404 Not Found: double-check the ID in the path and confirm it belongs to the account/location your token is scoped to.409 Conflict: the request conflicts with the resource’s current state, for example creating a duplicate; re-fetch the resource and reconcile before retrying.413/415: check the endpoint’s page for its file size limit and theContent-Typeit accepts. For example, Upload attachments takes up to 5 files of up to 5 MB each.5xx: transient. Retry with exponential backoff for reads (GET) and idempotent writes (PUT,DELETE). For creates and sends (POST), check whether the first attempt took effect before retrying, because most endpoints have no idempotency key and a retry can create a duplicate. If the error persists, gather the details below before you reach out.
Debugging tips
Section titled “Debugging tips”When a request doesn’t behave the way you expect, capture these before you dig further:
- The response status and full body. The
messagefield almost always tells you exactly what’s wrong, so don’t rely on the status code alone. - The request identifiers. Copy the
x-amzn-requestidresponse header and, if the JSON body has one, thetraceIdfield. Support can use either to find the exact call. - The base URL and
Versionheader you actually sent. Many errors come from a mistyped host or a missing header rather than from the API itself.
curl -i https://services.smbcrm.com/contacts/<contact_id> \ -H "Authorization: Bearer <token>" \ -H "Version: v3"const res = await fetch('https://services.smbcrm.com/contacts/<contact_id>', { headers: { Authorization: `Bearer ${token}`, Version: 'v3', },});
const body = await res.json().catch(() => null);console.log(res.status, res.headers.get('x-amzn-requestid'), JSON.stringify(body));-i (cURL) prints the status line and the response headers, including x-amzn-requestid.
In Node.js, logging res.status and the header next to the parsed body does the same, even on
responses your JSON client would otherwise swallow on error.
Related
Section titled “Related”- Base URL & Headers: required headers, versioning, and the full list of rate-limit response headers.
- Versioning & Stability: the
Versionheader and the change policy. - Scopes: the scope each endpoint needs, and how to add one to an existing token.
- OAuth (account access): refresh an expired access token.
- Private Integration Tokens: edit scopes and rotate a token.
- Token Safety: call the API server-side instead of from the browser.
