Authentication
Every call to the CHeKT API authenticates with a Bearer token. CHeKT supports three token sources in a progression from simple to strict — start where you are, move forward as your needs grow.
The three auth modes
Each mode is independent — you can mix them across apps in the same dealer. Most partners start with an API key and stay there forever. OAuth and Assertion Tokens come into play when you've outgrown the simpler model.
Client Credentials (OAuth 2.0)
Default · Most endpointsAuthorization access token
Connect endpointAssertion Tokens
Highest-trust partnersThe Authorization header
Whatever the mode, the wire format is the same. Send a standard Authorization header with a Bearer-prefixed token. The token value is what differs by mode:
- API key
- Authorization: Bearer chekt_live_8h2j3kfm… — long-lived, per-app, dealer-scoped.
- OAuth access token
- Authorization: Bearer eyJhbGciOiJSUzI1NiIs… — short-lived (1h), refreshable, per-user.
- Assertion-derived token
- Authorization: Bearer eyJhbGciOiJFUzI1Ni… — short-lived (1h), obtained by exchanging a signed JWT.
GET /v1/devices HTTP/1.1
Host: api.chekt.com
Authorization: Bearer chekt_live_8h2j3kfm2918sndlj…
Accept: application/jsonAPI keys
API keys are the default. Every CHeKT App you create in the dealer portal gets exactly one active key. Keys are per-app, dealer-scoped, and rotatable.
- Format
- chekt_live_… (32+ characters). Prefix indicates environment: chekt_live_ for production, chekt_test_ for sandbox.
- Scope
- Limited to the app's granted permissions. The same key cannot exceed the scope set at app creation.
- Lifetime
- Indefinite. Keys do not expire. Rotate them yourself on the schedule that suits your risk model — quarterly is a good default.
- Storage
- Secret manager only. Never in code, environment files committed to git, or chat tools.
Storing the API key
Where to put your key, by platform. All five approaches are first-class; pick what matches your deployment.
# Export from a sourced ~/.zshrc or ~/.bashrc
export CHEKT_API_KEY='chekt_live_…'
# Or load from .env via direnv
echo 'export CHEKT_API_KEY="chekt_live_…"' > .envrc
direnv allowRotation flow
Rotation is a one-click action in the dealer portal that issues a new key and keeps the old one valid for 24 hours so you can deploy without downtime.
- 01Dealer adminCHeKTClick Rotate keySettings → CHeKT Apps → [app] → Security
- 02CHeKTCHeKTGenerate new key + old key 24h grace
- 03CHeKTDealer adminShow new key (once)
- 04Dealer adminDeveloperHand off via secret manager1Password / Doppler / Vault
- 05DeveloperCHeKTDeploy + verify with new key
- 06Dealer adminCHeKTClick Revoke previous to end grace early (optional)
OAuth 2.0 (preview)
For apps that act on behalf of end users — letting a dealer admin or operator sign in and approve scopes themselves — OAuth 2.0 is the right model. We're rolling this out in Q3 2026 for select partners.
Authorization code flow
The standard three-legged OAuth flow with PKCE. The end user signs in, grants scopes, and your app exchanges the code for tokens.
- 01End userYour appUser clicks Connect to CHeKT
- 02Your appEnd userRedirect to CHeKT auth?client_id=…&scope=devices:read events:read
- 03End userCHeKT authUser signs in & approves scopes
- 04CHeKT authYour appRedirect back with ?code=…
- 05Your appCHeKT authPOST /oauth/token (exchange code)
- 06CHeKT authYour appIssue access_token + refresh_token
- 07Your appCHeKT authCall API with Bearer <access_token>
authorization_endpoint- https://auth.chekt.com/oauth/authorize — where you redirect the user.
token_endpoint- https://api.chekt.com/oauth/token — where you POST to exchange the code.
- access_token TTL
- One hour. Use the refresh token to get a new one without re-prompting the user.
- refresh_token TTL
- Thirty days, sliding window. Make any API call and the window resets.
Assertion Tokens (preview)
The strictest mode. Your service signs short-lived JWTs with a private key, exchanges them at CHeKT's token endpoint for access tokens, and rotates the signing key on its own schedule. No long-lived secret ever lives on disk.
Reserve Assertion Tokens for high-volume partners running in regulated environments — central stations with SOC 2 obligations, panel manufacturers shipping to regulated markets, large-scale automation platforms.
- 01Partner servicePartner serviceSign a short-lived JWTHeader alg=ES256, exp=now+5min
- 02Partner serviceCHeKT authPOST /oauth/token (grant_type=jwt-bearer)
- 03CHeKT authPartner serviceIssue access_token (1h TTL)
- 04Partner serviceCHeKT APICall API with Bearer <access_token>
- 05Partner servicePartner serviceRotate signing key quarterlyPublish new JWK; CHeKT picks it up
import { SignJWT, importJWK } from 'jose';
const privateJwk = JSON.parse(process.env.PARTNER_PRIVATE_JWK!);
const key = await importJWK(privateJwk, 'ES256');
const jwt = await new SignJWT({})
.setProtectedHeader({ alg: 'ES256', kid: 'partner-key-1' })
.setIssuer('your-partner-id')
.setSubject('your-partner-id')
.setAudience('https://api.chekt.com')
.setIssuedAt()
.setExpirationTime('5m')
.sign(key);
// Exchange for an access token
const res = await fetch('https://api.chekt.com/oauth/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
assertion: jwt,
}),
});
const { access_token } = await res.json();IP allow lists
Layered on top of any auth mode, an IP allow list restricts API calls to known source IPs — your VPC NAT egress, your office, your CI runners. Requests from outside the list are rejected at the edge before reaching the API.
Common auth errors
The six errors that account for ~95% of auth-related calls to support.
invalid_api_keyToken missing, malformed, or revoked. Confirm CHEKT_API_KEY is set and that the value matches what the dealer portal shows. If you rotated recently, the old key may have entered the 24h grace window and then been revoked.
token_expiredOAuth or assertion-token access tokens expire after their TTL. Use the refresh token (OAuth) or sign a new JWT (assertion) to get a fresh access token.
signature_invalidWebhook HMAC verification failed. You're probably verifying against parsed JSON instead of raw bytes, or the signing secret rotated without your service redeploying.
permission_deniedThe token is valid but missing the scope this endpoint requires. Open the app's permissions in the dealer portal and grant the matching scope.
ip_not_allowedThe request came from an IP outside the app's allow list. Add the source IP, or temporarily disable the allow list to confirm the rest of the call works.
rate_limitedYou're sending too many requests. Inspect the Retry-After header and back off with exponential jitter.
For the full error catalogue including non-auth codes, see the error reference. Every error response includes a request_id — log it.