Legal Entities API
Provision, list, authorise, and off-board your customers as Peppol legal entities. The multi-tenant surface for platforms.
legal_entities:* scope. A standard key receives 403. Customers you don't own return 404, never another platform's data. Looking for your own account's identity instead? That is GET /v1/identity — it works with any API key and needs no platform activation.Getting a master key. In the sandbox, an organisation admin starts the platform sandbox trial from the console overview, then creates a sandbox master key from the console. Production platform access is set up with our team — email hello@getpeppr.dev to request it. Onboarding your own company needs none of this: that is done in the console, on the Peppol identity page.
Create a customer as a draft Peppol legal entity. getpeppr validates the identifier format (for example, a Luhn check), then kicks off registry verification asynchronously. Returns 202 on creation, or 200 if the same externalId is replayed with the same normalized Peppol participant (the call is idempotent, including scheme aliases and identifier case).
externalId returns 409 with code: "identifier_already_in_use" before any provider creation. When the owner is already committed, existingLegalEntity contains its getpeppr id and your externalId; no other account's data is ever exposed. An overlapping request can temporarily return 409 legal_entity_creation_in_progress; retry the same payload.202 while the identifier later settles as unsupported_scheme, verification_failed, or registration_failed if automatic verification is unavailable, the registry check fails, or the identifier is already registered elsewhere or not attachable on the test network. Poll GET /v1/legal-entities/:id or subscribe to legal_entity.* webhooks before treating the customer as send-ready.Body parameters
| Name | Type | Required | Description |
|---|---|---|---|
externalId | string | Required | Your stable reference for this customer (1–64 chars). Echoed on every response and webhook. |
companyName | string | Required | Legal company name (2–64 chars). Matched against the business registry. |
country | string | Required | ISO 3166-1 alpha-2 country code (e.g. GB). |
address.line1 | string | Required | Street address (2–192 chars). |
address.city | string | Required | City (2–64 chars). |
address.zip | string | Required | Postal / ZIP code (2–32 chars). |
identifier.scheme | string | Required | Peppol identifier scheme (1–16 chars, e.g. GB:VAT, 0208). |
identifier.value | string | Required | The identifier value (1–64 chars). Format-checked (e.g. Luhn) before verification. |
import { Peppol } from "@getpeppr/sdk";
const peppol = new Peppol({ apiKey: "sk_live_your_master_key" });
const customer = await peppol.legalEntities.create({
externalId: "customer_8412",
companyName: "Bright Health Ltd",
country: "GB",
address: { line1: "10 King Street", city: "London", zip: "EC2V 8EA" },
identifier: { scheme: "GB:VAT", value: "gb123456789" },
});
console.log(customer.id, customer.status, customer.networkDiscovery.state);
// "7c9a1b34-…", "pending", "pending"// 202 Accepted — verification runs asynchronously
{
"id": "7c9a1b34-2d5e-4f60-8a1b-9c2d3e4f5a6b",
"externalId": "customer_8412",
"companyName": "Bright Health Ltd",
"country": "GB",
"identifier": { "scheme": "GB:VAT", "value": "gb123456789" },
"status": "pending",
"networkDiscovery": { "state": "pending", "attempts": 0 },
"environment": "production",
"createdAt": "2026-06-01T10:00:00.000Z"
}Belgian customers
For Belgian Legal Entities, use scheme 0208 with the 10-digit BCE/KBO enterprise number. If you start from a VAT number such as BE0685660237, drop the BE prefix and send 0208:0685660237. The companyName should be the exact registered legal name for that number. Avoid 9925 for this API flow unless getpeppr support explicitly instructs you to use it. When VAT registration is confirmed during verification, getpeppr also registers the derived BE:VAT identifier on the network automatically — required for sending invoices that carry VAT (see Network identifiers and VAT).
curl https://api.getpeppr.dev/v1/legal-entities \
-H "Authorization: Bearer sk_sandbox_your_master_key" \
-H "Content-Type: application/json" \
-d '{
"externalId": "customer_be_001",
"companyName": "Exact Registered Legal Name",
"country": "BE",
"address": { "line1": "Rue de la Loi 16", "city": "Brussels", "zip": "1000" },
"identifier": { "scheme": "0208", "value": "0685660237" }
}'Swedish customers
For Swedish Legal Entities, use scheme 0007 with the 10-digit organisation number (organisationsnummer). If you start from a VAT number such as SE556036079301, drop the SE prefix and the trailing 01 and send 0007:5560360793. When the derived VAT number is confirmed against VIES, getpeppr also registers the SE:VAT identifier on the network automatically (see Network identifiers and VAT).
List the customers you manage, newest first. Results are isolated to your platform account. When offset exceeds the total, data is empty and has_more is false.
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
limit | number | Optional | Max results (default 50, max 100). |
offset | number | Optional | Pagination offset (default 0, max 100000). |
import { Peppol } from "@getpeppr/sdk";
const peppol = new Peppol({ apiKey: "sk_live_your_master_key" });
// One page
const page = await peppol.legalEntities.list({ limit: 20, offset: 0 });
console.log(page.data, page.meta.hasMore);
// …or iterate every customer — pagination handled for you
for await (const customer of peppol.legalEntities.listAll()) {
console.log(customer.externalId, customer.status, customer.networkDiscovery.state);
}{
"data": [
{
"id": "7c9a1b34-2d5e-4f60-8a1b-9c2d3e4f5a6b",
"externalId": "customer_8412",
"companyName": "Bright Health Ltd",
"country": "GB",
"identifier": { "scheme": "GB:VAT", "value": "gb123456789" },
"status": "active",
"networkDiscovery": {
"state": "verified",
"attempts": 0,
"checkedAt": "2026-06-01T10:02:00.000Z",
"nextAttemptAt": "2026-06-02T10:02:00.000Z"
},
"environment": "production",
"createdAt": "2026-06-01T10:00:00.000Z"
}
],
"pagination": { "total_count": 42, "offset": 0, "limit": 20, "has_more": true }
}Retrieve a single customer by their getpeppr id. On production this surfaces the full attestation lifecycle status (see the status reference); in sandbox it returns the verification status. When status is registration_failed, registrationDetail.reason is one of already_registered, invalid_format, or provider_error. The same additive field appears in LIST results and is absent for every other status. Unknown or unowned ids return 404.
import { Peppol } from "@getpeppr/sdk";
const peppol = new Peppol({ apiKey: "sk_live_your_master_key" });
const customer = await peppol.legalEntities.get(
"7c9a1b34-2d5e-4f60-8a1b-9c2d3e4f5a6b",
);
console.log(customer.status, customer.networkDiscovery.state);
// "active", "verified" once cross-AP receive readiness is proven{
"id": "7c9a1b34-2d5e-4f60-8a1b-9c2d3e4f5a6b",
"externalId": "customer_8412",
"companyName": "Bright Health Ltd",
"country": "GB",
"identifier": { "scheme": "GB:VAT", "value": "gb123456789" },
"status": "active",
"networkDiscovery": {
"state": "verified",
"attempts": 0,
"checkedAt": "2026-06-01T10:02:00.000Z",
"nextAttemptAt": "2026-06-02T10:02:00.000Z"
},
"environment": "production",
"createdAt": "2026-06-01T10:00:00.000Z"
}{
"id": "7c9a1b34-2d5e-4f60-8a1b-9c2d3e4f5a6b",
"externalId": "customer_8412",
"companyName": "Bright Health Ltd",
"country": "GB",
"identifier": { "scheme": "GB:VAT", "value": "gb123456789" },
"status": "registration_failed",
"registrationDetail": { "reason": "already_registered" },
"environment": "sandbox",
"createdAt": "2026-06-01T10:00:00.000Z"
}Off-board a customer. The legal entity is archived: the customer drops out of active lists and can no longer send, while audit records, attestations, and history are retained for compliance. Returns 200 with the archived entity. You can also archive a customer from the console (Legal Entities → the row's Archive action), which applies the same retention rules — production archives require typing the customer reference to confirm.
import { Peppol } from "@getpeppr/sdk";
const peppol = new Peppol({ apiKey: "sk_live_your_master_key" });
const result = await peppol.legalEntities.archive(
"7c9a1b34-2d5e-4f60-8a1b-9c2d3e4f5a6b",
);
console.log(result.status); // "archived"{
"id": "7c9a1b34-2d5e-4f60-8a1b-9c2d3e4f5a6b",
"externalId": "customer_8412",
"status": "archived"
}Request (or resend) the customer's authorisation. getpeppr emails a co-branded, getpeppr-signed attestation link to the contact; once they confirm, the customer advances toward active. Production only — calling this with a sandbox key returns 400.
Body parameters
| Name | Type | Required | Description |
|---|---|---|---|
contactEmail | string | Required | Email of the person who will authorise invoicing for this customer (max 254 chars). |
contactName | string | Optional | Display name shown in the authorisation email (max 128 chars). |
409 if the customer isn't verified yet or has already attested, and 502 if the email couldn't be sent (retry to mint a fresh link). Issuing an attestation emits a legal_entity.awaiting_authz webhook.import { Peppol } from "@getpeppr/sdk";
const peppol = new Peppol({ apiKey: "sk_live_your_master_key" });
const attestation = await peppol.legalEntities.requestAttestation(
"7c9a1b34-2d5e-4f60-8a1b-9c2d3e4f5a6b",
{ contactEmail: "owner@brighthealth.co.uk", contactName: "Dr Jane Okafor" },
);
console.log(attestation.status, attestation.expiresAt); // "awaiting_authz", "…"// 202 Accepted — co-branded email sent to the customer
{
"id": "7c9a1b34-2d5e-4f60-8a1b-9c2d3e4f5a6b",
"externalId": "customer_8412",
"status": "awaiting_authz",
"expiresAt": "2026-06-08T10:00:00.000Z"
}