Brivio Developers
Create accountSign in
OverviewQuickstartAuthenticationIdempotencyWebhooksErrors & sandboxSDKs & CLIMCP serverOAuth2 appsAPI reference

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.

Error response
{
  "data": null,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Validation failed",
    "details": { "contactId": ["Required"] }
  }
}

Error catalog

CodeHTTPDescription
BAD_REQUEST400The request is malformed — unparseable body, invalid parameter shape.
UNAUTHORIZED401Missing, invalid, expired or revoked API key.
SUBSCRIPTION_REQUIRED402The organization needs an active subscription for this operation.
PLAN_LIMIT_EXCEEDED402A plan quota was reached (documents, seats, storage). Upgrade to continue.
FORBIDDEN403Authenticated, but not allowed to perform this action.
SCOPE_REQUIRED403The API key lacks the scope required by this endpoint.
MODULE_DISABLED403The feature module used by this endpoint is not enabled for the organization.
NOT_FOUND404The resource does not exist or belongs to another organization.
CONFLICT409The request conflicts with current state (duplicate, stale version).
IDEMPOTENCY_KEY_REUSED409This Idempotency-Key was already used with different request parameters. Generate a fresh key for a different request — reusing one is how a retry silently returns the previous call’s result.
IDEMPOTENCY_IN_PROGRESS409Another request with this Idempotency-Key is still executing. Retry after a short delay to receive the stored response.
VALIDATION_ERROR422Input failed validation. `error.details` maps field → messages.
RATE_LIMITED429Per-key rate limit exceeded. Honor the `Retry-After` header.
INTERNAL_ERROR500Unexpected server error. Safe to retry with backoff.
SERVICE_UNAVAILABLE503A 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:

TypeScript
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.