# Billit > Billit is a European e-invoicing and billing platform and a certified Peppol Access Point. Founded in Ghent (Belgium) in 2014, it serves 255,000+ users and processes millions of invoices a month for senders and receivers in 37+ countries. AI agents can connect to a user's live Billit data through the Billit MCP server at https://mcp.my.billit.eu/ (Streamable HTTP, OAuth with PKCE, read-only). MCP access is granted on request, not self-service. This is the complete reference. A short index is at [llms.txt](https://www.billit.eu/llms.txt). Structured e-invoicing via Peppol is mandatory for B2B in Belgium since 1 January 2026; Billit is Peppol-ready by default. The platform covers sales invoicing, quotes, credit notes, OCR-based expense capture, automatic booking, recurring invoices, bank reconciliation, payment links and QR codes, and accountant collaboration. It ships with iOS and Android apps, 100+ integrations with banks, ERPs, CRMs, payment providers and accounting software, a public REST API, and a remote MCP server for AI agents. Pricing is consumption-based (from €7.50/month after a free 15-day trial) on processed documents rather than seats. Market portals exist for BE, NL, LU, DE and an international English site; product links below default to nl-be. Two ways in: - **AI agents and MCP clients:** `https://mcp.my.billit.eu/`. Read-only, OAuth with the user's own Billit login, access on request. See "MCP server". - **Developers and integrations:** REST API at `https://api.billit.be`, with a separate, self-service sandbox. `ApiKey` header for auth, `PartyID` header to select the company. Read and write. See "API". ## MCP server Billit builds and operates a remote Model Context Protocol server. It is the recommended way for AI assistants and agents (Claude, ChatGPT, Microsoft Copilot, or any MCP-compatible client) to work with a user's live Billit data. | Property | Value | |---|---| | Server URL | `https://mcp.my.billit.eu/` | | Transport | Streamable HTTP | | Getting access | Not self-service. Billit provides OAuth access and allowlists your client's redirect URI. Request it from the MCP team: sign in at [my.billit.eu](https://my.billit.eu/) and use the **Help** button in the portal. State which MCP client you use and its exact OAuth redirect (callback) URI. | | Authentication | OAuth 2.0 authorization code flow with PKCE (`S256`) and refresh tokens. The user signs in with their own Billit account. Access and refresh tokens are issued by Billit's OAuth server; the MCP server itself issues none. Send the access token as `Authorization: Bearer `. Requests without a valid token return `401`. | | Client registration | Dynamic client registration (RFC 7591) at `https://mcp.my.billit.eu/register` returns Billit's public client (no client secret). Only allowlisted redirect URIs are accepted, matched exactly; any other URI returns `400 invalid_redirect_uri`. The Claude and ChatGPT connector callbacks are on the allowlist. | | Protected resource metadata | `https://mcp.my.billit.eu/.well-known/oauth-protected-resource` | | Authorization server metadata | `https://mcp.my.billit.eu/.well-known/oauth-authorization-server` | | Authorization endpoint | `https://my.billit.be/Account/LogOn` | | Token endpoint | `https://api.billit.be/OAuth2/token` | | Access | Read-only. No documents are created, no invoices are sent, no records are changed. | | Scope of data | All companies the signed-in user can access, limited to what that user's role permits. | | Environment | Production only. There is no sandbox MCP endpoint. | | Documentation | [docs.billit.be/docs/mcp](https://docs.billit.be/docs/mcp) | ### Connecting **Step 1: request access.** Sign in at [my.billit.eu](https://my.billit.eu/), click **Help**, and ask the MCP team for MCP access. Include your MCP client and its exact redirect URI. **Step 2: connect your client.** - **Claude:** Settings → Connectors → **Add custom connector**. Name: `Billit MCP`. Server URL: `https://mcp.my.billit.eu/`. Click **Connect**, sign in to Billit, and approve the consent screen. - **ChatGPT:** add a connector with MCP server URL `https://mcp.my.billit.eu/`, trigger the connection, and sign in to Billit. Then enable Billit MCP from the tools menu in a chat. - **Microsoft Copilot:** an admin or maker adds the server once in Copilot Studio (agent → **Tools** → **Add a tool** → **New tool** → **Model Context Protocol**, server URL `https://mcp.my.billit.eu/`) and publishes the agent. Each end user then connects via **Open connection manager** → **Connect** and signs in to Billit. - **Any other MCP client or SDK:** point it at `https://mcp.my.billit.eu/`. The client reads the protected resource metadata, registers at `/register` with its allowlisted redirect URI, runs the authorization code flow with PKCE, and then calls `tools/list`. Users can revoke access at any time from their AI client's connector settings; this invalidates the OAuth tokens immediately. ### Tools | Tool | What it does | |---|---| | `get_account_information` | Returns the authenticated user and the companies the account can act for. Call first to set company context on multi-company accounts. | | `list_orders` | Lists invoices, credit notes, and orders (sales `Income` and purchase `Cost`) with OData filters on status, date, paid, expiry, amount, counterparty. At most 120 results per page. | | `get_order` | Full detail of one order: order lines, VAT groups, counterparty, payment status, Peppol delivery details. | | `summarize_orders` | Totals per group (for example per customer or supplier via `groupBy`) over the orders matching an OData `filter`. Scans a bounded number of orders (`maxRecords`) and returns per-group counts and sums, a grand total, a `Scanned` count, and a `Truncated` flag. | | `list_parties` | Lists customers or suppliers with compact summaries. Filters by type, VAT, name. | | `get_party` | Full detail of one customer or supplier: addresses, IBAN/BIC, contact info, Peppol identifier. | | `check_peppol_participant` | Checks whether a company (by VAT/CBE number) can receive Peppol e-invoices, which document types it supports, and its service provider. Uses the public Peppol directory. | | `search_companies` | Searches the public Belgian CBE/KBO registry by name or VAT number to find companies and resolve official VAT numbers. | `tools/list` on the live server returns the input schema for each tool. ### Agent guidance - If registration returns `400 invalid_redirect_uri`, or sign-in fails with a redirect error, the client is not allowlisted. Tell the user to request access via the **Help** button at [my.billit.eu](https://my.billit.eu/). Do not retry. - Call `get_account_information` first and pick the right company before any other call. Never assume a default company. - The MCP server is read-only. To create, send, or modify invoices, use the REST API in the "API" section. - Before recommending Peppol delivery to a counterparty, call `check_peppol_participant`. - For totals per customer or supplier, call `summarize_orders` instead of paging through `list_orders`. If `Truncated` is `true`, narrow the `filter` (for example with a date range) and call again. - Tool results are sent to the AI provider the user connected, under that provider's data terms. For EU data residency, run the model through an EU endpoint (for example AWS Bedrock or Google Vertex AI in an EU region). - Example requests the server is built for: "Show me all unpaid sales invoices that are overdue", "What did we spend with suppliers since January?", "Is Acme NV able to receive an e-invoice over Peppol?", "Find the VAT number for 'Example Bakery' in the company registry". ### MCP vs. REST API | Use the MCP server when | Use the REST API when | |---|---| | An AI assistant acts on behalf of a signed-in user | You build a server-to-server integration | | You need read access to invoices, parties, and Peppol status | You need to create, send, or update documents, or manage webhooks | | The user signs in with their own Billit login | You manage an `ApiKey` per environment | | You work with production data | You need a sandbox for testing | ## API Billit's public REST API is JSON over HTTPS, hosted at `api.billit.be`. Every request is authenticated via an `ApiKey` (or `Authorization` access token) header and scoped to a company by the `PartyID` header. Sandbox and production are fully separate environments (separate keys, separate data, identical schema), so the same client code works in both by swapping the base URL and key. **Scope.** The API covers e-invoicing and document workflows: orders (sales and purchase invoices, quotes, credit notes), parties (customers and suppliers), files (PDFs and attachments), Peppol send and receive, expenses, and webhooks. It does **not** cover payroll, full bookkeeping ledgers, VAT return filing, or banking transactions; those live in adjacent systems Billit integrates with. ### Quickstart (60 seconds to a successful call) 1. Sign up at [my.sandbox.billit.be](https://my.sandbox.billit.be/) for a free sandbox company. 2. In the sandbox app, go to **Settings → API keys** and create a key. 3. Verify your credentials and discover your `PartyID`s by calling the account-information endpoint. It needs only the `ApiKey` header (no `PartyID` yet) and returns the companies your user has access to: ```bash curl "https://api.sandbox.billit.be/v1/account/accountInformation" \ -H "ApiKey: $BILLIT_SANDBOX_KEY" \ -H "Accept: application/json" ``` A 200 response means auth works. Read `PartyID` from any item in the `Companies` array; that's the value to send in the `PartyID` header on subsequent calls. When ready, swap `api.sandbox.billit.be` for `api.billit.be` and the sandbox key for a production key. No other code changes. ### Environments - **Production:** `https://api.billit.be`. Live environment. Invoices created here are legally valid and can be transmitted over Peppol. Interactive Swagger reference at `https://api.billit.be/swagger/ui/index`. - **Sandbox:** `https://api.sandbox.billit.be`. Free test environment for development, automated tests, and Peppol send/receive dry-runs. No real invoices are issued. - **MCP (AI agents):** `https://mcp.my.billit.eu/`. Production only, read-only, OAuth. See "MCP server". ### Authentication Billit supports two authentication methods, both passed as request headers: - **API key** (recommended for server-to-server): `ApiKey: ` - **Access token** (OAuth-style): `Authorization: ` Use one or the other, not both. API keys are issued per environment from the Billit web app. Treat keys and tokens as secrets: store them in environment variables or a secret manager, never commit them to source control, and rotate on suspected compromise. In addition to authentication, every call needs a **`PartyID`** header identifying which company (party) the call operates on. If the caller is an accountant operating on behalf of a client, also send **`ContextPartyID`** for the client party. See "Required headers" below. ### Resource model A few naming conventions and behaviors are non-obvious and worth knowing up-front: - **Orders, not invoices.** Sales invoices, purchase invoices, quotes, and credit notes are all `Order` resources, distinguished by `OrderType` (`Invoice`, `Quote`, `CreditNote`, etc.) and `OrderDirection` (`Income` for sales, `Cost` for purchases). The list endpoint is `/v1/orders`; there is no `/v1/invoices`. - **Parties, not contacts.** Customers, suppliers, and your own company are all `Party` resources, distinguished by `PartyType` (`Customer`, `Supplier`, etc.). Each party can carry multiple `Identifiers` (VAT number, GLN, Peppol participant ID). - **Customers auto-create on first invoice.** When you POST an order, Billit tries to match the embedded `Customer` against existing parties (by VAT number, email, address, or `Nr`). If no match is found, a new party is created automatically; no separate "create customer" call is needed. - **Totals are calculated server-side.** Send `Quantity`, `UnitPriceExcl`, and `VATPercentage` per line; Billit computes line totals, VAT, and document totals. This avoids rounding mismatches at validation. See the [calculations doc](https://docs.billit.be/docs/calculations). - **Currency defaults to EUR.** Set `Currency` (ISO code) explicitly only when invoicing in another currency. - **Custom Fields are the extension point.** Anything not in the base schema (Peppol UBL paths like `Invoice.AccountingCost`, delivery location IDs, contract references, supplier/customer contact blocks, line-level item identifiers, etc.) is set via a `CustomFields` object on the order or order line. Field names are dot-paths matching the Peppol UBL element. For credit notes, replace the `Invoice.` prefix with `CreditNote.`. See [custom fields](https://docs.billit.be/docs/custom-fields-credit-note) and the header / line custom-field references in the developer docs. - **Files.** PDFs and other attachments are referenced by `FileID` (UUID) and fetched via the file endpoints rather than embedded inline. ### Required headers Headers Billit accepts on requests **to** the API: | Header | Value | Purpose | |---|---|---| | `ApiKey` | Your secret API key | Authentication (option 1) | | `Authorization` | Access token | Authentication (option 2) | | `PartyID` | Integer: your party (company) ID | Defines which company the request operates on. Required when your account spans multiple companies. | | `ContextPartyID` | Integer: a client's party ID | Used by accountants to act on behalf of a client party. | | `Idempotent-Key` | Unique client-generated string | Makes a request idempotent, safe to retry without creating duplicates. Note the spelling: `Idempotent-Key`, not the more common `Idempotency-Key`. | | `StrictTransportType` | `true` | Force strict transport behavior: fail rather than fall back to an alternative method (e.g. don't fall back to email if Peppol delivery isn't available). | | `Accept` | `application/json` | Recommended on every request. | | `Content-Type` | `application/json` | Required on `POST`, `PUT`, `PATCH`. | Headers Billit sends **from** the API (on webhooks): | Header | Value | Purpose | |---|---|---| | `Billit-Signature` | Timestamp + signature | Verify the webhook payload originated from Billit. Validate this on every webhook receiver. | ### Get account information (verified example) `GET /v1/account/accountInformation` returns the authenticated user's profile (`Email`, `FirstName`, `LastName`) and the `Companies` array: every party the user has access to, fully populated. Use this call to discover valid `PartyID` values, IBAN/BIC, Peppol identifiers, default transport types, and bank-account details for each company. ```bash curl "https://api.billit.be/v1/account/accountInformation" \ -H "ApiKey: $BILLIT_API_KEY" \ -H "Accept: application/json" ``` Each item in `Companies` is a `Party` with fields including `PartyID`, `Name`, `VATNumber`, `PeppolIdentifier`, `IBAN` / `BIC`, `BankAccounts`, `Addresses`, `DefaultTransportTypeTC`, `DefaultPaymentMethodTC`, and `Identifiers`. This is the canonical schema for Party objects throughout the API; the same shape appears as the `CounterParty` on orders, the `Customer` on invoices, and so on. ### List invoices (verified example) `GET /v1/orders` returns a paginated list of orders (invoices, quotes, credit notes; see "Resource model"). The response is wrapped in `{ "Items": [ ... ] }`. List responses contain a summary projection of each order, not the full document; fetch a single order by ID for complete data. Filter using OData query parameters. To list only sales invoices (sent to your customers): ```bash curl -X GET "https://api.billit.be/v1/orders?\$filter=OrderType+eq+'Invoice'+and+OrderDirection+eq+'Income'" \ -H "ApiKey: $BILLIT_API_KEY" \ -H "PartyID: $BILLIT_PARTY_ID" \ -H "Accept: application/json" ``` Sample response (one item shown, fields truncated): ```json { "Items": [ { "OrderID": 125478, "OrderType": "Invoice", "OrderDirection": "Income", "OrderNumber": "QS-001", "OrderDate": "2024-01-01T00:00:00", "ExpiryDate": "2024-01-31T00:00:00", "OrderStatus": "ToSend", "TotalExcl": 17.50, "TotalIncl": 19.68, "TotalVAT": 2.18, "ToPay": 19.68, "Paid": false, "Overdue": true, "DaysOverdue": 39, "Currency": "EUR", "PaymentReference": "+++001/1800/63503+++", "OrderPDF": { "FileID": "c1cd2bfa-3678-42dd-99f3-2f6d323bd2fc" }, "CounterParty": { "PartyID": 125999, "DisplayName": "Billit", "VATNumber": "BE0563846944", "PartyType": "Customer", "Identifiers": [ { "IdentifierType": "GLN", "Identifier": "5430003732007" } ] } } ] } ``` Common filter recipes: | Goal | OData filter | |---|---| | Sales invoices | `OrderType eq 'Invoice' and OrderDirection eq 'Income'` | | Purchase invoices | `OrderType eq 'Invoice' and OrderDirection eq 'Cost'` | | Unpaid sales invoices | `OrderType eq 'Invoice' and OrderDirection eq 'Income' and Paid eq false` | | Overdue | `Overdue eq true` | | Issued in 2025 | `OrderDate ge 2025-01-01 and OrderDate lt 2026-01-01` | See the [OData reference](https://docs.billit.be/docs/odata) for the full query syntax (`$filter`, `$top`, `$skip`, `$orderby`, `$select`). ### Create an invoice (verified example) `POST /v1/orders` creates an order. The same endpoint creates sales invoices, purchase invoices, quotes, and credit notes; the `OrderType` and `OrderDirection` fields determine which. The response is a single integer: the new `OrderID`. Store it on your side; you'll need it to send, retrieve, or credit the order later. A minimal sales invoice (`OrderDirection: Income`). Billit auto-creates the customer if it doesn't already exist and calculates all totals server-side: ```bash curl -X POST "https://api.billit.be/v1/orders" \ -H "ApiKey: $BILLIT_API_KEY" \ -H "PartyID: $BILLIT_PARTY_ID" \ -H "Idempotent-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -H "Accept: application/json" \ -d '{ "OrderType": "Invoice", "OrderDirection": "Income", "OrderNumber": "QS-001", "OrderDate": "2025-09-01", "ExpiryDate": "2025-10-30", "Customer": { "Name": "Billit", "VATNumber": "BE0563846944", "PartyType": "Customer", "Email": "buyer@example.com", "Addresses": [{ "AddressType": "InvoiceAddress", "Street": "Oktrooiplein", "StreetNumber": "1", "City": "Ghent", "Zipcode": "9000", "CountryCode": "BE" }] }, "OrderLines": [ { "Quantity": 1, "UnitPriceExcl": 10.00, "Description": "Box of cookies", "VATPercentage": 6 }, { "Quantity": 2, "UnitPriceExcl": 3.75, "Description": "Sticks", "VATPercentage": 21 } ] }' ``` Common header-level fields beyond the minimum: `Reference` (buyer's PO number, one only, no extra text), `OrderTitle` (other buyer reference), `PaymentReference` (structured OGM or free text; Billit auto-generates one if omitted), `DeliveryDate`, `Currency` (defaults to EUR), `Comments` (free text shown on the PDF). For richer Peppol UBL fields (project reference, contract reference, accounting cost, tax representative party, delivery location, line-level item identifiers, etc.), use `CustomFields`; see [Create First Invoice docs](https://docs.billit.be/docs/create-first-invoice) and the custom-fields references. **Validation errors** return HTTP 4xx with this shape: ```json { "errors": [ { "Code": "InvalidVAtPercentageForTaxCountry_0_", "Description": "Invalid VAT percentage for country code BE" } ] } ``` After creating an order it lives in Billit but isn't yet sent. Use the **send command** described next to deliver it over Peppol, email, or another channel. ### Send an invoice (verified example) `POST /v1/orders/commands/send` dispatches one or more existing orders. The same endpoint covers Peppol, email, and other channels; the channel is selected by `Transporttype`. **On success the response is HTTP 200 with an empty body.** Agents that expect JSON will misread silence as failure if not handled. ```bash curl -X POST "https://api.billit.be/v1/orders/commands/send" \ -H "ApiKey: $BILLIT_API_KEY" \ -H "PartyID: $BILLIT_PARTY_ID" \ -H "Content-Type: application/json" \ -d '{ "Transporttype": "Peppol", "OrderIDs": [2602961] }' ``` Body shape: `{ "Transporttype": "", "OrderIDs": [, ...] }`. The other plausible-looking shape, `{ "Transactions": [{ "OrderID": ..., "Transporttype": ... }] }`, returns `ListOfOrdersToSendIsRequired`. **Transport types.** Common values: | Value | Channel | Customer prerequisite | |---|---|---| | `Peppol` | Peppol network (BE/EU) | Customer party has a Peppol-resolvable identifier. For BE companies, setting `Customer.VATNumber` is sufficient; Billit derives the participant ID (e.g. `0208:0563846944`) automatically. | | `SMTP` | Email | `Customer.Email` is set to a valid address. | `Email` is **not** a valid transport type and returns `Invalid transport type`; use `SMTP`. See [transport types](https://docs.billit.be/docs/types) for the full list including letter and country-specific channels. **Side effects of a successful send.** `OrderStatus` transitions `ToSend` → `ToPay`, `IsSent` flips to `true`, and a new entry is appended to the order's `Messages` array with `TransportType`, `Destination`, `Trials`, `CreationDate`, and (once the transport confirms) `Success: true`. Re-sending the same order on a different transport is allowed and appends another `Messages` entry rather than erroring or creating a new order, which is useful for multi-channel delivery and retries. **Tracking delivery.** Two fields on the order, refreshable via `GET /v1/orders/{OrderID}`: - `CurrentDocumentDeliveryDetails`: `{ IsDocumentDelivered, DocumentDeliveryStatus, DocumentDeliveryDate, DocumentDeliveryInfo }`. Peppol typically completes synchronously (status `Delivered` already set when the send response returns); SMTP enters `Pending` and resolves asynchronously. - `Messages[*].Success`: flips to `true` once the transport confirms delivery. For production, prefer webhook subscriptions on `EntityType: Order` over polling. **Common send errors.** | Code | Meaning | Fix | |---|---|---| | `ListOfOrdersToSendIsRequired` | Wrong body shape | Use `{ "Transporttype": "...", "OrderIDs": [...] }`, not `Transactions: [...]` | | `TheCustomerIsNotActiveOnPeppol` | Customer has no Peppol identifier or isn't on the Peppol SML | PATCH `VATNumber` onto the customer party (see "Update a party"), or fall back to `SMTP` | | `TheCustomer_0_DoesNotHaveAValidEmailAddress` | SMTP transport but no `Customer.Email` | PATCH `Email` onto the customer party | | `Invalid transport type` | Sent `Email` (or another invalid value) | Use `SMTP` for email | **Pre-flight pattern.** Before calling send, fetch the customer party and verify the prerequisite for the chosen transport (Peppol → `VATNumber` set; SMTP → `Email` set). If missing, either `PATCH /v1/parties/{PartyID}` to add it (see "Update a party") or fall back to the other transport. Setting `StrictTransportType: true` on the request disables fallback and forces a hard fail if the chosen transport can't deliver, which is useful when Peppol delivery must succeed or fail loudly. Agents connected via MCP can run the Peppol check with `check_peppol_participant`. ### Update a party (verified example) `PATCH /v1/parties/{PartyID}` partial-updates a party record (customer, supplier, or own company). Send only the fields you want to change. Returns HTTP 200 with empty body. ```bash curl -X PATCH "https://api.billit.be/v1/parties/1079522" \ -H "ApiKey: $BILLIT_API_KEY" \ -H "PartyID: $BILLIT_PARTY_ID" \ -H "Content-Type: application/json" \ -d '{"Email": "buyer@example.com"}' ``` Commonly-patched fields: `Email`, `VATNumber`, `Phone`, `Addresses`, `Identifiers`, `IBAN`. Top-level fields can be set directly; they don't need to be nested under `Identifiers`. In particular, setting `VATNumber` on a BE party is sufficient to make the party Peppol-resolvable; Billit derives the Peppol participant ID (`0208:`) at send time. Most common reason to call this: a send command failed with `TheCustomer_0_DoesNotHaveAValidEmailAddress` or `TheCustomerIsNotActiveOnPeppol`. PATCH the missing field on, then retry the send. ### Common tasks Fast index from intent to endpoint, tool, or doc: | Task | How | |---|---| | Connect an AI assistant or agent to Billit | MCP server `https://mcp.my.billit.eu/`; see "MCP server" | | Get my user + list of companies I can access | `GET /v1/account/accountInformation` (no `PartyID` header needed), or MCP `get_account_information` | | List sales invoices | `GET /v1/orders` with `$filter=OrderType eq 'Invoice' and OrderDirection eq 'Income'`, or MCP `list_orders` | | List purchase invoices (received) | `GET /v1/orders` with `$filter=OrderType eq 'Invoice' and OrderDirection eq 'Cost'`, or MCP `list_orders` | | List unpaid / overdue invoices | OData filters on `Paid eq false` or `Overdue eq true` | | Get a single order by ID | `GET /v1/orders/{OrderID}` (full document, not the list summary), or MCP `get_order` | | Totals per customer or supplier | MCP `summarize_orders` | | Create sales invoice | `POST /v1/orders` with `OrderType: Invoice, OrderDirection: Income` (REST only) | | Create credit note | `POST /v1/orders` with `OrderType: CreditNote`. In `CustomFields`, swap the `Invoice.` prefix for `CreditNote.` | | Create quote | `POST /v1/orders` with `OrderType: Quote` | | Send invoice over Peppol | `POST /v1/orders/commands/send` with `{"Transporttype": "Peppol", "OrderIDs": [...]}` | | Send invoice via email | `POST /v1/orders/commands/send` with `{"Transporttype": "SMTP", "OrderIDs": [...]}`; `SMTP`, not `Email` | | Check if a company can receive Peppol invoices | MCP `check_peppol_participant` | | Look up a Belgian company's VAT number | MCP `search_companies` (CBE/KBO registry) | | Track delivery status | `GET /v1/orders/{OrderID}`, read `CurrentDocumentDeliveryDetails` and `Messages[]`, or subscribe to webhooks on `EntityType: Order` | | Add a customer | No separate call: embed `Customer` in the order; Billit auto-creates parties on first use | | Update a customer / supplier | `PATCH /v1/parties/{PartyID}` with the fields to change | | Attach a PDF | `AdditionalDocumentReference` in `CustomFields`; see [header custom fields](https://docs.billit.be/docs/how-can-i-add-certain-peppol-values-that-billit-json-does-not-support) | | Set delivery location (GLN) | `Invoice.Delivery.DeliveryLocation.*` in `CustomFields`; see [delivery location](https://docs.billit.be/docs/extra-fields-delivery-location) | | Override supplier/customer contact for one invoice | `Invoice.AccountingSupplierParty.Party.Contact.*` / `Invoice.AccountingCustomerParty.Party.Contact.*` in `CustomFields`; see [contacts](https://docs.billit.be/docs/extra-fields-supplier-and-customer-contacts) | | Act as accountant for a client | Add `ContextPartyID: ` header on every call | | React to invoice status changes | Subscribe to webhooks; see "Webhooks" | ### Webhooks Subscribe to webhooks to be notified of state changes (invoice sent, payment received, Peppol delivery succeeded or failed, document received) instead of polling `/v1/orders`. **Subscription lifecycle.** Manage webhooks entirely via the API: | Method | Endpoint | Purpose | |---|---|---| | `POST` | `/v1/webhooks` | Create a subscription. Response includes the `Secret` used to verify incoming events. Store it; it isn't returned again. | | `GET` | `/v1/webhooks` | List all subscriptions for the current account (without secrets). | | `DELETE` | `/v1/webhooks/{webhookID}` | Unsubscribe. | | `POST` | `/v1/webhooks/refresh/{webhookID}` | Rotate the secret if it leaks. | Create-request body: ```json { "EntityType": "Order", "EntityUpdateType": "I", "WebhookURL": "https://your-app.example.com/billit-events" } ``` Create-response: ```json { "WebhookID": 12345, "EntityType": "Order", "EntityUpdateType": "I", "WebhookURL": "https://your-app.example.com/billit-events", "Secret": "" } ``` **Subscriptions are environment-scoped.** Sandbox webhooks fire only on sandbox events; production webhooks fire only on production events. **Payload shape.** Billit POSTs JSON with the entity ID and an `EntityType` discriminator, e.g.: ```json { "OrderID": 12345, "EntityType": "Order", ... } ``` **Signature verification.** Every webhook carries a `Billit-Signature` header containing a timestamp and HMAC-SHA-256 signature, comma-separated: ``` Billit-Signature: t=1657133145,s=479f86b8baa181b97281b767b8db125cc312d18b3e611d6157571abcd539f09f ``` To verify a webhook on receipt: 1. **Parse the header.** Split on `,` then on `=` to extract the timestamp `t` and signature `s`. 2. **Rebuild the signed payload.** Concatenate `` + `.` + ``. Use the *raw* request body, not a re-serialized version; re-serializing can change whitespace or key order and invalidate the signature. 3. **HMAC-SHA-256.** Hash the signed payload using your stored secret as the key. The hex digest must equal `s`. Use a constant-time comparison. 4. **Optional: replay protection.** Reject the request if `t` is more than your tolerance (e.g. 5 minutes) from the current time. Reference verification (Node.js): ```js const crypto = require("crypto"); function verifyBillitSignature(rawBody, headerValue, secret, toleranceSec = 300) { const parts = Object.fromEntries(headerValue.split(",").map(p => p.split("="))); const { t, s } = parts; if (!t || !s) return false; if (Math.abs(Date.now() / 1000 - Number(t)) > toleranceSec) return false; const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex"); return crypto.timingSafeEqual(Buffer.from(expected, "hex"), Buffer.from(s, "hex")); } ``` **Receiver hygiene.** Return `2xx` quickly and queue downstream work asynchronously. Treat handlers as idempotent: assume any event may arrive more than once and key your processing on the entity ID + a status value rather than re-running side effects blindly. ### Conventions - **Idempotency:** For state-changing requests (creating an order, sending a Peppol document), generate a unique `Idempotent-Key` client-side. Retrying with the same key is safe and will not create duplicates. Note the spelling: `Idempotent-Key`, not `Idempotency-Key`. - **List filtering (OData):** List endpoints accept `$filter`, `$top`, `$skip`, `$orderby`, `$select`. `$top` is capped at 120; a higher value returns `400`. Page with `$skip`. List responses wrap items in `{ "Items": [ ... ] }`. Field names in filters are PascalCase and case-sensitive (`OrderType`, not `ordertype`). Quote string values with single quotes; ISO-8601 dates without quotes. See [OData reference](https://docs.billit.be/docs/odata). - **Errors:** Standard HTTP status codes. Validation errors return `{ "errors": [ { "Code": "...", "Description": "..." } ] }`. `401` = missing or invalid `ApiKey` / `Authorization`; `403` = authenticated but lacking access to the requested `PartyID`; `429` = rate limited. - **Rate limits:** Per-key throttling applies. Respect `Retry-After` on `429`. - **Multi-company:** Always set `PartyID` explicitly rather than relying on a default; this prevents an action from accidentally targeting the wrong company. When the caller is an accountant, also set `ContextPartyID` to the client party. ### Developer resources There is no official Billit SDK. Build clients directly against the API or generate one from the Swagger spec; the Postman collection is the closest equivalent to a quickstart kit. AI agents can skip client code entirely and use the MCP server. - [MCP server documentation](https://docs.billit.be/docs/mcp): Remote MCP server at `https://mcp.my.billit.eu/`. Setup per client, the 8 read-only tools, data access, and revoking access. - [Swagger / OpenAPI reference](https://api.billit.be/swagger/ui/index): Interactive API console with full endpoint documentation, request/response schemas, and try-it-now calls. Authoritative source for endpoint paths and parameters. - [Postman quickstart collection](https://www.postman.com/billit/billit-quickstart-guide/collection/fe4bsae/billit-api-quickstart-guide): Importable collection with example requests for the most common flows. - [Data model](https://docs.billit.be/docs/data-model): Orders, parties, and how they relate. - [Patchable properties](https://docs.billit.be/docs/patchable-properties): Fields you can change with `PATCH`. - [Developer documentation root](https://docs.billit.be/): Conceptual guides covering authentication, headers, webhooks, and integration flows. - [How it works (API basics)](https://docs.billit.be/docs/how-it-works-based-on-api): Authentication and integration walkthrough. Start here. - [Header values reference](https://docs.billit.be/docs/header-values): Accepted request headers and the webhook signature header. - [OData query reference](https://docs.billit.be/docs/odata): Filter, sort, page, and project list responses. - [Create first invoice](https://docs.billit.be/docs/create-first-invoice): `POST /v1/orders` with minimal and advanced bodies, customer auto-creation, validation, multi-company headers. - [Calculations](https://docs.billit.be/docs/calculations): How Billit computes line and document totals server-side. - [Custom Fields (header)](https://docs.billit.be/docs/how-can-i-add-certain-peppol-values-that-billit-json-does-not-support): Header-level Peppol UBL extensions: `Invoice.AccountingCost`, `Invoice.ProjectReference.ID.Text`, `Invoice.ContractDocumentReference.ID.Text`, `PaymentTerms`, `PaymentMeansCode`, `AdditionalDocumentReferenceN`, `Invoice.TaxRepresentativeParty.*`, etc. - [Custom Fields: supplier & customer contacts](https://docs.billit.be/docs/extra-fields-supplier-and-customer-contacts): Per-invoice contact override. - [Custom Fields: delivery location](https://docs.billit.be/docs/extra-fields-delivery-location): GLN-coded or free-text delivery addresses. - [Custom Fields: line level](https://docs.billit.be/docs/extra-fields-extend-content-with-extra-values-line): `PeppolUnitCode`, `PeppolLineID`, `InvoiceLine.Note`, `InvoiceLine.InvoicePeriod.*`, `InvoiceLine.Item.StandardItemIdentification.*` (GTIN), `InvoiceLine.Item.BuyersItemIdentification.*`, `CommodityClassification*`, `AdditionalItemProperty*`. - [Custom Fields for credit notes](https://docs.billit.be/docs/custom-fields-credit-note): Replace the `Invoice.` prefix with `CreditNote.`. - [Transport types](https://docs.billit.be/docs/types): Channels available to the send command. - [Webhook API reference](https://docs.billit.be/reference/webhook): Create, retrieve, delete, and refresh-secret endpoints. ## Product - [How Billit works](https://www.billit.eu/nl-be/zo-werkt-billit/): Product overview. - [All features](https://www.billit.eu/nl-be/zo-werkt-billit/functies/): Full catalog of invoicing, expense, automation, and collaboration features. - [Sales / income](https://www.billit.eu/nl-be/functies/inkomsten/): Quotes, sales invoices, credit notes, payment links, QR codes, automated reminders. - [Expenses](https://www.billit.eu/nl-be/functies/uitgaven/): Receipt scanning, supplier-invoice intake via email and Peppol, approvals. - [Automatic processing](https://www.billit.eu/nl-be/functies/automatische-verwerking/): OCR, auto-booking, recurring invoices, bank reconciliation. - [Mobile app](https://www.billit.eu/nl-be/billit-app/): iOS and Android apps for invoicing and receipt capture. - [Integrations](https://www.billit.eu/nl-be/koppelen-met-billit/): 100+ connectors with banks, ERPs, CRMs, payment providers, and accounting suites. - [Bank integrations](https://www.billit.eu/nl-be/koppelen-met-billit/bankkoppelingen/): Connect bank accounts for payment matching. - [Accounting software integrations](https://www.billit.eu/nl-be/koppelen-met-billit/koppel-met-je-boekhoudsoftware/): Sync with accounting packages. - [Billit MCP](https://docs.billit.be/docs/mcp): Connect Claude, ChatGPT, Copilot, or any MCP client to Billit. ## Peppol & e-invoicing - [Peppol](https://www.billit.eu/nl-be/peppol/): What Peppol is and Billit's role as a certified Access Point. - [Country list](https://www.billit.eu/nl-be/peppol/peppol-en-e-invoicing-landenlijst/): Per-country e-invoicing mandates and Peppol coverage. - [Connect software to the Billit Access Point](https://www.billit.eu/nl-be/koppelen-met-billit/koppel-jouw-software-met-het-billit-peppol-access-point/): Integration paths for software vendors and ERP providers. ## Audiences - [Freelancers and sole traders](https://www.billit.eu/nl-be/voor-wie/freelancers-en-eenmanszaken/): Lightweight invoicing for solo operators. - [Self-employed and small businesses](https://www.billit.eu/nl-be/voor-wie/zelfstandigen-en-kleine-ondernemingen/): Invoicing and expenses for small teams. - [SMEs and large companies](https://www.billit.eu/nl-be/voor-wie/kmos-en-grote-bedrijven/): Multi-user workflows, high volumes, ERP integration, multi-entity setups. - [Public institutions](https://www.billit.eu/nl-be/voor-wie/openbare-instellingen/): Procurement and Peppol-mandated public-sector flows. - [Accountants](https://www.billit.eu/nl-be/voor-wie/accountants/): Client portal and the Trusted Billit Advisors program. - [Software & ERP providers](https://www.billit.eu/nl-be/voor-wie/software-en-erp-dienstverleners/): White-label and Access Point integration. - [Partners](https://www.billit.eu/nl-be/partners/): Reseller and referral program. ## Pricing - [Pricing](https://www.billit.eu/nl-be/prijzen/): Document-based plans from €7.50/month, free 15-day trial, no credit card required, free Peppol connection on every plan. - [120% tax deductibility (BE)](https://www.billit.eu/nl-be/prijzen/billit-is-120-procent-fiscaal-aftrekbaar/): Belgian tax incentive for e-invoicing software through end of 2027. ## Help & support - [Support](https://www.billit.eu/nl-be/ondersteuning/): Support options overview. - [Help articles](https://www.billit.eu/nl-be/helpartikelen/): Searchable product documentation and how-tos. - [Get started in 6 steps](https://www.billit.eu/nl-be/zo-werkt-billit/aan-de-slag-met-billit-in-6-stappen/): Quickstart guide for new users. - [Webinars & workshops](https://www.billit.eu/nl-be/ondersteuning/webinars-workshops/): Live and recorded sessions. - [Helpcenter Sint-Niklaas](https://www.billit.eu/nl-be/ondersteuning/helpcenter-sint-niklaas/): Walk-in support center. - [Remote assistance](https://www.billit.eu/nl-be/ondersteuning/hulp-op-afstand/): Screen-share support sessions. - [Contact](https://www.billit.eu/nl-be/contact/): Sales and support channels. ## Trust & compliance - [Compliance and trust](https://www.billit.eu/nl-be/over-ons/conformiteit-en-vertrouwen/): Security, privacy, GDPR posture, Peppol certification. - [Legal information](https://www.billit.eu/nl-be/juridische-info/): Terms, privacy policy, cookie policy. - [Customer stories](https://www.billit.eu/nl-be/over-ons/klantverhalen/): Case studies across customer segments. ## Company - [About Billit](https://www.billit.eu/nl-be/over-ons/): Founded 2014. HQ: Oktrooiplein 1/302, 9000 Ghent, Belgium. VAT: BE0563846944. - [Careers](https://jobs.billit.eu/): Open roles at Billit. - [In the media](https://www.billit.eu/nl-be/over-ons/in-de-media/): Media coverage. - [Brochures](https://www.billit.eu/nl-be/resources/brochure/): Downloadable product brochures. ## App & login - [Free signup](https://my.billit.eu/Account/Register?country=BE&language=NL): Create a Billit account. - [Login](https://my.billit.eu/Account/Logon?language=NL&country=BE): Sign in to the Billit web app. - [Sandbox login](https://my.sandbox.billit.be/): Sign in to the sandbox environment for API testing. - [iOS app](https://apps.apple.com/be/app/billit/id6469793857): Native iOS application. - [Android app](https://play.google.com/store/apps/details?id=eu.billit.mobile): Native Android application. ## Localized portals - [English (international)](https://www.billit.eu/en-int/) - [Dutch (Belgium)](https://www.billit.eu/nl-be/) - [French (Belgium)](https://www.billit.eu/fr-be/) - [German (Belgium)](https://www.billit.eu/de-be/) - [Dutch (Netherlands)](https://www.billit.eu/nl-nl/) - [German (Germany)](https://www.billit.eu/de-de/) - [German (Luxembourg)](https://www.billit.eu/de-lu/) - [French (Luxembourg)](https://www.billit.eu/fr-lu/) ## Optional - [Blog](https://www.billit.eu/nl-be/resources/blog/): Long-form content on e-invoicing, Peppol, and accounting trends. - [Trusted Billit Advisors](https://www.billit.eu/nl-be/voor-wie/accountants/trusted-billit-advisors/): Certified-accountant partner program. - [LinkedIn](https://www.linkedin.com/company/billit/) - [Facebook](https://www.facebook.com/billit.be.nl) - [Instagram](https://www.instagram.com/billitbenl/)