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.
How the sandbox works
Section titled “How the sandbox works”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.
Getting started with the sandbox
Section titled “Getting started with the sandbox”-
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_... -
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",}); -
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"
Sandbox limits
Section titled “Sandbox limits”The sandbox has usage limits to prevent abuse. These limits apply per account.
| Limit | Free tier | Paid plans |
|---|---|---|
| Invoices per month | 50 | 500 |
| Companies | 5 | 20 |
| Connections | 5 | 20 |
| API requests per minute | 30 | 120 |
| Data retention | 30 days | 30 days |
Test data
Section titled “Test data”Valid test VAT numbers
Section titled “Valid test VAT numbers”Use these VAT numbers in the sandbox. They pass format validation but do not correspond to real entities.
| Country | VAT number | Notes |
|---|---|---|
| Romania (RO) | RO12345678 | 8-digit CUI |
| Romania (RO) | RO1234567890 | 10-digit CUI |
| Italy (IT) | IT12345678901 | 11-digit Partita IVA |
| Belgium (BE) | BE0123456789 | 10-digit with leading 0 |
| Poland (PL) | PL1234567890 | 10-digit NIP |
| France (FR) | FR12345678901 | 11-character SIREN |
| Germany (DE) | DE123456789 | 9-digit USt-IdNr |
Simulated invoice statuses
Section titled “Simulated invoice statuses”In the sandbox, invoices follow a predictable lifecycle:
created— Returned immediately afterPOST /v1/invoicesvalidated— After ~1 second (validation rules run)converting— After ~1 second (JSON-to-XML conversion)submitting— After ~1 second (simulated upload)submitted— After ~2 seconds (simulated government receipt)accepted— After ~5 seconds (simulated government acceptance)
The full cycle takes approximately 10 seconds. Webhooks fire at each transition.
Triggering rejections
Section titled “Triggering rejections”To test error handling, use specific patterns in the sandbox:
| Trigger | Behavior |
|---|---|
Customer VAT ending in 999 | Invoice is rejected with a simulated government error |
Unit price of exactly 0.01 | Triggers a validation error (below minimum) |
Note containing FORCE_TIMEOUT | Simulates a government timeout (status stays at submitting) |
Note containing FORCE_ERROR | Simulates an internal error (status moves to error) |
// Trigger a rejection to test error handlingconst { 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 populatedDry-run validation
Section titled “Dry-run validation”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);}Switching to production
Section titled “Switching to production”When you are ready to go live:
-
Verify your integration — Ensure all invoice scenarios work correctly in the sandbox: successful submissions, rejected invoices, webhook delivery, and idempotency key handling.
-
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.
-
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 -
Monitor the first submissions — Watch the first few invoices in the dashboard to confirm they are accepted by the government system.
Next steps
Section titled “Next steps”- Quickstart — Build your first integration
- Error handling — Handle validation and government errors
- Webhooks guide — Set up real-time status notifications