Skip to content

Authentication

Every request to the Mandato API must include a valid API key. Keys are scoped to an account and determine which environment (test or production) the request targets.

Mandato API keys use a prefix that indicates the environment:

PrefixEnvironmentDescription
sk_test_TestSandbox environment. No real government submissions.
sk_live_ProductionReal submissions to government e-invoicing systems.

A full key looks like this:

sk_test_4f8a9b2c3d1e6f7a8b9c0d1e2f3a4b5c
sk_live_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6

Include your API key in the Authorization header as a Bearer token on every request:

Terminal window
curl https://api.getmandato.dev/v1/invoices \
-H "Authorization: Bearer sk_test_your_api_key_here"

With the Node.js SDK, pass the key when constructing the client:

import { MandatoClient } from "@getmandato/sdk";
const mandato = new MandatoClient({
apiKey: "sk_test_your_api_key_here",
});

The SDK automatically includes the Authorization: Bearer header on every request.

Mandato operates two fully isolated environments. The environment is determined by the API key prefix — there is no separate base URL.

  • Invoices are validated and converted to XML but not submitted to real government systems
  • ANAF, SDI, KSeF, and other connections are simulated
  • Useful for integration testing, CI/CD pipelines, and development
  • Subject to sandbox limits (50 invoices/month on the free tier)
  • Data is retained for 30 days
  • Invoices are validated, converted, and submitted to real government systems
  • Requires active connections with valid OAuth tokens or certificates
  • Usage counts toward your plan limits
  • Data is retained for the legally required period (10 years for audit logs)

Log in to the dashboard, navigate to Settings > API Keys, and click Create API Key. Select the environment (test or production) and give it a descriptive name.

You can also create keys programmatically. You need an existing key to authenticate.

const { data: newKey } = await mandato.apiKeys.create({
name: "CI/CD pipeline",
environment: "test",
expiresAt: "2026-01-01T00:00:00Z", // optional expiration
});
// IMPORTANT: The full key is only returned once
console.log("New key:", newKey.key);
console.log("Key ID:", newKey.id);
console.log("Prefix:", newKey.keyPrefix);

List all keys for your account. The key value is masked — only the prefix is shown.

const { data: keys } = await mandato.apiKeys.list();
for (const key of keys) {
console.log(`${key.name} (${key.keyPrefix}...) - ${key.environment}`);
console.log(` Created: ${key.createdAt}`);
console.log(` Last used: ${key.lastUsedAt ?? "never"}`);
console.log(` Expires: ${key.expiresAt ?? "never"}`);
console.log(` Revoked: ${key.revokedAt ?? "no"}`);
}

Revoked keys are immediately invalidated and cannot be used again.

const { data: result } = await mandato.apiKeys.revoke("key_id_here");
console.log("Revoked:", result.revoked); // true
  1. Never commit API keys to source control. Use environment variables or a secrets manager.
const mandato = new MandatoClient({
apiKey: process.env.MANDATO_API_KEY!,
});
  1. Use separate keys per environment. Have distinct keys for development, staging, and production.

  2. Set expiration dates on keys that are used in automated pipelines or temporary integrations.

  3. Revoke compromised keys immediately. If a key is leaked, revoke it through the dashboard or API and create a replacement.

  4. Use the minimum number of keys necessary. Each key is a potential attack surface.

If authentication fails, the API returns a 401 status:

{
"error": {
"type": "authentication_error",
"message": "Invalid API key",
"requestId": "req_abc123"
}
}

Common causes:

  • Missing Authorization header
  • Malformed header (must be Bearer <key>, not just the key)
  • Revoked or expired key
  • Key does not exist

API keys are rate-limited per account. The current limits are returned in response headers:

HeaderDescription
X-RateLimit-LimitMaximum requests per minute
X-RateLimit-RemainingRequests remaining in current window
X-RateLimit-ResetUnix timestamp when the window resets
Retry-AfterSeconds to wait (only on 429 responses)

If you exceed the rate limit, you receive a 429 Too Many Requests response. The SDK automatically retries with exponential backoff.