COUNTCOUNT
Sign Up

Getting Started

Authentication & signing

The Partner API uses two layers of authentication: an HMAC signature that proves the request came from your app, and a workspace access token that scopes the request to one customer's data.

Both layers are required on data endpoints

Data endpoints need both a valid HMAC signature (your app) and a Bearer access token (the workspace). Only the token-exchange routes are signed without a Bearer token.

Layer 1 — HMAC request signing

Every request carries three headers derived from your clientSecret. The signature is an HMAC-SHA256 of a base string built from the request:

Base string format
METHOD:path:timestamp:bodyHash

# Example for: POST /partners/customers
POST:/customers:1717689600:9b74c9897bac770ffc029102a200c5de

The four parts, joined by colons, are:

  • METHOD — the uppercase HTTP method.
  • path — the path relative to the /partners mount. For /partners/customers you sign /customers.
  • timestamp — Unix time in seconds; send the same value in x-timestamp. Must be within ±300 seconds of server time (five-minute clock skew window).
  • bodyHash — SHA-256 hex of the exact JSON body for POST/PUT/PATCH, or an empty string otherwise.

Sign the path without /partners

The most common signing mistake is including the /partners prefix in the base string. The server signs req.path, which is relative to the mount.
signRequest (Node.js)
import crypto from 'node:crypto';

function signRequest({ method, path, timestamp, body, clientSecret }) {
  // path is relative to the /partners mount, e.g. "/customers"
  const bodyHash = ['POST', 'PUT', 'PATCH'].includes(method)
    ? crypto.createHash('sha256').update(body ?? '').digest('hex')
    : '';

  const baseString = `${method}:${path}:${timestamp}:${bodyHash}`;

  return crypto.createHmac('sha256', clientSecret).update(baseString).digest('hex');
}

Attach the result as headers:

Request headers
x-client-id: <your clientId>
x-timestamp: <unix seconds, same value used in the signature>
x-signature: <hex HMAC-SHA256 of the base string>
Authorization: Bearer <workspace access token>   # data endpoints only
Content-Type: application/json                     # when sending a body

Layer 2 — Workspace access tokens (OAuth)

To act on a workspace you first send the user through the OAuth consent flow, then exchange the returned code for an access token. The token exchange is signed with HMAC but does not need a Bearer token.

Use the arrows or dots below to step through each OAuth phase. Details and code samples update as you browse.

Your systemStep 1 of 7

Create OAuth credentials

Register your integration in COUNT Partners and store clientId and clientSecret on your server.

  • Create an OAuth app in the COUNT Partners dashboard.
  • Keep clientSecret server-side only; never ship it to browsers or mobile apps.
Authentication overview

See OAuth consent experience for what your users see on the consent screen and how to handle the redirect.

Exchange the authorization code
POST https://api.getcount.com/partners/grant-access-token

{
  "code": "<authorization code from the redirect>",
  "grantType": "authorization_code"
}

The response contains the tokens and the workspace they are scoped to:

Token response
{
  "accessToken": "eyJhbGciOi...",
  "refreshToken": "eyJhbGciOi...",
  "accessTokenExpiresAt": "2026-06-06T12:00:00.000Z",
  "refreshTokenExpiresAt": "2026-07-06T12:00:00.000Z",
  "workspaceId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "workspaceName": "Acme Books"
}

Access tokens expire. When they do, call POST /partners/refresh-user-access-token with your refresh token to get a new pair.

Putting it together

Sign the base string, set the three HMAC headers plus the Bearer token, and send the request. Every code sample in this reference does exactly this — the starter templates wrap it into a single helper so you never compute a signature by hand.