> ## 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.

# Looking Up Peppol Participants

> Find and verify customer Peppol IDs before sending invoices

## Overview

Before sending an e-invoice via Peppol, you need to know your customer's **Peppol ID** and verify they can receive invoices. The e-invoice.be API provides two distinct lookup methods, each with different data sources and use cases:

1. **`/api/lookup`** - Direct SMP lookup (real-time, always accurate)
2. **`/api/lookup/participants`** - Peppol Directory search (broader search, may be incomplete)

Understanding the difference between these endpoints is crucial for reliable participant verification.

## What is a Peppol ID?

A Peppol ID is a unique identifier for organizations registered on the Peppol network. It consists of:

* **Scheme**: The identifier scheme (e.g., `0208` for Belgian companies)
* **Identifier**: The actual ID

Format: `scheme:identifier` → `0208:0123456789`

<Note>
  **Belgian Peppol IDs** use the CBE number (Crossroads Bank for Enterprises number), which is equivalent to the Belgian VAT number **without the 'BE' prefix**. For example, if the VAT number is BE0123456789, the Peppol ID is `0208:0123456789` (numbers only).
</Note>

<Note>
  Organizations must be registered with a Peppol Access Point to receive e-invoices. If a customer is not registered, they cannot receive invoices via Peppol.
</Note>

## `/api/lookup` - Direct SMP Lookup

### What It Does

Performs an **exact, real-time lookup** directly against the Service Metadata Publisher (SMP) using a specific identifier. This queries the authoritative source for participant registration information.

### Key Characteristics

* **Always accurate**: Queries the SMP in real-time (a-la-minute)
* **Exact match required**: Requires precise Peppol ID
* **Authoritative data**: Returns the current registration status directly from the SMP
* **100% reliable**: Always shows registered participants

### When to Use

* **Before sending invoices**: Verify a recipient can receive documents
* **Exact identifier known**: You have the CBE number or Peppol ID
* **Need certainty**: Must confirm current registration status
* **Production validation**: Pre-flight checks before document transmission

### Request

The endpoint requires a Peppol ID in the format `<scheme>:<id>`. For Belgian companies, use scheme `0208` followed by the 10-digit CBE/BTW number.

```bash theme={null}
curl -X GET "https://api.e-invoice.be/api/lookup?peppol_id=0208:1018265814" \
     -H "Authorization: Bearer YOUR_API_KEY"
```

### Response

**Registered participant** (`PeppolIdLookupResponse`, abbreviated):

```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": { "...": "supported document types and processes" },
  "businessCard": {
    "status": "success",
    "entities": [
      { "name": "E-INVOICE BV", "countryCode": "BE" }
    ],
    "queryTimeMs": 123.45
  },
  "certificates": [],
  "executionTimeMs": 895.32
}
```

<Note>
  To decide whether a participant can receive invoices, inspect `dnsInfo.status` (DNS resolves to an SMP), whether a `businessCard` is present, and the entries under `serviceMetadata`. There is no single top-level `registered` boolean — use the [`/api/validate/peppol-id`](/guides/validation) endpoint if you want a consolidated `is_valid` result.
</Note>

## `/api/lookup/participants` - Peppol Directory Search

### What It Does

Searches the **official Peppol Directory** database, which contains participant information that access points have voluntarily published. This is a proxy for the [public Peppol Directory](https://directory.peppol.eu).

### Key Characteristics

* **Search functionality**: Find participants by name or partial identifier
* **Directory-based**: Only shows participants whose access points publish to the directory
* **May be incomplete**: Not all registered participants appear in the directory
* **Discovery tool**: Useful for finding participants when exact ID is unknown

### Important Limitation

<Warning>
  **Not all registered Peppol participants appear in the Directory**. Publishing to the Peppol Directory is optional, not mandatory. An access point may choose not to synchronize participant data with the directory, even though those participants are fully registered and can receive invoices.
</Warning>

### When to Use

* **Discovery**: Search for participants by company name
* **Browsing**: Explore registered participants in a country
* **Fuzzy search**: Find participants without knowing exact identifiers
* **Autocomplete features**: Suggest participants as users type

### When NOT to Use

* **Validation before sending**: Use `/api/lookup` instead for accurate verification
* **Confirming registration**: Directory absence doesn't mean unregistered
* **Production checks**: Not reliable for pre-send validation

### Request

```bash theme={null}
curl -X GET "https://api.e-invoice.be/api/lookup/participants?query=E-INVOICE&country_code=BE" \
     -H "Authorization: Bearer YOUR_API_KEY"
```

### Query Parameters

| Parameter      | Required | Description                            | Example     |
| -------------- | -------- | -------------------------------------- | ----------- |
| `query`        | Yes      | Search term (name or identifier)       | `E-INVOICE` |
| `country_code` | No       | Filter by country (ISO 3166-1 alpha-2) | `BE`        |

### Response

```json theme={null}
{
  "total_count": 2,
  "used_count": 2,
  "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": "2023-05-14"
        }
      ],
      "document_types": []
    },
    {
      "peppol_id": "0123456789",
      "peppol_scheme": "0208",
      "entities": [
        {
          "name": "Example Company BVBA",
          "country_code": "BE",
          "registration_date": "2022-11-02"
        }
      ],
      "document_types": []
    }
  ]
}
```

## Comparison

| Feature            | `/api/lookup`                        | `/api/lookup/participants`       |
| ------------------ | ------------------------------------ | -------------------------------- |
| **Data source**    | SMP (real-time)                      | Peppol Directory (database)      |
| **Accuracy**       | 100% accurate                        | May be incomplete                |
| **Query type**     | Exact match                          | Fuzzy search                     |
| **Use case**       | Validation                           | Discovery                        |
| **Speed**          | Fast (single lookup)                 | Moderate (database search)       |
| **Reliability**    | Always shows registered participants | May miss registered participants |
| **Required input** | Exact Peppol ID                      | Partial name or identifier       |

## Common Peppol ID Schemes

Different countries use different identifier schemes:

| Country       | Scheme Code | Identifier Type               | Format Example        |
| ------------- | ----------- | ----------------------------- | --------------------- |
| Belgium       | `0208`      | CBE number (VAT without 'BE') | `0208:0123456789`     |
| Netherlands   | `0106`      | KVK number                    | `0106:12345678`       |
| Germany       | `0204`      | VAT number                    | `0204:DE123456789`    |
| France        | `0009`      | SIRET                         | `0009:12345678901234` |
| UK            | `0088`      | GLN                           | `0088:1234567890123`  |
| Norway        | `0192`      | Organization number           | `0192:123456789`      |
| Sweden        | `0007`      | Organization number           | `0007:1234567890`     |
| Denmark       | `0184`      | CVR number                    | `0184:12345678`       |
| International | `0088`      | Global Location Number        | `0088:1234567890123`  |

<Tip>
  For a complete list of Peppol identifier schemes, see the [official Peppol code list](https://docs.peppol.eu/edelivery/codelists/v9.5/Peppol%20Code%20Lists%20-%20Participant%20identifier%20schemes%20v9.5.html).
</Tip>

## Integration Examples

### Direct Lookup by CBE Number

```javascript theme={null}
const axios = require('axios');

const api = axios.create({
  baseURL: 'https://api.e-invoice.be',
  headers: {
    'Authorization': `Bearer ${process.env.E_INVOICE_API_KEY}`,
    'Content-Type': 'application/json'
  }
});

async function lookupCustomer(cbeNumber) {
  try {
    // Construct Peppol ID (Belgian example)
    // Note: For Belgium, use CBE number without 'BE' prefix
    // If you have VAT number BE0123456789, use just 0123456789
    const peppolId = `0208:${cbeNumber}`;

    const response = await api.get('/api/lookup', {
      params: { peppol_id: peppolId }
    });

    if (response.data.dnsInfo?.status === 'success') {
      console.log('✓ Customer can receive e-invoices via Peppol');
      console.log('  Name:', response.data.businessCard?.entities?.[0]?.name);
      console.log('  Peppol ID:', peppolId);
      return response.data;
    }

    // /api/lookup returns HTTP 200 even when not registered
    console.log('✗ Customer is not registered on Peppol network');
    console.log('  They cannot receive e-invoices electronically');
    return null;

  } catch (error) {
    console.error('Error looking up customer:', error.response?.data || error.message);
    return null;
  }
}

// Example usage
await lookupCustomer('1018265814');  // CBE number without 'BE' prefix
```

### Discovery + Validation Pattern

Combine both endpoints for the best user experience:

```javascript theme={null}
async function findAndValidateCustomer(companyName, cbeNumber = null) {

  // Step 1: If we have exact CBE number, use direct lookup
  if (cbeNumber) {
    console.log(`Looking up CBE ${cbeNumber} directly...`);
    try {
      const peppolId = `0208:${cbeNumber}`;
      const response = await api.get('/api/lookup', {
        params: { peppol_id: peppolId }
      });

      if (response.data.dnsInfo?.status === 'success') {
        console.log('✓ Found via direct SMP lookup');
        console.log('  Name:', response.data.businessCard?.entities?.[0]?.name);
        return response.data;
      }
      // /api/lookup returns HTTP 200 even when not registered
      console.log('✗ Not registered on Peppol network');
      return null;
    } catch (error) {
      console.error('Direct lookup failed:', error.message);
      return null;
    }
  }

  // Step 2: Search directory by name
  console.log(`Searching directory for "${companyName}"...`);
  try {
    const searchResponse = await api.get('/api/lookup/participants', {
      params: {
        query: companyName,
        country_code: 'BE'
      }
    });

    if (!searchResponse.data.participants || searchResponse.data.participants.length === 0) {
      console.log('⚠ No results in Peppol Directory');
      console.log('  Note: This does not mean the company is unregistered.');
      console.log('  Some access points do not publish to the directory.');
      console.log('  If you have the CBE number, use direct lookup instead.');
      return null;
    }

    console.log(`Found ${searchResponse.data.participants.length} participant(s) in directory:`);
    searchResponse.data.participants.forEach((p, i) => {
      const peppolId = `${p.peppol_scheme}:${p.peppol_id}`;
      const name = p.entities[0]?.name;
      console.log(`  ${i + 1}. ${name} (${peppolId})`);
    });

    // Step 3: Validate the first result via direct lookup
    const firstResult = searchResponse.data.participants[0];
    const firstPeppolId = `${firstResult.peppol_scheme}:${firstResult.peppol_id}`;
    console.log(`\nValidating ${firstResult.entities[0]?.name} via direct SMP lookup...`);

    const validateResponse = await api.get('/api/lookup', {
      params: { peppol_id: firstPeppolId }
    });

    if (validateResponse.data.dnsInfo?.status === 'success') {
      console.log('✓ Confirmed registration via SMP');
      return validateResponse.data;
    }

  } catch (error) {
    console.error('Search failed:', error.message);
    return null;
  }
}

// Example usage

// With exact CBE number (preferred)
await findAndValidateCustomer('E-INVOICE', '1018265814');

// With company name only (discovery)
await findAndValidateCustomer('E-INVOICE');
```

### Pre-Flight Check Workflow

Before creating an invoice, validate the recipient:

```javascript theme={null}
async function createInvoiceWithValidation(invoiceData) {
  try {
    // 1. Validate recipient is on Peppol network
    const customerPeppolId = invoiceData.customer.party_legal_entity.company_id;

    console.log('Checking if customer can receive e-invoices...');
    const validation = await api.get('/api/lookup', {
      params: { peppol_id: customerPeppolId }
    });

    if (validation.data.dnsInfo?.status !== 'success') {
      console.error('❌ Customer cannot receive e-invoices via Peppol');
      console.error('   Peppol ID:', customerPeppolId);
      console.error('   Please use alternative delivery method (email, PDF)');
      return null;
    }

    console.log('✓ Customer can receive e-invoices');

    // 2. Validate invoice JSON
    console.log('Validating invoice JSON...');
    const jsonValidation = await api.post('/api/validate/json', invoiceData);

    if (!jsonValidation.data.is_valid) {
      console.error('❌ Invoice validation failed');
      jsonValidation.data.issues.forEach(issue => {
        console.error(`   - ${issue.rule_id}: ${issue.message}`);
      });
      return null;
    }

    console.log('✓ Invoice JSON is valid');

    // 3. Create the invoice
    console.log('Creating invoice...');
    const document = await api.post('/api/documents/', invoiceData);
    console.log('✓ Invoice created:', document.data.id);

    // 4. Send via Peppol
    console.log('Sending via Peppol...');
    const result = await api.post(`/api/documents/${document.data.id}/send`);
    console.log('✓ Invoice sent:', result.data.state);

    return document.data;

  } catch (error) {
    console.error('Error:', error.response?.data || error.message);
    return null;
  }
}
```

## Handling Unregistered Customers

If a customer is not on the Peppol network:

1. **Inform them**: Let them know about Peppol e-invoicing benefits
2. **Alternative delivery**: Send PDF invoices via email
3. **Register with e-invoice.be**: Customers can sign up at [e-invoice.be](https://app.e-invoice.be)

### Example: Fallback Logic

```javascript theme={null}
async function sendInvoiceToCustomer(invoiceData) {
  const peppolId = invoiceData.customer.party_legal_entity.company_id;

  try {
    // Check if customer is on Peppol
    const validation = await api.get('/api/lookup', {
      params: { peppol_id: peppolId }
    });

    if (validation.data.dnsInfo?.status === 'success') {
      // Send via Peppol
      return await sendViaPeppol(invoiceData);
    }
  } catch (error) {
    // Fallback to email with PDF
    console.log('Customer not on Peppol, sending PDF via email');
    return await sendPdfViaEmail(invoiceData);
  }
}
```

## Best Practices

<AccordionGroup>
  <Accordion title="Always Use Direct Lookup for Validation">
    Before sending invoices, use `/api/lookup` with the exact Peppol ID:

    ```javascript theme={null}
    // ✓ Correct: Direct lookup for validation
    const peppolId = `0208:${cbeNumber}`;
    try {
      const validation = await api.get('/api/lookup', {
        params: { peppol_id: peppolId }
      });

      if (validation.data.dnsInfo?.status === 'success') {
        await createAndSendInvoice(invoiceData);
      }
    } catch (error) {
      // Participant not found
      console.log('Customer not registered on Peppol');
    }

    // ✗ Wrong: Using directory search for validation
    const search = await api.get('/api/lookup/participants', {
      params: { query: cbeNumber }
    });
    // May return no results even if registered!
    ```
  </Accordion>

  <Accordion title="Use Directory Search for Discovery Only">
    The Peppol Directory is excellent for discovery, not validation:

    ```javascript theme={null}
    // Use case: User types company name in autocomplete
    async function autocompleteSearch(userInput) {
      const results = await api.get('/api/lookup/participants', {
        params: {
          query: userInput,
          country_code: 'BE'
        }
      });

      // Display results as suggestions
      return results.data.participants || [];
    }

    // When user selects a result, validate via direct lookup
    async function onCustomerSelected(identifier) {
      const peppolId = `${identifier.scheme}:${identifier.value}`;

      try {
        const participant = await api.get('/api/lookup', {
          params: { peppol_id: peppolId }
        });
        return participant.data;
      } catch (error) {
        return null;
      }
    }
    ```
  </Accordion>

  <Accordion title="Cache Lookup Results">
    Cache participant lookups to reduce API calls:

    ```javascript theme={null}
    const participantCache = new Map();
    const CACHE_TTL = 24 * 60 * 60 * 1000; // 24 hours

    async function lookupWithCache(peppolId) {
      const cached = participantCache.get(peppolId);
      if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
        return cached.data;
      }

      try {
        const response = await api.get('/api/lookup', {
          params: { peppol_id: peppolId }
        });

        const data = response.data;
        participantCache.set(peppolId, {
          data,
          timestamp: Date.now()
        });

        return data;
      } catch (error) {
        return null;
      }
    }
    ```
  </Accordion>

  <Accordion title="Handle Scheme Variations">
    Different countries use different schemes. Build a mapping:

    ```javascript theme={null}
    const PEPPOL_SCHEMES = {
      'BE': '0208',  // Belgian CBE (numbers only, no 'BE' prefix)
      'NL': '0106',  // Dutch KVK
      'DE': '0204',  // German VAT
      'FR': '0009',  // French SIRET
      'UK': '0088',  // UK GLN
    };

    function buildPeppolId(countryCode, identifier) {
      const scheme = PEPPOL_SCHEMES[countryCode];
      if (!scheme) {
        throw new Error(`Unknown country: ${countryCode}`);
      }
      return `${scheme}:${identifier}`;
    }
    ```
  </Accordion>

  <Accordion title="Provide Clear User Feedback">
    Explain why directory search may not find participants:

    ```javascript theme={null}
    const searchResults = await api.get('/api/lookup/participants', {
      params: { query: companyName }
    });

    if (!searchResults.data.participants || searchResults.data.participants.length === 0) {
      console.log(`
        No results found in Peppol Directory for "${companyName}".

        This does not necessarily mean they are unregistered.
        Some Peppol access points do not publish participant data
        to the directory, even though those participants can receive
        invoices.

        If you have the customer's CBE number or Peppol ID, use that
        for a direct lookup instead.
      `);
    }
    ```
  </Accordion>

  <Accordion title="Handle Missing Directory Entries Gracefully">
    Don't assume a participant is unregistered if not in directory:

    ```javascript theme={null}
    async function lookupCustomer(cbeNumber) {
      const peppolId = `0208:${cbeNumber}`;

      try {
        // Try direct lookup
        const response = await api.get('/api/lookup', {
          params: { peppol_id: peppolId }
        });

        if (response.data.dnsInfo?.status === 'success') {
          return {
            found: true,
            data: response.data,
            source: 'SMP'
          };
        }
      } catch (error) {
        // Not registered or error
        return {
          found: false,
          message: 'Customer not registered on Peppol network',
          suggestion: 'Ask customer to register at https://app.e-invoice.be'
        };
      }
    }
    ```
  </Accordion>
</AccordionGroup>

## Real-World Scenario

### Directory vs. SMP Lookup

Some Peppol access points register participants but do not publish all participant data to the Peppol Directory.

**What happens**:

* A participant is fully registered with their access point
* They can send and receive invoices via Peppol
* Their registration is in the SMP (authoritative source)
* But their access point has not synchronized data with the directory

**Results**:

**Via `/api/lookup` (Direct SMP)** — `dnsInfo.status` is `"success"` and a `businessCard` is present:

```json theme={null}
{
  "status": "success",
  "dnsInfo": { "status": "success", "smpHostname": "smp.example.com" },
  "businessCard": {
    "status": "success",
    "entities": [{ "name": "Example Company BV", "countryCode": "BE" }]
  },
  "serviceMetadata": { "...": "supported document types" }
}
```

✓ **Found** - The participant is registered

**Via `/api/lookup/participants` (Peppol Directory)**:

```json theme={null}
{
  "total_count": 0,
  "used_count": 0,
  "query_terms": "Example Company BV",
  "search_date": "2026-01-12T14:32:10.123456",
  "participants": []
}
```

✗ **Not found** - Not in the directory database

### The Takeaway

**The participant is fully registered and can receive invoices**, but does not appear in directory searches. This demonstrates why `/api/lookup` must be used for validation before sending invoices.

## Technical Details

### Why the Difference Exists

* **SMP registration** is required for Peppol participation
* **Directory publication** is optional for access points
* Some access points prioritize privacy and don't publish participant lists
* Others may have technical reasons for not synchronizing with the directory

### Data Freshness

| Endpoint                   | Data Age  | Updates                                 |
| -------------------------- | --------- | --------------------------------------- |
| `/api/lookup`              | Real-time | Immediate (queries SMP directly)        |
| `/api/lookup/participants` | Cached    | Periodic (synced from Peppol Directory) |

## Integration Checklist

When implementing participant lookup in your application:

* [ ] Use `/api/lookup` for all validation before sending invoices
* [ ] Use `/api/lookup/participants` only for discovery and search features
* [ ] Never rely on directory search absence as proof of non-registration
* [ ] Always validate directory search results via direct SMP lookup
* [ ] Provide clear feedback when directory search returns no results
* [ ] Cache direct lookup results (with appropriate TTL)
* [ ] Handle cases where CBE number is known vs. only company name
* [ ] Test with known participants from different access points

## Next Steps

<CardGroup cols={2}>
  <Card title="Create Invoices" icon="file-invoice" href="/guides/creating-invoices">
    Create and send e-invoices
  </Card>

  <Card title="Validation Guide" icon="check-circle" href="/guides/validation">
    Test invoice JSON during development
  </Card>

  <Card title="Set Up Webhooks" icon="webhook" href="/essentials/webhooks">
    Get notified about delivery status
  </Card>

  <Card title="API Reference" icon="code" href="/api-reference">
    Explore all endpoints
  </Card>
</CardGroup>
