API: Peppol lookups, watch lists and change webhooks
The E-Invoice Map API lets your own software do what the account page does: look up whether a company can receive e-invoices over Peppol and through which provider, keep watch lists, read what changed, and get changes pushed to your server. It is part of the Firm plan (plans). All answers come from the public Peppol network (the SML and each company's SMP); they are information, not an official certificate.
Authentication and limits
Create a key on your account page (owner only; it is shown once) and send it in a header:
X-API-Key: eir_…
- Base URL:
https://einvoicemap.com/api/v1. HTTPS only. Requests and answers are JSON unless noted. - Rate: your plan's requests per minute (Firm: 300) across all endpoints. Every answer carries
X-RateLimit-LimitandX-RateLimit-Remainingon lookups; above the limit you get429withRetry-After. - The key can read and change your lists. It can never change billing, users or close the account.
- Replacing or revoking the key on the account page stops the old one at once.
Errors
| Status | status field |
Meaning |
|---|---|---|
| 400 / 422 | invalid_request, invalid_input |
the input isn't valid (the error field says why) |
| 401 | not_logged_in, bad_key |
missing, unknown or revoked key |
| 403 | not_in_plan |
your plan doesn't include this |
| 404 | no_workspace, not_found |
no such client workspace or item |
| 429 | rate_limited |
too many requests; wait for Retry-After seconds |
| 503 | busy |
the service is busy; retry after Retry-After seconds |
Look up one company
GET /lookup?id=<VAT, company or Peppol number>&cc=<country, optional>
curl -H "X-API-Key: $KEY" "https://einvoicemap.com/api/v1/lookup?id=BE0403170701"
{
"status": "ok",
"countries": ["BE"],
"candidates": ["0208:0403170701", "9925:be0403170701"],
"match": {
"pid": "0208:0403170701",
"status": "ok",
"access_point": {"organisation": "Basware Corporation", "seat_id": "PFI000010", "country": "FI"},
"smp_host": "smp.tickstar.com",
"receives_invoices": true,
"document_types": ["Peppol BIS Billing 3.0 invoice", "Peppol BIS Billing 3.0 credit note"],
"transport": "peppol-transport-as4-v2_0",
"certificate": {"issuer": "PEPPOL ACCESS POINT CA - G3", "issuer_ok": true, "not_after": "2028-02-28",
"sha256": "fcdf3ad4…"}
},
"other_matches": [],
"checked_at": "2026-10-09T19:59:09+00:00",
"cached": false,
"products": [{"product": "…", "provider": "Basware"}]
}
status is ok (registered), no_naptr (not on Peppol), smp_404 / sg_empty (registration broken),
invalid_input, or timeout / error (try again). receives_invoices: false means registered but no invoice type
listed. Answers are cached for 24 hours (cached: true).
Watch lists
Your lists are the same as on the account page. kind is own (your own company's IDs) or customer; workspace
is a client workspace id (0 or omitted = your main list).
| Method and path | What |
|---|---|
GET /watch?kind=customer&workspace=0&limit=500&offset=0 |
the list with each ID's current state, totals, your plan's limit |
POST /watch |
add {"kind": "customer", "workspace": 0, "rows": [{"id": "BE0403170701", "cc": "BE", "ref": "C-17"}]} (up to 5,000 rows) |
POST /watch/remove |
remove {"ids": [<entry id>, …]} |
GET /watch/events?days=30&workspace=0 |
what changed (see the kinds below) |
GET /watch/export.csv?kind=customer&workspace=0 |
the list as CSV |
GET /watch/history.csv?workspace=0 |
every change in your plan's history window, as CSV |
GET /watch/evidence.pdf?workspace=0 |
the evidence record (PDF) |
GET /watch/onboarding.csv?workspace=0&lang=en |
the "please join Peppol" letter for every customer who can't receive yet |
GET /workspaces · POST /workspaces {"name"} |
client workspaces |
GET /workspaces/<id>/report.pdf?month=2026-09 |
a client's readiness report |
Adding answers with counts: {"added": 1, "duplicates": 0, "over_limit": 0, "invalid_count": 0, "used": 12, "limit": 50000}. New IDs are checked within about 15 minutes, then daily.
curl -H "X-API-Key: $KEY" -H "Content-Type: application/json" \
-d '{"kind":"customer","rows":[{"id":"BE0403170701","ref":"C-17"}]}' "https://einvoicemap.com/api/v1/watch"
Change kinds: first_check, became_reachable, no_longer_reachable, provider_changed, formats_changed,
registered_twice, registered_twice_resolved, certificate_expiring (within 30 days), certificate_expired,
certificate_renewed.
Webhook
Set an https URL on the account page (public host, port 443). After each check run (every 15 minutes) we POST your new changes there:
{"type": "watch.changes", "sent_at": "2026-10-10T08:15:03+00:00", "events": [
{"id": 812, "at": "2026-10-10T08:14:51+00:00", "kind": "provider_changed",
"detail": {"from": "Billit nv", "from_seat": "PBE000076", "to": "Basware Corporation", "to_seat": "PFI000010"},
"list": "customer", "input": "BE 0403.170.701", "ref": "C-17", "workspace": 0, "client": ""}]}
Answer with any 2xx status. Anything else, or no answer within 10 seconds, and we send the same changes again after
the next run. At most 500 events per request; more follow in the next one.
Check the signature before trusting a request. The header is X-EIM-Signature: t=<unix time>,v1=<hex>, where
v1 is the HMAC-SHA256 of <t>.<raw body> with your webhook secret (shown once when you save the URL):
import hashlib, hmac, time
def valid(raw_body: bytes, header: str, secret: str) -> bool:
parts = dict(p.split("=", 1) for p in header.split(","))
if abs(time.time() - int(parts["t"])) > 300:
return False
want = hmac.new(secret.encode(), parts["t"].encode() + b"." + raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(want, parts["v1"])
const crypto = require("crypto");
function valid(rawBody, header, secret) {
const p = Object.fromEntries(header.split(",").map(x => x.split("=")));
if (Math.abs(Date.now() / 1000 - Number(p.t)) > 300) return false;
const want = crypto.createHmac("sha256", secret).update(p.t + "." + rawBody).digest("hex");
return crypto.timingSafeEqual(Buffer.from(want), Buffer.from(p.v1));
}
Stability
This is version 1 (/api/v1). We add fields without notice; we don't remove or rename fields or change their meaning
within v1. Breaking changes come as /api/v2, announced by email at least 90 days ahead.
Need help integrating? If your software sends e-invoices and you are stuck, see Integration help.
Last updated: