Skip to content

Sandbox

The Mandato sandbox lets you build and test your e-invoicing integration without submitting anything to real government systems. Invoices go through the full validation and conversion pipeline, but submission is simulated.

When you use an API key with the sk_test_ prefix, all requests are routed to the test environment:

  • Validation runs the same rules as production (EN 16931 base rules + country-specific CIUS rules)
  • Conversion generates the same XML output (UBL, FatturaPA, XRechnung, etc.)
  • Submission is simulated — invoices are automatically accepted after a short delay
  • Connections are auto-activated without requiring real OAuth credentials
  • Webhooks fire with the same event payloads as production

This means your integration code works identically in both environments. The only difference is that sandbox invoices never reach a government system.

  1. Get a test API key — Create a test key from the dashboard under Settings > API Keys, or use the API:

    import { MandatoClient } from "@getmandato/sdk";
    const mandato = new MandatoClient({
    apiKey: "sk_test_your_existing_key",
    });
    const { data: key } = await mandato.apiKeys.create({
    name: "Development",
    environment: "test",
    });
    console.log("Test key:", key.key); // sk_test_...
  2. Create a test company — Register a company with test VAT numbers. In the sandbox, VAT number validation is relaxed.

    const { data: company } = await mandato.companies.create({
    name: "Test Company SRL",
    vatNumber: "RO12345678",
    country: "RO",
    });
  3. Submit test invoices — Submit invoices exactly as you would in production. The sandbox simulates the full lifecycle.

    const { data: invoice } = await mandato.invoices.create({
    country: "RO",
    companyId: company.id,
    supplier: {
    vatNumber: "RO12345678",
    name: "Test Company SRL",
    },
    customer: {
    vatNumber: "RO87654321",
    name: "Test Customer SRL",
    },
    lines: [
    {
    description: "Test service",
    quantity: 1,
    unitPrice: 100,
    vatRate: 19,
    },
    ],
    });
    console.log(invoice.status); // "created"

The sandbox has usage limits to prevent abuse. These limits apply per account.

LimitFree tierPaid plans
Invoices per month50500
Companies520
Connections520
API requests per minute30120
Data retention30 days30 days

Use these VAT numbers in the sandbox. They pass format validation but do not correspond to real entities.

CountryVAT numberNotes
Romania (RO)RO123456788-digit CUI
Romania (RO)RO123456789010-digit CUI
Italy (IT)IT1234567890111-digit Partita IVA
Belgium (BE)BE012345678910-digit with leading 0
Poland (PL)PL123456789010-digit NIP
France (FR)FR1234567890111-character SIREN
Germany (DE)DE1234567899-digit USt-IdNr

In the sandbox, invoices follow a predictable lifecycle:

  1. created — Returned immediately after POST /v1/invoices
  2. validated — After ~1 second (validation rules run)
  3. converting — After ~1 second (JSON-to-XML conversion)
  4. submitting — After ~1 second (simulated upload)
  5. submitted — After ~2 seconds (simulated government receipt)
  6. accepted — After ~5 seconds (simulated government acceptance)

The full cycle takes approximately 10 seconds. Webhooks fire at each transition.

To test error handling, use specific patterns in the sandbox:

TriggerBehavior
Customer VAT ending in 999Invoice is rejected with a simulated government error
Unit price of exactly 0.01Triggers a validation error (below minimum)
Note containing FORCE_TIMEOUTSimulates a government timeout (status stays at submitting)
Note containing FORCE_ERRORSimulates an internal error (status moves to error)
// Trigger a rejection to test error handling
const { data: invoice } = await mandato.invoices.create({
country: "RO",
supplier: { vatNumber: "RO12345678", name: "Test SRL" },
customer: { vatNumber: "RO87654999", name: "Rejected Customer" }, // ends in 999
lines: [{ description: "Test", unitPrice: 100, vatRate: 19 }],
});
// After ~10 seconds, status will be "rejected"
// The errorMessage and errorTranslated fields will be populated

Use the validate endpoint to check an invoice without creating it. This is useful for form validation in your UI.

const result = await mandato.invoices.validate({
country: "RO",
supplier: { vatNumber: "RO12345678", name: "Test SRL" },
customer: { vatNumber: "RO87654321", name: "Customer SRL" },
lines: [{ description: "Test", unitPrice: 100, vatRate: 19 }],
});
if (result.valid) {
console.log("Invoice is valid");
} else {
console.log("Errors:", result.errors);
}

When you are ready to go live:

  1. Verify your integration — Ensure all invoice scenarios work correctly in the sandbox: successful submissions, rejected invoices, webhook delivery, and idempotency key handling.

  2. Set up real government connections — Production connections require real OAuth credentials. For Romania, you need an ANAF SPV account and a valid digital certificate. See the Romania (ANAF) guide for detailed setup.

  3. Switch your API key — Replace your test key with a production key:

    Terminal window
    # Before (test)
    MANDATO_API_KEY=sk_test_your_test_key
    # After (production)
    MANDATO_API_KEY=sk_live_your_production_key
  4. Monitor the first submissions — Watch the first few invoices in the dashboard to confirm they are accepted by the government system.