Authentication
Every request to POST /api/v1/tasks must be authenticated with a project API
key. BugBoard supports two schemes — bearer (publishable keys) and HMAC
signatures (secret keys) — chosen by your key type. For help deciding, see
Choosing a key type.
Authentication proves who is reporting. To also keep the report body unreadable in transit — in the browser network tab, at proxies, and in logs — see Encrypted Transport, which uses a separate project encryption key.
Publishable keys (bearer)
Send the key as a bearer token:
Authorization: Bearer bb_pub_<48 hex chars>
Publishable keys are write-only and scoped to a single project. They're meant to be embedded in client code, so secrecy is not what protects them — the origin allow-list and your monthly quota are.
Secret keys (HMAC signature)
The secret is never sent. Instead, each request carries three headers computed from the signing secret:
X-Bb-Key-Id: bbk_<id> X-Bb-Timestamp: <unix seconds> X-Bb-Signature: <hex HMAC-SHA256>
Signing algorithm
sign(method, path, body, signingSecret, keyId):
timestamp = current unix time in SECONDS, as a string
payload = timestamp + "." + UPPERCASE(method) + "." + path + "." + sha256_hex(body)
signature = hmac_sha256_hex(key = signingSecret, message = payload)
return headers:
X-Bb-Key-Id = keyId
X-Bb-Timestamp = timestamp
X-Bb-Signature = signature
pathis the request path with its leading slash and no query string — for ingestion always/api/v1/tasks.bodyis the exact raw bytes you transmit. Re-serialising the JSON (different key order or whitespace) breaks the signature — sign the same bytes you send.timestampis integer Unix seconds sent as digits only — a millisecond value (JavaScript'sDate.now()) is rejected, so divide by 1000 first.- Both
sha256_hex(body)and the final signature are lowercase hex. The server compares them exactly, so uppercase hex or base64 will not match. - The server rejects any timestamp more than ±300 seconds from its clock
(replay protection). A bad signature is a hard
401with noRetry-After.
The server SDKs implement this for you; the full reference is in the API Reference.
Origin allow-list (publishable keys only)
A publishable key may carry a list of allowed origins. When the list is
non-empty, the server only accepts requests whose Origin (falling back to
Referer) matches an entry; an empty list accepts any origin. Entries may be
a full origin (https://app.example.com) or a bare host (app.example.com), and
a mobile identifier in the X-Bb-App-Id header is matched too.
This is a junk filter, not a security wall. A real browser can't forge
Origin, so it blocks other sites from using your key — but a script can send anyOrigin. The real cap on a leaked key is the quota.
Getting and rotating keys
- Open your project → Settings → API Keys (requires the
manageApiKeyability). - Create key, choose Publishable or Secret, name it, and for publishable keys optionally add allowed origins.
- Copy the shown value(s) immediately — BugBoard stores only a hash.
- Revoke any key at will (password-confirmed). Revocation is immediate and per-key, so you can rotate a leaked key without breaking other integrations.
Errors
Authentication failures return JSON:
| Status | Meaning |
|---|---|
401 |
Missing/invalid key, bad signature, or expired timestamp |
403 |
Publishable key used from a disallowed origin |
See Error Codes for the full list.