Skip to content

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.

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.

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:

422 Unprocessable Entity
{
"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:

401 Unauthorized (OAuth token endpoint)
{
"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.

Start with the two things every request needs:

  1. The Authorization header is present and correctly formatted: Authorization: Bearer <token>, with a real token in place of the placeholder and no extra quotes or surrounding whitespace.
  2. The Version header is present and valid: Version: v3, in lowercase. The version check runs before your token is checked, so a missing or malformed Version header fails every request. See Versioning & Stability.
Terminal window
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.

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 the locationId (or altId) 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 scope field 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.

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-DD for form, survey and payments filters, and mm-dd-yyyy for 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.

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.

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.

  • 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: ..."}, the Version header isn’t a supported value: send v3.
  • 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 the Content-Type it 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.

When a request doesn’t behave the way you expect, capture these before you dig further:

  1. The response status and full body. The message field almost always tells you exactly what’s wrong, so don’t rely on the status code alone.
  2. The request identifiers. Copy the x-amzn-requestid response header and, if the JSON body has one, the traceId field. Support can use either to find the exact call.
  3. The base URL and Version header you actually sent. Many errors come from a mistyped host or a missing header rather than from the API itself.
Terminal window
curl -i https://services.smbcrm.com/contacts/<contact_id> \
-H "Authorization: Bearer <token>" \
-H "Version: v3"

-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.