Authentication

Sign in to CONNEX and generate an API key — a client id and secret. Your server trades it for a one-hour access token, and sends that token with every API call.

How it works

CONNEX uses the OAuth 2.0 client credentials flow — your server authenticates as your CONNEX account, with no extra sign-in. Access tokens are short-lived, so a leaked one stops working within the hour, and the long-lived secret never leaves your server except to fetch them.

Get an API key

  1. Sign in to CONNEX with your usual account.
  2. Open API keys and choose Generate API key. Give it a name you'll recognise — “Shop”, “ERP” — and choose when it expires: 30 days, 90 days, 1 year, a date of your own, or never.
  3. Copy the client_id (cnx_client_…) and client_secret (cnx_sk_…) into your server's configuration.
Open API keys in CONNEX
The secret is shown once — CONNEX stores only a hash of it. Keep it on your server: never in a browser, a mobile app or a public repository. If it leaks, rotate it on the same screen — the old secret and every token made with it stop working immediately.

Make one key per integration, so each can be rotated or deleted on its own. An account can have up to five active keys; expired ones don't count.

Expiry

An expired key can't get tokens, and tokens already issued with it stop working at the same moment — a token never outlives its key, so expires_in can be under an hour near the end. The key list in CONNEX warns you a week ahead; use Change expiry to extend it. The secret stays the same, so nothing using the key needs updating — and an expired key can be renewed the same way.

Scopes & your account

A key belongs to the CONNEX account it was generated in, and reaches that account's data only — there is no parameter to name another one. Tokens carry every scope unless you ask for fewer.

ScopeGrants
rates:readGet rates — live shipping options.
tracking:readGet tracking for your account's orders.
orders:writeCreate orders in your account.
webhooks:writeWebhook endpoints for your account.

Signed in to more than one CONNEX account? Keys belong to whichever account is selected when you generate them.

Get an access token

POST/oauth/token

Call from your server with your key as HTTP Basic auth (or client_id / client_secret form fields). The body is application/x-www-form-urlencoded. There is no refresh token: when the hour is up, ask for a new token the same way.

Body parameters

grant_typestringrequired
client_credentials
scopestring
Space-separated, to ask for fewer scopes than all. Omit it to get every scope.

Response

access_tokenstring
Send as Authorization: Bearer … on every API call.
expires_ininteger
Seconds until the token expires — 3600.
scopestring
The scopes this token carries.
accountobject
The CONNEX account the key belongs to — { fastShipId, name }.
ErrorMeaning
401 invalid_clientWrong client id or secret — or the secret was rotated, the key deleted, or the key expired (the message says which).
400 unsupported_grant_typegrant_type is not client_credentials.
400 invalid_scopeA scope that doesn't exist.
Request
curl https://api.connexglobal.co/oauth/token \
  -u "$CONNEX_CLIENT_ID:$CONNEX_CLIENT_SECRET" \
  -d grant_type=client_credentials
Responsejson
{
  "access_token": "cnx_at_eyJ0eXAiOiJjbnhfYXQi…",
  "token_type": "bearer",
  "expires_in": 3600,
  "scope": "rates:read tracking:read orders:write webhooks:write",
  "account": { "fastShipId": 137971, "name": "21APRIL Jewelry" }
}

Use the token

Send the token in the Authorization header of every /v1 request. Reuse it for its full hour — requesting a fresh token per call is slow and unnecessary.

  • 401 — the token expired or was revoked, or the key itself expired. Get a new token and retry once; if that is refused, check the key in CONNEX.
  • 403 — the token was requested without the scope this endpoint needs.
  • Revoking — rotate the secret or delete the key in CONNEX → Tools → API keys. Its tokens stop working within 30 seconds.
Reuse the token until it expiresjs
let cached = { token: '', expiresAt: 0 };

async function connexToken() {
  // Refresh a minute early, so a request never leaves with a token about to expire.
  if (Date.now() < cached.expiresAt - 60_000) return cached.token;
  const res = await fetch('https://api.connexglobal.co/oauth/token', { /* as above */ });
  if (!res.ok) throw new Error('CONNEX key rejected — check or rotate it');
  const t = await res.json();
  cached = { token: t.access_token, expiresAt: Date.now() + t.expires_in * 1000 };
  return cached.token;
}

const rates = await fetch('https://api.connexglobal.co/v1/rates', {
  method: 'POST',
  headers: {
    authorization: `Bearer ${await connexToken()}`,
    'content-type': 'application/json',
  },
  body: JSON.stringify({ Country: 'USA', Weight: 1200, Length: 30, Width: 20, Height: 10 }),
});