Skip to content

Base URL & Headers

Every SMBcrm REST API request shares the same shape: one base URL, a bearer token, an API version, and a JSON body when the request sends one. Get this right once and every endpoint in the reference works the same way.

https://services.smbcrm.com

Send all requests over HTTPS to this host. Endpoint paths in this documentation are relative to it. For example, GET /contacts/{contactId} means GET https://services.smbcrm.com/contacts/{contactId}.

Header Value Required
Authorization Bearer <access_token_or_private_integration_token> Always, except POST /oauth/token, which takes client_id and client_secret in the form body
Version v3 Always
Accept application/json Recommended
Content-Type application/json On any request that sends a JSON body, including some DELETE requests. File uploads and POST /oauth/token use other content types (see Content type & responses)

The Version header is required on every request and selects the API version. Send v3, the current version. The API also accepts older date-based versions (such as 2021-07-28) for backward compatibility, but new endpoints and improvements land in v3, so use it for anything new. See Versioning & Stability.

Terminal window
curl https://services.smbcrm.com/contacts/<contact_id> \
-H "Authorization: Bearer <access_token_or_private_integration_token>" \
-H "Version: v3" \
-H "Accept: application/json"

Most endpoints operate on a single SMBcrm account/location. Where an endpoint requires that ID, this documentation uses the placeholder <location_id> for the identifier of your SMBcrm account/location. Depending on the endpoint, it appears as:

  • a path segment: GET /locations/<location_id>
  • a query parameter: GET /forms/?locationId=<location_id>
  • a field in the JSON body: { "locationId": "<location_id>" }
  • altId and altType: Payments, Invoices, Products, Store and Media endpoints identify your account/location with altId=<location_id>&altType=location in the query string, or "altId": "<location_id>", "altType": "location" in the JSON body, instead of locationId.

Each endpoint page shows exactly where the ID belongs. Send it wherever the page lists it, including when you authenticate with a Private Integration Token.

Requests that send a JSON body must set Content-Type: application/json and send valid JSON. A few DELETE endpoints take a JSON body too, such as DELETE /contacts/{contactId}/tags and DELETE /payments/coupon. Two kinds of request use a different content type:

  • File uploads use multipart/form-data, for example POST /forms/upload-custom-files. Let your HTTP client set the header and the boundary; don’t set it by hand.
  • The OAuth token endpoint (POST /oauth/token) uses application/x-www-form-urlencoded with snake_case field names. See OAuth 2.0.

A request with the wrong content type is rejected. The token endpoint returns 400 with invalid_request for a JSON body, and other endpoints can return 415 Unsupported Media Type.

Responses are JSON. Successful responses use standard 2xx status codes. Some deletes return 204 No Content with an empty body, so check the status before you parse JSON. Errors use 4xx and 5xx with a JSON body describing the problem. See Errors & Troubleshooting.

List endpoints are paginated, and the parameters differ by endpoint. Most take limit plus skip or offset. Some take a page number (page, often with pageSize) or a cursor (startAfter, startAfterId, cursor, nextCursor), and others take a page token (pageToken or after) and return the values for the next page in a paging object. Totals appear as total, count, totalCount or inside a meta object, depending on the endpoint. Use the pagination parameters and response fields documented on each endpoint’s page, and don’t assume a page size.

Terminal window
curl "https://services.smbcrm.com/forms/?locationId=<location_id>&limit=20&skip=0" \
-H "Authorization: Bearer <token>" \
-H "Version: v3"
200 OK
{
"forms": [{ "id": "<form_id>", "name": "Contact form", "locationId": "<location_id>" }],
"total": 1
}

This endpoint pages with skip and limit: request the next page by raising skip by the value of limit, and stop when skip reaches total.

By default, an integration can make 100 requests per 10 seconds (burst) and 200,000 requests per day against one account/location:

Limit Default allowance
Burst 100 requests per 10 seconds
Daily 200,000 requests per day

Limits are counted per integration and per account/location. Another integration has its own allowance, and an OAuth app installed on several accounts gets a separate allowance on each one, so adding installs doesn’t divide it.

When you exceed a limit, you receive 429 Too Many Requests. Back off and retry. Every response also includes your current rate-limit status in headers. Treat them as authoritative and throttle before you hit a limit:

Response header Meaning
X-RateLimit-Max Requests allowed in the current burst window
X-RateLimit-Remaining Requests remaining in the current burst window
X-RateLimit-Interval-Milliseconds Length of the burst window
X-RateLimit-Limit-Daily Requests allowed per day
X-RateLimit-Daily-Remaining Requests remaining today

Call the REST API from your server, not from browser JavaScript. The API accepts cross-origin requests, so a browser can send the Authorization and Version headers, but a token in client-side code is exposed to anyone who opens the page. Send requests from your backend or a server-side proxy you control, and give your pages only the results they need. See Token Safety, and CORS errors in the browser if you’re testing from a browser.