Skip to content

Quickstart

Mandato is a single REST API that abstracts all EU e-invoicing government systems. You send JSON, and we convert it to the right XML format, authenticate with the right government, submit it, track the status, and fire webhooks when things change.

This guide walks you through submitting your first invoice.

  • A Mandato account (sign up at getmandato.dev)
  • An API key from the dashboard
  • Node.js 18+ (if using the SDK)
  1. Install the SDK

    Terminal window
    npm install @getmandato/sdk
  2. Initialize the client

    Create a MandatoClient instance with your API key. Use a test key (sk_test_) during development and a live key (sk_live_) in production.

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

    Before you can submit invoices, register the company that will be the supplier. This only needs to be done once per company.

    const { data: company } = await mandato.companies.create({
    name: "TechVision SRL",
    vatNumber: "RO12345678",
    country: "RO",
    address: {
    street: "Strada Exemplu 42",
    city: "Bucharest",
    postalCode: "010101",
    country: "RO",
    },
    });
    console.log("Company created:", company.id);
  4. Connect to a government system

    Initialize the connection between your company and the target country’s e-invoicing system. For Romania, this starts an OAuth2 flow with ANAF.

    const { data: connection } = await mandato.connections.create({
    companyId: company.id,
    countryCode: "RO",
    });
    // For Romania, redirect the user to complete ANAF OAuth
    if (connection.authorizationUrl) {
    console.log("Complete OAuth at:", connection.authorizationUrl);
    }
  5. Submit your first invoice

    Send an invoice as JSON. Mandato converts it to the correct XML format (UBL CIUS-RO for Romania), validates it, and submits it to the government system.

    const { data: invoice } = await mandato.invoices.create({
    country: "RO",
    companyId: company.id,
    supplier: {
    vatNumber: "RO12345678",
    name: "TechVision SRL",
    address: {
    street: "Strada Exemplu 42",
    city: "Bucharest",
    postalCode: "010101",
    country: "RO",
    },
    },
    customer: {
    vatNumber: "RO87654321",
    name: "Client Corp SRL",
    address: {
    street: "Bulevardul Unirii 10",
    city: "Cluj-Napoca",
    postalCode: "400001",
    country: "RO",
    },
    },
    lines: [
    {
    description: "Software development services - January 2025",
    quantity: 1,
    unitPrice: 5000,
    vatRate: 19,
    },
    {
    description: "Cloud hosting",
    quantity: 1,
    unitPrice: 200,
    vatRate: 19,
    },
    ],
    currency: "RON",
    issueDate: "2025-01-15",
    dueDate: "2025-02-15",
    paymentMeans: {
    bankAccount: "RO49AAAA1B31007593840000",
    bic: "AAABROBU",
    paymentTerms: "Net 30",
    },
    note: "Thank you for your business.",
    });
    console.log("Invoice created:", invoice.id);
    console.log("Status:", invoice.status);
    // Status will be "created" initially, then progress through
    // "validated" -> "converting" -> "submitting" -> "submitted" -> "accepted"
  6. Check the invoice status

    Poll the invoice to see its current status, or set up webhooks to get notified automatically.

    const { data: updated } = await mandato.invoices.get(invoice.id);
    console.log("Status:", updated.status);
    console.log("Government ID:", updated.govId);
    console.log("Net amount:", updated.netAmount);
    console.log("VAT amount:", updated.vatAmount);
    console.log("Gross amount:", updated.grossAmount);
  7. View the event timeline

    Every invoice has a full event timeline showing each status transition.

    const { data: events } = await mandato.invoices.getEvents(invoice.id);
    for (const event of events) {
    console.log(`${event.createdAt} - ${event.event} (${event.status})`);
    }
    // 2025-01-15T10:00:00Z - invoice.created (created)
    // 2025-01-15T10:00:01Z - invoice.validated (validated)
    // 2025-01-15T10:00:02Z - invoice.submitted (submitted)
    // 2025-01-15T10:00:15Z - invoice.accepted (accepted)

If you prefer to call the REST API directly, all endpoints accept JSON and return JSON.

Terminal window
curl -X POST https://api.getmandato.dev/v1/invoices \
-H "Authorization: Bearer sk_test_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"country": "RO",
"supplier": {
"vatNumber": "RO12345678",
"name": "TechVision SRL"
},
"customer": {
"vatNumber": "RO87654321",
"name": "Client Corp SRL"
},
"lines": [
{
"description": "Software development services",
"quantity": 1,
"unitPrice": 5000,
"vatRate": 19
}
],
"currency": "RON"
}'

Every POST /v1/invoices request can include an X-Idempotency-Key header. If you retry the same request with the same key, you will get back the original invoice instead of creating a duplicate.

const { data: invoice } = await mandato.invoices.create(
{
country: "RO",
supplier: { vatNumber: "RO12345678", name: "TechVision SRL" },
customer: { vatNumber: "RO87654321", name: "Client Corp SRL" },
lines: [{ description: "Consulting", unitPrice: 1000, vatRate: 19 }],
},
"order-12345-inv-001" // idempotency key
);

When you call POST /v1/invoices, Mandato:

  1. Validates your JSON against the target country’s rules (e.g., BR-RO rules for Romania)
  2. Converts the JSON into the correct XML format (e.g., UBL 2.1 CIUS-RO)
  3. Authenticates with the government system using stored OAuth tokens
  4. Submits the XML to the government API (e.g., ANAF e-Factura)
  5. Polls for the government’s response (accepted or rejected)
  6. Fires webhooks when the status changes

All of this happens asynchronously after the initial API response. The invoice is created immediately with a created status, and progresses through the pipeline.