Errors & sandbox
Stable error codes, webhook signature verification, and test-mode keys.
Error envelope
Every error response uses the same envelope. error.code is stable and machine-readable; error.message is human-readable and may change. Validation errors carry error.details mapping field → messages.
{
"data": null,
"error": {
"code": "VALIDATION_ERROR",
"message": "Validation failed",
"details": { "contactId": ["Required"] }
}
}Error catalog
| Code | HTTP | Description |
|---|---|---|
| BAD_REQUEST | 400 | The request is malformed — unparseable body, invalid parameter shape. |
| UNAUTHORIZED | 401 | Missing, invalid, expired or revoked API key. |
| SUBSCRIPTION_REQUIRED | 402 | The organization needs an active subscription for this operation. |
| PLAN_LIMIT_EXCEEDED | 402 | A plan quota was reached (documents, seats, storage). Upgrade to continue. |
| FORBIDDEN | 403 | Authenticated, but not allowed to perform this action. |
| SCOPE_REQUIRED | 403 | The API key lacks the scope required by this endpoint. |
| MODULE_DISABLED | 403 | The feature module used by this endpoint is not enabled for the organization. |
| NOT_FOUND | 404 | The resource does not exist or belongs to another organization. |
| CONFLICT | 409 | The request conflicts with current state (duplicate, stale version). |
| VALIDATION_ERROR | 422 | Input failed validation. `error.details` maps field → messages. |
| RATE_LIMITED | 429 | Per-key rate limit exceeded. Honor the `Retry-After` header. |
| INTERNAL_ERROR | 500 | Unexpected server error. Safe to retry with backoff. |
| SERVICE_UNAVAILABLE | 503 | A dependent service (VIES, DNS, registrar) is unavailable or not configured. |
Sandbox / test keys
brivio_sk_test_… keys authenticate against a paired sandbox organization, provisioned automatically on first use. Data is fully isolated from your live organization, fiscal identifiers stay empty, and no ANAF/e-Factura submissions or other external side effects ever fire. Webhooks are environment- scoped too: endpoints registered with a test key only receive test events.
Verify webhook signatures
Deliveries are signed with HMAC-SHA256 over the raw request body (X-Brivio-Signature: sha256=<hex>) and carry a X-Brivio-Timestamp (unix seconds) for replay protection:
import { createHmac, timingSafeEqual } from "node:crypto";
function verifyBrivioWebhook(
rawBody: string,
signatureHeader: string, // "sha256=<hex>"
timestampHeader: string, // unix seconds
secret: string,
): boolean {
const ts = Number(timestampHeader);
if (!ts || Math.abs(Date.now() / 1000 - ts) > 300) return false; // 5 min replay window
const expected = createHmac("sha256", secret).update(rawBody, "utf8").digest("hex");
const actual = signatureHeader.replace(/^sha256=/, "");
if (expected.length !== actual.length) return false;
return timingSafeEqual(Buffer.from(expected), Buffer.from(actual));
}Or use verifyWebhookSignature() from the @brivio/sdk package, which implements the same check.