Datele tale, decizia ta

Alege ce putem păstra pe dispozitivul tău

  • Niciun script de analiză sau marketing nu rulează înainte să decizi — nici măcar în fundal.
  • Fiecare categorie este o decizie separată. Acceptarea uneia nu le acceptă pe celelalte.
  • Îți poți schimba sau retrage alegerea oricând, dintr-un singur clic.

Alege ce putem păstra pe dispozitivul tău

Folosim cookie-uri esențiale pentru funcționarea platformei și, doar cu acordul tău, cookie-uri funcționale, de analiză sau de marketing. Nimic neesențial nu se încarcă fără consimțământul tău. Detalii în Politica de cookie-uri

Strict necesare

Autentificare, securitate (CSRF), preferință de limbă și temă, reținerea alegerii tale privind consimțământul. Întotdeauna active.

Funcționale

Rețin preferințe suplimentare (ex. setări de afișare) pentru o experiență îmbunătățită.

Analiză

Ne ajută să înțelegem cum este folosit site-ul (statistici agregate). Nu se încarcă fără acordul tău.

Marketing

Folosite pentru publicitate personalizată de către terți. Nu se încarcă fără acordul tău.

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": { "contact_id": ["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.
CONTACT_NOT_FOUND404The `contact_id` or `contact_external_id` on a document does not resolve to a contact in this organization. Create the contact first or check the external reference.
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.
EXTERNAL_ID_TAKEN409Another contact or article in this organization already carries this `external_id`. External ids are unique per organization; look the existing row up with `?external_id=` and update it instead.
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.
EXCHANGE_RATE_UNAVAILABLE422A non-RON document needs the BNR T-1 reference rate (Cod Fiscal art. 290) and it could not be obtained for that currency and issue date. Retry later or pass a positive `exchange_rate` explicitly; the rate is never defaulted to 1.
INVOICE_CREDITED422The invoice is fully reversed by credit notes (status CREDITED). Its status and balance are derived from the credit notes: it cannot take payments and its status cannot be set by hand.
INVOICE_NOT_DELETABLE409Only a DRAFT invoice that was never submitted to e-Factura can be deleted. An issued invoice is a numbered fiscal document: reverse it with a credit note (POST /v1/invoices/{id}/credit-note).
INVOICE_NOT_EDITABLE409The change is not allowed on this invoice. PATCH may move an issued invoice only between its chase states (SENT → VIEWED/OVERDUE) or cancel a draft; issue with /send, record payments with /payments, reverse with a credit note. Fiscal fields (contact, dates, currency, items) are frozen once the invoice is issued or known to e-Factura.
SOURCE_FULLY_CREDITED422The source invoice is already fully credited (status CREDITED); no further credit note can reverse it.
CREDIT_EXCEEDS_SOURCE422The credit note would take the cumulative credited amount above the source invoice total. A full storno is possible only once; partial credit notes may add up to the total and not beyond.
from_domain_not_verified422POST /v1/email/send: the `from` address is not on a sending domain this organization has verified. Verify it via /v1/email-domains (DKIM, SPF, DMARC) or send from a verified domain.
sender_identity_missing422POST /v1/email/send and /v1/emails/send: the `from` domain is verified but no sending identity (the mail-server account the message authenticates as) may send as that exact address, so the message would be refused. A `from` is sendable when it is the domain's sending address (`<from_local_part>@<domain>`) or a hosted mailbox of the organization, or one of its aliases, with API sending enabled (Mail settings). Nothing was recorded or sent. Re-run /v1/email-domains/{id}/verify if the domain identity is missing, or omit `from` to send as the organization default.
RATE_LIMITED429Per-key rate limit exceeded. Honor the `Retry-After` header.
INTERNAL_ERROR500Unexpected server error. Safe to retry with backoff.
send_failed502POST /v1/email/send: the mail transport refused the message and it could not be queued for retry. Nothing was delivered. A replay with the same Idempotency-Key returns this same error; retry with a new key.
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 follow Standard Webhooks: each request carries webhook-id, webhook-timestamp (unix seconds) and webhook-signature, a space-separated list of v1,<base64> tokens — HMAC-SHA256 over {id}.{timestamp}.{rawBody}, one token per active secret while a secret is being rotated. The timestamp is inside the MAC, so a captured delivery cannot be replayed with a new timestamp:

TypeScript
import { createHmac, timingSafeEqual } from "node:crypto";

function verifyBrivioWebhook(
  rawBody: string,
  id: string,        // "webhook-id" header
  timestamp: string, // "webhook-timestamp" header, unix seconds
  signature: string, // "webhook-signature" header, "v1,<base64> [v1,<base64>]"
  secret: string,    // "whsec_..."
): boolean {
  const ts = Number(timestamp);
  if (!Number.isInteger(ts) || Math.abs(Date.now() / 1000 - ts) > 300) return false; // 5 min window
  const key = Buffer.from(secret.replace(/^whsec_/, ""), "base64");
  const expected = Buffer.from(
    "v1," + createHmac("sha256", key).update(`${id}.${ts}.${rawBody}`, "utf8").digest("base64"),
  );
  return signature.split(" ").some((token) => {
    const actual = Buffer.from(token);
    return actual.length === expected.length && timingSafeEqual(actual, expected);
  });
}

Or use verifyWebhookSignature() from the @brivio-ro/sdk package, which implements the same check.