> ## Documentation Index
> Fetch the complete documentation index at: https://docs.e-invoice.be/llms.txt
> Use this file to discover all available pages before exploring further.

# Look up Peppol participants

> Find the Peppol ID of a customer and make sure that the customer can receive invoices before you send.

A receiver must have a [Peppol ID](/glossary#peppol-id) and a registration with an Access Point before you can send a document on the Peppol network. The API has three operations to find and check a receiver. All three work without an API key: the `Authorization` header is not necessary.

## Compare the operations

| | `GET /api/validate/peppol-id` | `GET /api/lookup` | `GET /api/lookup/participants` |
| - | - | - | - |
| **Purpose** | Yes-or-no check before you send | Full technical record of a participant | Search by name |
| **Input** | Exact Peppol ID | Exact Peppol ID | Name or part of an identifier |
| **Data source** | SML and [SMP](/glossary#smp) | SML and SMP | [Peppol Directory](/glossary#peppol-directory) |
| **Result** | `is_valid` and the supported document types | DNS data, endpoints, certificates, [business card](/glossary#business-card) | List of possible participants |
| **Complete** | Yes, for registered participants | Yes, for registered participants | No |

The two operations with an exact Peppol ID use the same lookup and the same cache. The API keeps each result for 15 minutes.

<Warning>
  The Peppol Directory can be incomplete. Publication in the directory is optional for an Access Point, so a registered participant can be absent from the search results. An empty search result does not show that a company is not on Peppol. Use the directory search to find candidates only, then check the Peppol ID.
</Warning>

## Peppol ID schemes

A Peppol ID has the format `scheme:identifier`, for example `0208:1018265814`. The [scheme](/glossary#scheme) is a four-digit code that tells which register issued the identifier.

| Country | ISO code | Scheme | Identifier type | Example |
| - | - | - | - | - |
| Belgium | BE | `0208` | Enterprise number (CBE/KBO), 10 digits. This is the VAT number without `BE`. | `0208:1018265814` |
| Netherlands | NL | `0106` | KVK number | `0106:12345678` |
| Netherlands | NL | `9944` | VAT number | `9944:NL123456789B01` |
| Luxembourg | LU | `9938` | VAT number | `9938:LU12345678` |
| France | FR | `0009` | SIRET number | `0009:12345678901234` |
| Germany | DE | `0204` | Leitweg-ID (public sector) | `0204:991-12345-67` |
| Germany | DE | `9930` | VAT number | `9930:DE123456789` |
| Italy | IT | `0211` | VAT number (Partita IVA) | `0211:IT12345678901` |
| United Kingdom | GB | `9932` | VAT number | `9932:GB123456789` |
| Denmark | DK | `0184` | CVR number | `0184:12345678` |
| Norway | NO | `0192` | Organisation number | `0192:123456789` |
| Sweden | SE | `0007` | Organisation number | `0007:1234567890` |
| Finland | FI | `0216` | OVT code | `0216:003712345678` |
| International | - | `0088` | Global Location Number (GLN) | `0088:1234567890123` |
| International | - | `0060` | D-U-N-S number | `0060:123456789` |

This table shows the most frequent schemes of the Peppol participant identifier scheme code list, version 9.1. This is the version that the API uses. For all schemes, see the [Peppol code lists](https://docs.peppol.eu/edelivery/codelists/).

For a Belgian company, the identifier is the [enterprise number](/glossary#cbe-and-kbo): the [VAT number](/glossary#vat-number) without `BE`. The VAT number `BE1018265814` gives the Peppol ID `0208:1018265814`.

## Pre-send check

Before you send a document, call `GET /api/validate/peppol-id` with the Peppol ID of the receiver. Send only if `is_valid` is `true` and `supported_document_types` contains the document type that you send. This is the only place where this rule is written; other pages link to it.

<ParamField query="peppol_id" type="string" required>
  Peppol ID in the format `scheme:identifier`, for example `0208:1018265814`.
</ParamField>

<CodeGroup>
  ```bash cURL theme={null}
  curl "https://api.e-invoice.be/api/validate/peppol-id?peppol_id=0208:1018265814"
  ```

  ```javascript Node.js theme={null}
  const params = new URLSearchParams({ peppol_id: '0208:1018265814' });
  const response = await fetch(`https://api.e-invoice.be/api/validate/peppol-id?${params}`);
  const result = await response.json();
  console.log(result.is_valid, result.supported_document_types);
  ```

  ```python Python theme={null}
  import requests

  url = "https://api.e-invoice.be/api/validate/peppol-id"
  result = requests.get(url, params={"peppol_id": "0208:1018265814"}).json()
  print(result["is_valid"], result["supported_document_types"])
  ```

  ```php PHP theme={null}
  <?php
  $url = 'https://api.e-invoice.be/api/validate/peppol-id?' . http_build_query(['peppol_id' => '0208:1018265814']);
  $ch = curl_init($url);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  $result = json_decode(curl_exec($ch), true);
  var_dump($result['is_valid']);
  ```

  ```csharp C# theme={null}
  using var client = new HttpClient();
  var json = await client.GetStringAsync("https://api.e-invoice.be/api/validate/peppol-id?peppol_id=" + Uri.EscapeDataString("0208:1018265814"));
  Console.WriteLine(json);
  ```
</CodeGroup>

```json theme={null}
{
  "is_valid": true,
  "dns_valid": true,
  "business_card_valid": true,
  "supported_document_types": [
    "urn:oasis:names:specification:ubl:schema:xsd:Invoice-2::Invoice##urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0::2.1",
    "urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2::CreditNote##urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0::2.1"
  ],
  "business_card": { "name": "E-INVOICE BV", "country_code": "BE", "registration_date": "2025-01-15" }
}
```

<ResponseField name="dns_valid" type="boolean">
  The SML has a DNS record for the Peppol ID, so an SMP knows the participant.
</ResponseField>

<ResponseField name="business_card_valid" type="boolean">
  The SMP publishes a business card with a minimum of one entity.
</ResponseField>

<ResponseField name="is_valid" type="boolean">
  `true` if `dns_valid` or `business_card_valid` is `true`. It does not tell which document types the receiver accepts; read `supported_document_types` for that.
</ResponseField>

<ResponseField name="business_card" type="object">
  `null` if there is no business card.
</ResponseField>

For a Peppol ID that is not registered, the status is `200` and `is_valid` is `false`.

## Get the full record of a participant

`GET /api/lookup` returns the complete result of the SML and SMP lookup. Use it when you need the endpoints, the transport profile or the certificates of a receiver, for example to find the cause of a failed send. It has the same `peppol_id` query parameter as the pre-send check.

<CodeGroup>
  ```bash cURL theme={null}
  curl "https://api.e-invoice.be/api/lookup?peppol_id=0208:1018265814"
  ```

  ```javascript Node.js theme={null}
  const params = new URLSearchParams({ peppol_id: '0208:1018265814' });
  const response = await fetch(`https://api.e-invoice.be/api/lookup?${params}`);
  const record = await response.json();
  console.log(record.dnsInfo.status, record.businessCard.entities[0]?.name);
  ```

  ```python Python theme={null}
  import requests

  url = "https://api.e-invoice.be/api/lookup"
  record = requests.get(url, params={"peppol_id": "0208:1018265814"}).json()
  print(record["dnsInfo"]["status"])
  ```

  ```php PHP theme={null}
  <?php
  $url = 'https://api.e-invoice.be/api/lookup?' . http_build_query(['peppol_id' => '0208:1018265814']);
  $ch = curl_init($url);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  $record = json_decode(curl_exec($ch), true);
  echo $record['dnsInfo']['status'];
  ```

  ```csharp C# theme={null}
  using var client = new HttpClient();
  var json = await client.GetStringAsync("https://api.e-invoice.be/api/lookup?peppol_id=" + Uri.EscapeDataString("0208:1018265814"));
  Console.WriteLine(json);
  ```
</CodeGroup>

```json theme={null}
{
  "queryMetadata": {
    "identifierScheme": "iso6523-actorid-upis",
    "identifierValue": "0208:1018265814",
    "smlDomain": "participant.sml.prod.tech.peppol.org",
    "timestamp": "2026-01-12T14:32:10.123456",
    "version": "1.0.0"
  },
  "status": "success",
  "errors": [],
  "dnsInfo": {
    "status": "success",
    "smpHostname": "smp.e-invoice.be",
    "smlHostname": "participant.sml.prod.tech.peppol.org",
    "dnsRecords": [{ "ip": "193.140.151.175" }]
  },
  "serviceMetadata": {
    "status": "success",
    "queryTimeMs": 210.4,
    "endpoints": [
      {
        "status": "success",
        "url": "https://smp.e-invoice.be/iso6523-actorid-upis::0208:1018265814/services/busdox-docid-qns::urn:oasis:names:specification:ubl:schema:xsd:Invoice-2::Invoice%23%23urn:cen.eu:en16931:2017%23compliant%23urn:fdc:peppol.eu:2017:poacc:billing:3.0::2.1",
        "documentTypes": [
          { "scheme": "busdox-docid-qns", "value": "urn:oasis:names:specification:ubl:schema:xsd:Invoice-2::Invoice##urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0::2.1" }
        ]
      }
    ]
  },
  "businessCard": {
    "status": "success",
    "entities": [{ "name": "E-INVOICE BV", "countryCode": "BE" }],
    "queryTimeMs": 123.45
  },
  "certificates": [],
  "executionTimeMs": 895
}
```

This example is shortened: it shows one endpoint, and no `processes` and `certificates` content. The status is `200` also for a Peppol ID that is not registered. In that case `dnsInfo.status` is `error`. This response has no single field that says "registered"; the pre-send check gives that answer.

## Search the Peppol Directory

`GET /api/lookup/participants` searches the public [Peppol Directory](https://directory.peppol.eu). Use it when you know the name of a company but not its Peppol ID, for example for a search field in your application.

<ParamField query="query" type="string" required>
  Search text: a company name or a part of an identifier.
</ParamField>

<ParamField query="country_code" type="string">
  ISO 3166-1 alpha-2 country code, for example `BE`. Without this parameter the search is global.
</ParamField>

<CodeGroup>
  ```bash cURL theme={null}
  curl "https://api.e-invoice.be/api/lookup/participants?query=E-INVOICE&country_code=BE"
  ```

  ```javascript Node.js theme={null}
  const params = new URLSearchParams({ query: 'E-INVOICE', country_code: 'BE' });
  const response = await fetch(`https://api.e-invoice.be/api/lookup/participants?${params}`);
  const { participants } = await response.json();
  console.log(participants.map((p) => `${p.peppol_scheme}:${p.peppol_id}`));
  ```

  ```python Python theme={null}
  import requests

  url = "https://api.e-invoice.be/api/lookup/participants"
  result = requests.get(url, params={"query": "E-INVOICE", "country_code": "BE"}).json()
  for p in result["participants"]:
      print(f'{p["peppol_scheme"]}:{p["peppol_id"]}')
  ```

  ```php PHP theme={null}
  <?php
  $url = 'https://api.e-invoice.be/api/lookup/participants?' . http_build_query(['query' => 'E-INVOICE', 'country_code' => 'BE']);
  $ch = curl_init($url);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  $result = json_decode(curl_exec($ch), true);
  echo count($result['participants']);
  ```

  ```csharp C# theme={null}
  using var client = new HttpClient();
  var json = await client.GetStringAsync("https://api.e-invoice.be/api/lookup/participants?query=" + Uri.EscapeDataString("E-INVOICE") + "&country_code=BE");
  Console.WriteLine(json);
  ```
</CodeGroup>

```json theme={null}
{
  "total_count": 1,
  "used_count": 1,
  "query_terms": "E-INVOICE",
  "search_date": "2026-01-12T14:32:10.123456",
  "participants": [
    {
      "peppol_id": "1018265814",
      "peppol_scheme": "0208",
      "entities": [
        { "name": "E-INVOICE BV", "country_code": "BE", "registration_date": "2025-01-15", "identifiers": [] }
      ],
      "document_types": []
    }
  ]
}
```

Join `peppol_scheme` and `peppol_id` with a colon to get the Peppol ID for the pre-send check.

## Find and check a receiver

This pattern combines the operations. If you know the identifier, it checks the Peppol ID directly. If you know only the name, it searches the directory and checks each candidate.

<Accordion title="Complete example (Node.js and Python)">
  <CodeGroup>
    ```javascript Node.js theme={null}
    const BASE = 'https://api.e-invoice.be';
    const INVOICE = 'urn:oasis:names:specification:ubl:schema:xsd:Invoice-2::Invoice##';

    async function get(path, params) {
      const response = await fetch(`${BASE}${path}?${new URLSearchParams(params)}`);
      if (!response.ok) throw new Error(`${path} returned ${response.status}`);
      return response.json();
    }

    // Pre-send check: registered and able to receive invoices.
    async function checkReceiver(peppolId) {
      const result = await get('/api/validate/peppol-id', { peppol_id: peppolId });
      const canReceive = result.is_valid && result.supported_document_types.some((t) => t.startsWith(INVOICE));
      return { peppolId, canReceive, name: result.business_card?.name ?? null };
    }

    // Directory search: candidates only.
    async function searchDirectory(name, countryCode) {
      const result = await get('/api/lookup/participants', { query: name, country_code: countryCode });
      return (result.participants ?? []).map((p) => `${p.peppol_scheme}:${p.peppol_id}`);
    }

    async function findReceiver({ peppolId, name, countryCode = 'BE' }) {
      const candidates = peppolId ? [peppolId] : await searchDirectory(name, countryCode);
      const checked = await Promise.all(candidates.map(checkReceiver));
      return checked.filter((c) => c.canReceive);
    }

    console.log(await findReceiver({ peppolId: '0208:0848934496' }));
    console.log(await findReceiver({ name: 'OpenPeppol' }));
    ```

    ```python Python theme={null}
    import requests

    BASE = "https://api.e-invoice.be"
    INVOICE = "urn:oasis:names:specification:ubl:schema:xsd:Invoice-2::Invoice##"

    def get(path, params):
        response = requests.get(f"{BASE}{path}", params=params, timeout=30)
        response.raise_for_status()
        return response.json()

    def check_receiver(peppol_id):
        """Pre-send check: registered and able to receive invoices."""
        result = get("/api/validate/peppol-id", {"peppol_id": peppol_id})
        types = result["supported_document_types"]
        can_receive = result["is_valid"] and any(t.startswith(INVOICE) for t in types)
        card = result.get("business_card") or {}
        return {"peppol_id": peppol_id, "can_receive": can_receive, "name": card.get("name")}

    def search_directory(name, country_code):
        """Directory search: candidates only."""
        result = get("/api/lookup/participants", {"query": name, "country_code": country_code})
        return [f'{p["peppol_scheme"]}:{p["peppol_id"]}' for p in result.get("participants") or []]

    def find_receiver(peppol_id=None, name=None, country_code="BE"):
        candidates = [peppol_id] if peppol_id else search_directory(name, country_code)
        return [c for c in map(check_receiver, candidates) if c["can_receive"]]

    print(find_receiver(peppol_id="0208:0848934496"))
    print(find_receiver(name="OpenPeppol"))
    ```
  </CodeGroup>
</Accordion>

If the list is empty after a search by name, ask the customer for the enterprise number or VAT number and check the Peppol ID directly. If the direct check also gives `is_valid: false`, the customer cannot receive documents on Peppol; use a different delivery method.

## Integration checklist

* Do the [pre-send check](#pre-send-check) for each new receiver.
* Use the directory search only to find candidates, and check each candidate.
* Do not use an empty directory result as proof that a company is not registered.
* Show the user a clear message when the search finds nothing, and ask for the identifier.
* Keep a positive result in your own cache for a limited time; a receiver can change its Access Point or stop its registration.
* Set the Peppol IDs explicitly when you send. See [Create e-invoices](/guides/creating-invoices).
* For the status codes and the error body of these operations, see [Errors and troubleshooting](/guides/errors).

## Next Steps

<CardGroup cols={2}>
  <Card title="Create e-invoices" icon="file-invoice" href="/guides/creating-invoices">
    Create a document and send it to the receiver that you checked.
  </Card>

  <Card title="Document lifecycle and delivery tracking" icon="arrows-rotate" href="/guides/document-lifecycle">
    Follow the state of a document after the send.
  </Card>

  <Card title="Errors and troubleshooting" icon="triangle-exclamation" href="/guides/errors">
    Find the cause of a failed send.
  </Card>

  <Card title="Glossary" icon="book" href="/glossary">
    Read the definitions of the Peppol terms.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.