Skip to main content

The four headers

Every request to the PontisGlobe API must carry these headers:
HeaderValue
x-api-keyYour API key from Developer Tools
x-timestampCurrent Unix timestamp in seconds, as a string
x-signatureHMAC-SHA256(timestamp + "." + encrypted_body, HMAC_SECRET), hex-encoded
authorizationBearer <JWT> — required on every endpoint except /api/v1/user/login

The request envelope

The HTTP body is always:
{ "data": "<encrypted-blob>" }
The data field is your real payload, encrypted with AES-256-GCM using your Encryption Secret. The blob format is iv:tag:ciphertext, each component base64url-encoded:
9hMv3qB7E2x…:Z1jK9pA…:r6sL8nO2…

Sign in 3 steps

1

Encrypt the body

AES-256-GCM with a random 12-byte IV. The key is your Encryption Secret (base64url, 32 bytes after decoding).
const iv = randomBytes(12)
const c = createCipheriv('aes-256-gcm', encKey, iv)
const ct = Buffer.concat([c.update(JSON.stringify(payload), 'utf8'), c.final()])
const enc = `${iv.toString('base64url')}:${c.getAuthTag().toString('base64url')}:${ct.toString('base64url')}`
2

Sign timestamp + encrypted body

HMAC-SHA256 over the literal string "${timestamp}.${enc}", hex-encoded.
const ts = String(Math.floor(Date.now() / 1000))
const sig = createHmac('sha256', hmacKey).update(`${ts}.${enc}`).digest('hex')
3

Send

fetch(url, {
  method: 'POST',
  headers: {
    'content-type': 'application/json',
    'x-api-key': API_KEY,
    'x-timestamp': ts,
    'x-signature': sig,
    ...(jwt ? { authorization: `Bearer ${jwt}` } : {}),
  },
  body: JSON.stringify({ data: enc }),
})

The 15-minute JWT

POST /api/v1/user/login returns:
{
  "ok": true,
  "data": {
    "access_token": "eyJhbGciOi…",
    "token_type": "Bearer",
    "expires_in": 900
  }
}
Pass the access_token as Authorization: Bearer <token> on every endpoint that requires authentication. Tokens are bound to:
  • Your consumer account (regenerating credentials invalidates active tokens immediately)
  • The mode (a live JWT cannot be used in sandbox and vice versa)
Re-login when you receive 401 unauthorized with code jwt_expired.

Replay protection

We reject requests where x-timestamp drifts more than 5 minutes from server time. Keep your client’s clock in sync (NTP).

Why all of this?

LayerDefeats
AES-256-GCM body encryptionPassive interception (even if TLS is broken)
HMAC-SHA256 signatureBody tampering and request forgery
Timestamp in signature scopeReplay attacks
Short-lived JWTToken leakage blast radius