BugBoardDocs

Error Codes

Every response from /api/* is JSON, errors included. The body always has a message; validation errors add a field-level errors map.

Status Meaning Body shape
200 OK — report deduplicated into an existing card { "data": { … }, "deduplicated": true }
200 Dropped — accepted and discarded, no card created { "dropped": true, "reason": "…" }
201 Created — a new card { "data": { … }, "deduplicated": false }
401 Missing/invalid key, bad signature, or expired timestamp { "message": "Invalid or missing project API key." }
403 Publishable key used from a disallowed origin { "message": "Origin not allowed." }
422 Validation failed (or the project has no board column) { "message": "…", "errors": { "field": ["…"] } }
429 Per-minute burst limit exceeded { "message": "…" } + Retry-After
5xx Server error { "message": "…" }

A 200 is not always a stored card. A report can be accepted and dropped — the server answers 200 with { "dropped": true, "reason": "…" } and no data. This is deliberate: an SDK watching your app must never surface our billing or project state as a failure inside your app. A drop is never retryable.

quota_exceeded: true rides along on every drop as a legacy alias of dropped, so SDK versions released before reason existed still know not to retry. Prefer dropped and reason in new code.

The card is hidden by default. The SDKs send X-Bb-Hide-Response, which drops data from the 200/201 body — you get a bare { "deduplicated": true } or { "deduplicated": false }. The status codes and the outcome flags (deduplicated, dropped, reason) are unchanged; only the card itself is withheld, so a report you encrypted isn't handed back in plaintext. See the API Reference.

Why a report was dropped

reason tells you which of three very different things happened — and each has a different fix.

reason What it means What to do
quota Your account's event allowance for the day is spent. It is shared across every project you own. Not retryable. It resets at midnight UTC, or upgrade for a bigger allowance. You are emailed at 75%, 90% and 100% before it runs out.
paused The project is paused — read-only, and accepting no events. It still has all of its data. Not retryable. Resume the project from your projects page. If it was paused because you're over your plan's project limit, free a slot or upgrade.
archived The project is archived — permanently read-only. Not retryable. Archiving is one-way; point your SDK at a live project.

Only an active project accepts events. Pausing or archiving a project stops ingestion for it immediately, and that applies to the API exactly as it does to the dashboard.

What to do with each

  • 401 / 403 / 422 are configuration or payload bugs — fix the request. They are not retryable; retrying sends the same broken request.
  • 429 is retryable: back off and honour the Retry-After header (see Rate Limits & Quotas).
  • 5xx and network errors are retryable with exponential backoff.
  • A drop (dropped: true) is never retryable, whatever the reason. Retrying a paused project just burns requests against your per-minute limit.

Never crash the app you're monitoring. When you call the API directly, wrap reporting in a try/catch and swallow failures. The SDKs do this for you — reporting is fire-and-forget and errors surface only on the SDK's debug channel.

SDK exception mapping

Every SDK maps these statuses to typed exceptions so you can handle them consistently:

HTTP JavaScript PHP Carries
401, 403 BugBoardAuthError AuthException message
422 BugBoardValidationError ValidationException fieldErrors (the errors map)
429 BugBoardRateLimitError RateLimitException retryAfter (seconds)
5xx / network BugBoardServerError ServerException message, underlying cause

A drop maps to no exception — it is a 200, and the report simply went nowhere. Nothing is thrown, nothing is retried. Read dropped / reason off the body if you want to log why.

Treat 401/403/422 as non-retryable and retry only 429, 5xx, and network errors. Retry-After is honoured verbatim in seconds and takes precedence over the SDK's own backoff; a non-numeric value is ignored.

Common 422 causes

  • Missing severity — it's the one required field.
  • Invalid enumseverity or priority outside its allowed set (see the API Reference). description_type and status are decided by the server and ignored if sent, so they never cause a 422.
  • Oversized fieldtitle > 255, a tag > 50, or description > 65535 characters.