Documentation

API Reference

Our comprehensive API reference documentation.

Visit API Reference
Back to documentation

Getting Started with Recommand Peppol API

Getting Started

Welcome to Recommand's Peppol API documentation. This guide will help you set up and start using our API for Peppol document exchange.

Overview

Recommand's Peppol API provides a simple and effective way to integrate Peppol document exchange into your existing systems. The API supports document creation, sending, receiving, and management through simple REST endpoints.

Prerequisites

Before you begin, ensure you have:

Playground Environment

The Playground is a safe, isolated test environment. Playground teams look and behave like production teams, but they do not send documents over the real Peppol network. You can create as many playground teams as you like.

Key characteristics:

  • No real Peppol delivery: sending is simulated
  • Data isolation: documents and companies stay within the playground team
  • No billing/subscription checks and no SMP registrations
  • Webhooks work the same, triggered by simulated inbound delivery

Create a playground team

  • Dashboard: create a new team and choose Playground
  • API:
curl -X POST https://peppol.recommand.eu/api/peppol/playgrounds \
  -u key_xxx:secret_xxx \
  -H "Content-Type: application/json" \
  -d '{"name":"My Playground"}'
bash
const res = await fetch("https://peppol.recommand.eu/api/peppol/playgrounds", {
  method: "POST",
  headers: {
    Authorization:
      "Basic " + Buffer.from("key_xxx:secret_xxx").toString("base64"),
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ name: "My Playground" }),
});
const body = await res.json();
// body.success === true, body.playground.isPlayground === true
javascript

Using a playground

  • Use the same endpoints as production; just ensure your {teamId}/{companyId} belong to the playground team
  • Sending documents simulates delivery; a document is only “received” if the recipient company exists in the same playground team
  • Configure webhooks on the playground team to receive simulated inbound events (e.g. document.received)

Check if a team is a playground

curl -X GET https://peppol.recommand.eu/api/peppol/{teamId}/playground \
  -u key_xxx:secret_xxx
bash
const res = await fetch(
  "https://peppol.recommand.eu/api/peppol/{teamId}/playground",
  {
    headers: {
      Authorization:
        "Basic " + Buffer.from("key_xxx:secret_xxx").toString("base64"),
    },
  }
);
if (res.status === 404) {
  // Not a playground team
} else {
  const { playground } = await res.json();
  console.log("isPlayground:", playground.isPlayground);
}
javascript

Authentication

All API requests require Basic Authentication:

# Your API key as username, API secret as password
curl -X GET https://peppol.recommand.eu/api/peppol/{teamId}/companies \
  -u key_xxx:secret_xxx
bash
// Node.js example
const response = await fetch(
  "https://peppol.recommand.eu/api/peppol/{teamId}/companies",
  {
    headers: {
      Authorization:
        "Basic " + Buffer.from("key_xxx:secret_xxx").toString("base64"),
    },
  }
);
javascript

Core Concepts

Companies

Companies represent businesses that can send or receive Peppol documents. Each company must be registered before sending documents using the companies endpoint:

const response = await fetch(
  "https://peppol.recommand.eu/api/peppol/{teamId}/companies",
  {
    method: "POST",
    headers: {
      Authorization:
        "Basic " + Buffer.from("key_xxx:secret_xxx").toString("base64"),
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      name: "ACME Corporation",
      address: "123 Main Street",
      postalCode: "1000",
      city: "Brussels",
      country: "BE",
      enterpriseNumber: "0123456789",
      vatNumber: "BE0123456789",
    }),
  }
);
javascript

Sending Invoices

The primary function is sending Peppol-compliant invoices using the sendDocument endpoint.

A few things to note:

  • Remember to replace {companyId} with the correct company ID of the sender
  • Use your own API credentials, as explained above
  • The recipient field is the Peppol address of the recipient. In the example below, it's 0208:987654321, where 0208 is the Belgian Peppol Electronic Address Scheme and 987654321 is the recipient's enterprise number.
  • If you want to send a test invoice, you can simply register a new company and use it as both the sender and recipient.
const response = await fetch(
  "https://peppol.recommand.eu/api/peppol/{companyId}/sendDocument",
  {
    method: "POST",
    headers: {
      Authorization:
        "Basic " + Buffer.from("key_xxx:secret_xxx").toString("base64"),
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      recipient: "0208:987654321", // Peppol address of recipient
      documentType: "invoice",
      document: {
        invoiceNumber: "INV-2025-001",
        issueDate: "2024-05-15",
        dueDate: "2024-06-15",
        buyer: {
          vatNumber: "BE0987654321",
          name: "Customer Company",
          street: "Customer Street 1",
          city: "Antwerp",
          postalZone: "2000",
          country: "BE",
        },
        paymentMeans: [
          {
            iban: "BE1234567890",
          },
        ],
        lines: [
          {
            name: "Consulting Services",
            netPriceAmount: "100.00",
            vat: {
              percentage: "21.00",
            },
          },
        ],
      },
    }),
  }
);
javascript

You can also easily test the API with our interactive API docs.

Verifying Recipients

Before sending documents, you can verify if the recipient is registered in the Peppol network using the verify endpoint. This will be done automatically as well when you send a document.

const response = await fetch("https://peppol.recommand.eu/api/peppol/verify", {
  method: "POST",
  headers: {
    Authorization:
      "Basic " + Buffer.from("key_xxx:secret_xxx").toString("base64"),
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    peppolAddress: "0208:987654321",
  }),
});

console.log(await response.json());
javascript

For valid recipients, you should get a response like this:

{
  "success": true,
  "isValid": true,
  "smpUrl": "http://B-c6451d3d5bd755bb0c2576c41fea37fb.iso6523-actorid-upis.edelivery.tech.ec.europa.eu/iso6523-actorid-upis::0208%3A1012081766"
}
json

Retrieving Documents

Access received documents through the inbox endpoint:

const response = await fetch(
  "https://peppol.recommand.eu/api/peppol/{teamId}/inbox",
  {
    headers: {
      Authorization:
        "Basic " + Buffer.from("key_xxx:secret_xxx").toString("base64"),
    },
  }
);
javascript

When having processed a document in your system, you can mark it as read using the markAsRead endpoint. This will prevent it from being shown in the inbox again.

Setting Up Webhooks

Webhooks notify your system about new documents using the webhooks endpoint. Webhooks can also be created and managed from the dashboard.

const response = await fetch(
  "https://peppol.recommand.eu/api/peppol/{teamId}/webhooks",
  {
    method: "POST",
    headers: {
      Authorization:
        "Basic " + Buffer.from("key_xxx:secret_xxx").toString("base64"),
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      url: "https://your-service.com/peppol-webhook", // Try this out for real with a service like RequestBin (https://requestbin.whapi.cloud)
      companyId: "company_123", // Optional: specific company
    }),
  }
);
javascript

When a document is received, Recommand will send a POST request to your webhook URL with document details. This request will look something like this:

{
  "eventType": "document.received",
  "id": "doc_xxx",
  "teamId": "team_xxx",
  "companyId": "c_xxx"
}
json

From here, you can use the id to retrieve the document details using the getDocument endpoint.

Error Handling

The API returns structured error responses:

{
  "success": false,
  "errors": {
    "invoiceNumber": ["Invoice number is required"],
    "buyer.vatNumber": ["Invalid VAT number format"]
  }
}
json

Before processing results, always check the HTTP status code or the success property.

Complete Invoice Example

Here's a complete invoice example with all supported fields. You can view it in more detail in our interactive API docs.

const invoice = {
  invoiceNumber: "INV-2025-001",
  issueDate: "2024-05-15",
  dueDate: "2024-06-15",
  note: "Thank you for your business",
  buyerReference: "PO-2024-001",

  seller: {
    vatNumber: "BE0123456789",
    name: "Your Company",
    street: "Your Street 1",
    city: "Brussels",
    postalZone: "1000",
    country: "BE",
  },

  buyer: {
    vatNumber: "BE0987654321",
    name: "Customer Company",
    street: "Customer Street 1",
    city: "Antwerp",
    postalZone: "2000",
    country: "BE",
  },

  paymentMeans: [
    {
      paymentMethod: "credit_transfer",
      reference: "INV-2025-001",
      iban: "BE1234567890",
    },
  ],

  paymentTerms: {
    note: "Net 30",
  },

  lines: [
    {
      name: "Consulting Services",
      description: "Professional consulting services",
      sellersId: "CS-001",
      quantity: "10.00",
      unitCode: "HUR", // Hours
      netPriceAmount: "100.00",
      netAmount: "1000.00",
      vat: {
        category: "S",
        percentage: "21.00",
      },
    },
  ],

  attachments: [
    {
      id: "ATT-001",
      documentType: "130",
      mimeCode: "application/pdf",
      filename: "contract.pdf",
      embeddedDocument: "base64encodeddocument...",
    },
  ],
};
javascript

Next Steps

Now that you've sent your first document, explore these resources:

Support

If you encounter any issues or have questions, our support team is here to help: