Home
Home/Developers/Authentication
Ed25519 signaturesSHA-256 hashes8 SDKs
Status
Sign inStart free
Home
Start here
Overview
Authentication
Errors
FAQ
API
Events
Canonical JSON and hashes
Documents
Anchoring a file
AI attestations
Attestation schema
Verifying attestations
Traces
Sealing a trace
Audit logs
Organizations
Streams
Portal
Public proof
Event schema
Exporting events
Integrations
Clerk
Auth0
Embeddable viewer
All endpoints
Reference
SDKs
Node.js
Python
Go
Java
Ruby
Rust
.NET
PHP
REST
Verification
Docs · Authentication

Authentication

Send an API key on every call except the public proof and key endpoints. Keys carry scopes and share one rate limit per tenant.

Create a keyInspect a key
sha-256 · 3a352297…2592
sha-256 · bfafb4ec…e85c
Send a key

Put the key in the Authorization header with the Bearer scheme. X-API-Key works only when no Authorization header is sent.

Formatinvoance_live_<8 characters>_<43 characters>
Length66 characters
StorageShown once at creation; the server stores only its SHA-256.
No key neededThe /v1/proof routes and GET /keys/{domain}.
BrowsersThe API key routes carry no CORS layer, so browser JavaScript cannot call them cross-origin.
curl https://api.invoance.com/v1/me \
  -H "Authorization: Bearer $INVOANCE_API_KEY"
Headers
Authorizationstring

Bearer scheme followed by the API key, for example Authorization: Bearer invoance_live_Qm3xZ9aK_<43 characters>; read first when both headers are present.

X-API-Keystring

The raw API key; used only when no Authorization header is present.

Scopes

Ledger scopes (read, write) and audit scopes (audit:read, audit:write) are separate sets. A key without the scope an endpoint checks gets 403 insufficient_scope.

readGet, list and verify events, documents and AI attestations, download a stored original, read the raw attestation bytes, and get, list and download proofs of traces; a key with write passes every read check.
writeIngest events, anchor documents, ingest AI attestations, and create, seal and delete traces.
audit:readGet, list and verify audit events, create and fetch exports, list audit organizations and their streams, and read organization integrity; a key with audit:write passes every audit:read check.
audit:writeIngest audit events, create, update, archive, unarchive and delete audit organizations, set retention, create, delete and test streams, and create portal sessions.
Create a keyOpen the dashboard
  1. Open API & Access

    Sign in at app.invoance.com as an owner or admin. In the sidebar open Settings, then API & Access.

  2. Create token

    Click Create token. Give it a name, pick its scopes (Read is preselected), and add IP addresses or CIDR ranges if it should only work from known hosts. Empty allows any address.

  3. Copy it now

    The token is shown once. A lost token cannot be recovered; revoke it and create another.

  4. Set INVOANCE_API_KEY

    Every SDK reads INVOANCE_API_KEY from the environment. Keep it out of source control.

  5. Check it

    Call GET /v1/me. It checks no scope, so any active key answers with its organization, scopes and rate limit.

export INVOANCE_API_KEY="invoance_live_Qm3xZ9aK_..."
RevokeThe next request with that key gets 401 api_key_revoked.
Change scopes or IP rulesApplies on the next request.
Change plan or rate limitWaits for the 900 second key cache unless billing clears it.
Rate limits
Per secondThe plan's rate_limit_per_sec: developer 10, builder 50, growth 200, compliance 500, enterprise 500; or the organization's override when one is set.
Per minute60 times the per-second figure.
Shared byEvery key under the tenant. GET /v1/me reports the figure as limits.rate_limit_per_sec.
Monthly quotasSeparate. A used-up quota is 429 quota_exceeded with no Retry-After.
  • Buckets are fixed windows on the wall clock: one Redis counter per second and one per minute, keyed by tenant id, incremented atomically before the handler runs.
  • The limiter runs before authentication and looks the key up in the auth cache; a key that is not cached yet (its first request, or the first after a revoke, scope or IP rule change) is counted in one shared anonymous bucket at 10 requests per second, and so is a request with no key.
Headers on a 429
Retry-Afterinteger

Seconds to wait before retrying: 1 when the per-second bucket is full, the seconds left in the current minute when the minute bucket is full.

X-RateLimit-Limitinteger

The per-second limit that applied to the request; sent only on a 429.

X-RateLimit-Remaininginteger

Always 0 on a 429; the header is not sent on successful responses.

Idempotency

Send an Idempotency-Key header on POST /v1/events, POST /v1/document/anchor and POST /v1/ai/attestations (optional) and on POST /v1/audit/events (required); a retry with the same key and the same request gets the stored status and body back instead of creating a second record.

Header
Idempotency-Keystring · 1 to 128 characters after trimming on POST /v1/events; 1 to 255 on POST /v1/audit/events; only trimmed and non-empty on document anchor and AI attestation ingest

Caller-chosen unique string, for example a UUID; stored per tenant, API key and endpoint, so the same value on another key or endpoint is a different key.

Kept for24 hours
  • The same key with a different request hash returns 409 idempotency_key_reuse_mismatch with message "idempotency key was already used with a different request body".
  • A retry that arrives while the first request is still in flight gets 202 {"status":"processing"}; that reservation lives 120 seconds if the first request never finishes, and is deleted at once when the first request fails with an error, so the next retry is processed again.
  • POST /v1/audit/events requires the header (400 missing_idempotency_key when absent, 400 invalid_idempotency_key over 255 characters) and folds the audit organization into the key.
  • The in-process cache keys entries by tenant, API key and value only, so reusing one Idempotency-Key value on two different endpoints from the same key can be answered with 409 idempotency_key_reuse_mismatch; use a fresh value per request.
Key errorsEvery error code
CodeStatusWhen
missing_api_key401Neither an Authorization header nor an X-API-Key header was sent.
invalid_authorization_scheme401An Authorization header is present but does not start with Bearer.
invalid_api_key_format401The key does not start with invoance_live_.
invalid_api_key401No key with that value exists, or GET /v1/me found no row for the authenticated key.
api_key_revoked401The key exists but has been revoked.
ip_not_allowed403The key has IP rules and the request came from an address that is not listed.
insufficient_scope403The key lacks the scope the endpoint checks: read, write, audit:read or audit:write.
Inspect a key
GET/v1/me
API key

Introspect the API key

Returns the organization, tenant, key metadata and effective rate limit behind the calling key.

  • No scope is checked, so a key limited to audit:read or audit:write can call it; every SDK's validate() probes this endpoint and treats a 403 or 429 as proof that the key authenticated.
  • The response is sent with Cache-Control: no-store.
  • Sibling keys, dashboard users, billing details and the key's IP rules are not returned.
  • created_at and last_used_at are RFC 3339 as chrono writes them: a +00:00 offset rather than a trailing Z, with fractional seconds when the stored time has them.
  • Node returns a typed MeResponse; Python, Ruby and PHP return the decoded JSON as a dict, Hash or array; Go returns map[string]any; Java returns Map<String, Object>; Rust returns serde_json::Value; .NET returns a JsonNode.
  • The .NET README says ValidateAsync probes GET /v1/events?limit=1; the code probes GET /v1/me like the other SDKs.
Node.js
import { InvoanceClient } from "invoance";

// Reads INVOANCE_API_KEY from the environment.
const client = new InvoanceClient();

const me = await client.me();
console.log(me.organization.primary_domain, me.organization.plan_tier);
console.log(me.api_key.scopes, me.limits.rate_limit_per_sec);
Response · 200
{
  "valid": true,
  "organization": {
    "id": "9b2f6c41-3d7e-4a58-8c0b-1e5f7a29d6c3",
    "name": "Acme Corp",
    "issuer_name": "Acme Corp",
    "primary_domain": "acme.com",
    "domain_verified": true,
    "plan_tier": "growth"
  },
  "tenant": {
    "id": "4e8a1d27-6b3c-4f9e-a2d5-7c0b9e3f1a68",
    "name": "Acme Corp"
  },
  "api_key": {
    "id": "c7d3e9f1-2a4b-4c6d-8e0f-1b3d5f7a9c2e",
    "name": "production",
    "key_prefix": "invoance_live_Qm3xZ9aK",
    "key_last4": "7hT2",
    "scopes": [
      "read",
      "write"
    ],
    "created_at": "2026-05-14T09:30:12Z",
    "last_used_at": "2026-09-22T08:14:07Z"
  },
  "limits": {
    "rate_limit_per_sec": 200
  }
}
Response fields
validboolean · required

Always true on a 200; the SDK validate() helpers map it directly.

organizationobject · required

The organization the key belongs to.

organization.iduuid · required

Organization id.

organization.namestring · required

Organization display name.

organization.issuer_namestring · required

Issuer name shown on proof pages.

organization.primary_domainstring · required

Primary domain, the value GET /keys/{domain} resolves.

organization.domain_verifiedboolean · required

True when domain ownership has been verified.

organization.plan_tierstring · required

Plan tier as stored. One of developer, builder, growth, compliance, enterprise.

tenantobject · required

The tenant that owns the signing key, the API keys and the rate limit budget.

tenant.iduuid · required

Tenant id.

tenant.namestring · required

Tenant name.

api_keyobject · required

Metadata of the calling key.

api_key.iduuid · required

Id of the calling key.

api_key.namestring

Key name given at creation; null when the stored name is empty.

api_key.key_prefixstring · required

invoance_live_ plus the first 8 characters of the secret.

api_key.key_last4string · required

Last 4 characters of the key.

api_key.scopesstring[] · required

Scopes exactly as stored. One of read, write, audit:read, audit:write.

api_key.created_attimestamp (ISO 8601) · required

When the key was created.

api_key.last_used_attimestamp (ISO 8601)

Last use recorded before this call; omitted when the key was never used.

limitsobject · required

Effective limits for the key.

limits.rate_limit_per_secinteger · required

Requests per second the tenant may make: the organization override when set, otherwise the plan value, at least 1.

Errors
missing_api_key401

No Authorization header and no X-API-Key header was sent.

invalid_authorization_scheme401

The Authorization header does not use the Bearer scheme.

invalid_api_key_format401

The key does not start with invoance_live_.

invalid_api_key401

The key does not exist, or no row was found for it.

api_key_revoked401

The key has been revoked.

ip_not_allowed403

The key has IP rules and the request came from another address.

rate_limited429

The tenant's per-second or per-minute budget is used up; Retry-After is set.

api_key_lookup_failed500

The key lookup query failed.

api_key_required401

The authenticated context carries no API key id.

db_error500

The introspection query failed.

Verified documents and records. Invoance records the exact version of every invoice, report and notice your company sends, so the people who receive them can confirm they have the version you sent, without taking your word for it.

Products

  • Evidence Rooms
  • Audit Logs
  • Event Ledger
  • AI Attestation
  • Document Anchoring
  • Traces

Developers

  • Documentation
  • API reference
  • SDKs
  • How it works
  • How traces seal
  • System status

Verify

  • Audit Log
  • Event
  • AI Attestation
  • Document
  • Trace

Company

  • Company overview
  • What is Invoance
  • Pricing
  • Security
  • Compliance teams
  • Finance teams
  • Partners
  • Brand assets
  • Resources
  • Help center
  • Contact
© 2025 – 2026 Invoance, Inc. All rights reserved.© 2026 Invoance, Inc. All rights reserved.
PrivacyLegal noticeLegal FAQ