Skip to main content

Common Details

Base URLs​

Starboard gives you the API host for your account. A key works only with the host it was issued for. The examples in these docs write the host as https://<your-starboard-api-host>.

API Versions​

Each part of the API lives under one version prefix:

APIPrefixExample
Quotes/api/v3GET /api/v3/quotes
CRM (companies and contacts)/api/v2GET /api/v2/companies
Test Auth/api/v2POST /api/v2/test/auth

CRM and Test Auth exist only under /api/v2. The /api/v2/quotes endpoints are legacy: they read an older quote model, return a different shape, and are not for new integrations. Use /api/v3/quotes.

Required HTTP Headers​

API requests take the following headers:

HeaderDescriptionRequired
AuthorizationAuthentication credentials (see Security)Yes
Content-TypeMust be set to application/jsonOn requests with a body (POST, PUT). Optional on others, but when sent it must be application/json.
X-User-Company-IDYour company ID (provided by Starboard)Yes
caution

The X-User-Company-ID header must be set to the company ID value provided by Starboard, exactly as given (the match is case-sensitive). Any other value returns 401 Unauthorized. See Security.

Example Request​

curl -X GET https://<your-starboard-api-host>/api/v3/quotes \
-H "Authorization: Basic {base64-encoded-credentials}" \
-H "Content-Type: application/json" \
-H "X-User-Company-ID: {your-company-id}"

Payload Format​

All API requests must be sent in JSON format.

All API responses are returned in JSON format.

Error Handling​

Standard HTTP status codes are used to indicate the success or failure of requests:

  • 2xx: Success
  • 4xx: Client errors (bad request, authentication issues, etc.)
  • 5xx: Server errors

Every error the API itself returns has the same JSON body: a short error code to branch on, and a message for a person.

{
"error": "bad_request",
"message": "Missing X-User-Company-ID header"
}

Errors from the infrastructure in front of the API, such as 429, 502, 504 and some 503s, may not have a JSON body. Treat them like 503: retry with backoff (see Calling the API).

Credentials and X-User-Company-ID are checked before anything else, so on any /api/v2 or /api/v3 path an authentication error comes first.

Error Codes​

errorStatusMeaning
unauthorized401The Authorization header is missing, malformed or holds invalid credentials, or X-User-Company-ID is a company this key can't act for.
bad_request400The request is malformed: X-User-Company-ID is missing, Content-Type is set to something other than application/json, a parameter or the body is invalid. The message says which.
invalid_unlocode400address_unlocode on Create Company or Update Company isn't 2 uppercase letters followed by 3 uppercase letters or digits (for example USNYC).
not_found404The resource doesn't exist for your company.
integration_disabled403Record Push Status named an integration that isn't enabled for your company.
method_not_allowed405The HTTP method isn't one of GET, POST, PUT, PATCH or DELETE (for example HEAD or OPTIONS).
unavailable503Authentication is temporarily unavailable, so your credentials couldn't be checked. Retry with backoff.
internal_server_error500The request failed in Starboard. Some validation failures also come back this way; see CRM errors.
error404, 405 or 500The path doesn't exist ("Cannot GET /api/v3/…"), the path exists but not for this method ("Method Not Allowed"), or an unexpected failure. Here the code is the word error itself.

The two kinds of 405 have different bodies. A method other than GET, POST, PUT, PATCH or DELETE:

{
"error": "method_not_allowed",
"message": "Method not allowed"
}

A supported method on a path that doesn't accept it (for example, DELETE /api/v3/quotes):

{
"error": "error",
"message": "Method Not Allowed"
}

Update Quote Customer also returns its own codes (409, 422 and 503), listed with that operation.

Response Codes​

CodeDescription
200 OKRequest successful
204 No ContentDelete successful (empty body)
400 Bad RequestMalformed request, missing X-User-Company-ID, or invalid parameter or body
401 UnauthorizedMissing or invalid credentials, or an X-User-Company-ID the key may not act for
403 ForbiddenOnly integration_disabled, from Record Push Status
404 Not FoundResource or path not found
405 Method Not AllowedThe method isn't supported on that path
409 Conflict, 422 Unprocessable EntityRefusals from Update Quote Customer
500 Internal Server ErrorServer error, and some validation failures (see CRM errors)
503 Service Unavailableunavailable: authentication is temporarily unavailable; retry with backoff. Only a 401 means the credentials or company header are wrong. Update Quote Customer also returns 503 match_unavailable.

Pagination​

List endpoints return one page at a time, together with a cursor for the next page:

{
"data": [ ... ],
"cursor": "eyJvIjoyMH0"
}
  • Resend everything with the cursor. The cursor marks only a position in the results. To get the next page, send the same request again, with the same filters and the same limit, and add cursor. A request with only cursor loses your filters.
  • Stop on an empty cursor. The last page returns "cursor": "". The key is always present, so check for the empty string.
  • "data": null is not the end of the list. On List Companies, a page that failed in Starboard can come back with status 200, "data": null and "cursor": "". Retry that page.
  • limit defaults to 20 and is capped at 50: larger values are treated as 50, and 0, negative or non-numeric values as 20.
  • The cursor is opaque. Pass it back exactly as you received it, URL-encoded in the query string. An invalid cursor isn't an error: the API silently starts again from the first page. The exception is List Quotes with updated_since, which returns 400 instead.
  • Guard against loops. Stop if a cursor repeats, or if you've read clearly more pages than total divided by limit, rounded up (total is returned by List Quotes). Either means the cursor was garbled and the walk started over. List Quotes with updated_since is the exception, where this guard doesn't apply: an invalid cursor returns 400, and a walk can run past total when quotes change while you page.
  • Pages are positions, not a snapshot. Records created, deleted or changed while you page shift the rest of the results, so a record can appear on two pages or be skipped. De-duplicate on id. List Quotes with updated_since pages in updated_at order instead: a quote that changes while you page moves to the end, so it's listed again instead of being skipped.
# First page
GET /api/v3/quotes?status=win&limit=50

# Next page: the same filters and limit, plus the cursor from the previous response
GET /api/v3/quotes?status=win&limit=50&cursor=eyJvIjo1MH0