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.
Base URL
Section titled “Base URL”https://services.smbcrm.comSend 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}.
Required headers
Section titled “Required headers”| 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.
A complete request
Section titled “A complete request”curl https://services.smbcrm.com/contacts/<contact_id> \ -H "Authorization: Bearer <access_token_or_private_integration_token>" \ -H "Version: v3" \ -H "Accept: application/json"const res = await fetch(`https://services.smbcrm.com/contacts/${contactId}`, { headers: { Authorization: `Bearer ${token}`, Version: 'v3', Accept: 'application/json', },});const data = await res.json();import requests
res = requests.get( f"https://services.smbcrm.com/contacts/{contact_id}", headers={ "Authorization": f"Bearer {token}", "Version": "v3", "Accept": "application/json", },)data = res.json()Your account/location ID
Section titled “Your account/location ID”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>" } altIdandaltType: Payments, Invoices, Products, Store and Media endpoints identify your account/location withaltId=<location_id>&altType=locationin the query string, or"altId": "<location_id>", "altType": "location"in the JSON body, instead oflocationId.
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.
Content type & responses
Section titled “Content type & responses”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 examplePOST /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) usesapplication/x-www-form-urlencodedwith 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.
Pagination
Section titled “Pagination”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.
curl "https://services.smbcrm.com/forms/?locationId=<location_id>&limit=20&skip=0" \ -H "Authorization: Bearer <token>" \ -H "Version: v3"{ "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.
Rate limits
Section titled “Rate limits”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 |
Browser (client-side) requests
Section titled “Browser (client-side) requests”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.
