Envelopify API
Envelopify sends real paper letters with real postage stamps from your software, in Belgium. One API call per letter: we print, envelope, stamp and hand the letter to bpost. Standard: handed to bpost within 3 working days. Express: the same working day when received before 12:00 (Europe/Brussels). Flex: within 5 working days. bpost delivers within 2 to 3 working days after hand-over.
https://envelopify.lovable.app/api/v1Build it with AI
Give this link to your AI assistant and let it build the integration. The full documentation is available as plain text.
Read the Envelopify API docs at https://envelopify.lovable.app/llms-full.txt. Build an integration in my app that automatically sends a letter when [event, e.g. an invoice is created]. Only call the API from a server function, store the API key as a secret and never in the browser. Start with my test key (test_...). Show API errors to the user in plain language and store the id and status of every letter.Also available: openapi.json, developers.md and llms.txt.
Start in 5 minutes
- Create a key. Create a free account, open API-sleutels (API keys) and create a test key. Replace
test_YOUR_KEYin the examples. - Send a test letter. The example under Send a letter uses our sample PDF and works as is. Nothing is printed or charged.
- Check the status. Take the
idfrom the response and callGET /api/v1/letters/{id}. Add/pdfto get the PDF exactly as we print it. - Set up webhooks. Register an https URL with
POST /api/v1/webhooksto be notified when a letter is printed and posted. - Go live. Top up credit in the portal and click Go live next to your key. New keys always start in test mode. The key itself does not change.
Create a free account to create your key.
Authentication
Send your key with every request in the header Authorization: Bearer <key>. New keys start in test mode; you switch them live in the portal (API keys, Go live). The mode belongs to the key, not to its prefix. With a key in test mode letters are never printed, sent or charged; they get status received. A key is shown only once: store it as a server-side secret, never in browser code. A missing, invalid or revoked key returns 401 unauthorized.
Queue a flow eventPOST/api/v1/flow-events
For an active reminder flow and saved contact. Queues a one-time plan, not an immediate letter. Daily processing uses shared pricing and stop checks; insufficient credit retains the queue entry. event_key deduplicates per flow and key mode. Daily and recipient cooldown limits apply; test plans are admin-executed only and never send or charge.
| Field | Type | Required | Description |
|---|---|---|---|
flow_id | string | yes | Active flow id. |
contact_id | string | yes | Own saved contact id. |
event_key | string | yes | Unique event reference, 1 to 120 characters. |
order_cents | integer | Amount for condition, default 0. | |
variables | object | Required template fields only. |
Example request
curl -X POST https://envelopify.lovable.app/api/v1/flow-events \
-H "Authorization: Bearer test_JOUW_SLEUTEL" \
-H "Content-Type: application/json" \
-d '{
"flow_id": "b1d1a7f7-3db2-43df-a896-bc9dc10c6029",
"contact_id": "b1d1a7f7-3db2-43df-a896-bc9dc10c6029",
"event_key": "herinnering-104",
"variables": {}
}'{
"status": "queued",
"test": true
}Send a letterPOST/api/v1/letters
Creates a letter. Provide exactly one source: a PDF (pdf_base64 or pdf_url) or a template (template_id or template_code) with variables. PDFs follow the account default: a free address sheet or a white address box on page one using options.address_on_first_page. Small is up to 50 g, medium up to 100 g and large up to 350 g. Medium and large require a confirmed and enabled surcharge. The address sheet counts for weight, not the base price. Returns 201 with id, status, price with breakdown (price_breakdown), sheet count (address sheet included), latest post date for the chosen speed and test. With a test key the status is received and post_date is null. A rejected letter returns 422 with an error code and letter_id. Optional header Idempotency-Key.
| Field | Type | Required | Description |
|---|---|---|---|
options.address_on_first_page | boolean | For uploaded PDFs: fixed opaque white address window on page one, without an extra address sheet. Covered content is erased. Defaults to account preference (initially separate sheet). | |
options.corrections | object | PDF corrections: region {left,top,width,height} in mm on A4, window left/right/both, first_page, white_margins, flatten and a4_portrait. Omit to use the saved account rule. The old address region is whitened. The original is retained. | |
recipient.language | string | Optional: nl, fr, en or de. Selects a saved template language variant, otherwise the base text. Defaults by Belgian postcode. | |
recipient.company | string | Optional. Company name. | |
recipient.name | string | yes | Recipient name. Name or company is required. |
recipient.street | string | yes | Street. |
recipient.number | string | yes | House number. |
recipient.box | string | Optional. Box number. | |
recipient.postal_code | string | yes | Postal code. 4 digits in Belgium. |
recipient.city | string | yes | City. |
recipient.country | string | ISO country code (2 letters), default BE. All other EU countries too (e.g. NL, FR, DE), +€1,50 per letter, delivery 5 to 10 working days after dispatch, no express. | |
pdf_base64 | string | PDF as base64, A4, max 20 MB. | |
pdf_url | string | Direct public HTTPS URL of your PDF, max 20 MB. No redirects, credentials, private networks or nonstandard ports. | |
template_id | string | Template id, instead of a PDF. | |
template_code | string | Or the template code, e.g. herinnering. | |
variables | object | Values for the template fields, e.g. { "voornaam": "An" }. | |
options.flat | boolean | Unfolded in a large envelope, packed by hand. Only when activated. Surcharge from /price. | |
options.registered | boolean | Registered paper mail, BE and standard/express only, when activated. Tracking: registered_tracking, registered_handed_in_at and temporary registered_receipt_url. | |
options.color | boolean | Colour print. Default black and white. Surcharge per sheet via /price. | |
options.duplex | boolean | Double-sided: two pages per sheet. Default single-sided. | |
options.qr_tracking | boolean | Dynamic QR code with scan tracking, +€0,05 per letter. | |
options.speed | string | Speed: `standard` (handed to bpost within 3 working days), `express` (same working day when received before 12:00, +€0,15 per letter, minimum €15,00 per day) or `flex` (within 5 working days, €-0,05 per letter). Default `standard`. | |
options.qr_target_url | string | Required with qr_tracking. https URL the QR code redirects to. Can be changed later in the portal. | |
enclosure_batch_id | string | Received material id from the portal. One copy per letter after activation and receipt inspection. Pre-production cancellation restores stock. | |
reference | string | Optional. Your own reference, e.g. invoice number. | |
post_on | string | Optional hand-over day (YYYY-MM-DD), a weekday up to 90 calendar days ahead. Not a delivery date. Credit is reserved by charging on creation and refunded if cancelled before production. |
Example request
curl -X POST https://envelopify.lovable.app/api/v1/letters \
-H "Authorization: Bearer test_JOUW_SLEUTEL" \
-H "Idempotency-Key: factuur-2026-104" \
-H "Content-Type: application/json" \
-d '{
"recipient": {
"name": "An Peeters",
"street": "Meir",
"number": "12",
"postal_code": "2000",
"city": "Antwerpen"
},
"pdf_url": "https://envelopify.lovable.app/voorbeeld.pdf",
"options": {
"color": false
},
"reference": "factuur-2026-104"
}'{
"id": "b1d1a7f7-3db2-43df-a896-bc9dc10c6029",
"status": "scheduled",
"price_cents": 129,
"price_breakdown": {
"unit_price_cents": 129,
"tier": 1,
"tier_fixed": false,
"volume_30d": 120,
"letters_to_next_tier": 379,
"pages": 1,
"sheets": 1,
"address_sheet": true,
"total_sheets": 2,
"color": false,
"duplex": false,
"speed": "standard",
"destination": "BE",
"options": [],
"total_cents": 129,
"on_request": false,
"currency": "EUR",
"vat_included": false
},
"sheet_count": 2,
"post_date": "2026-11-13",
"free_trial": false,
"test": false
}Get a letterGET/api/v1/letters/{id}
Returns all letter fields, timestamps, the price breakdown, the number of QR scans (scan_count) and, once posted, a temporary link to the batch photo (batch_photo_url, valid 1 hour).
Example request
curl -X GET https://envelopify.lovable.app/api/v1/letters/b1d1a7f7-3db2-43df-a896-bc9dc10c6029 \
-H "Authorization: Bearer test_JOUW_SLEUTEL"{
"id": "b1d1a7f7-3db2-43df-a896-bc9dc10c6029",
"tracking_code": "B1D1A7F7",
"reference": "factuur-2026-104",
"status": "posted",
"test": false,
"reject_reason": null,
"return_reason": null,
"recipient": {
"company": null,
"name": "An Peeters",
"street": "Meir",
"number": "12",
"box": null,
"postal_code": "2000",
"city": "Antwerpen",
"country": "BE"
},
"source": "pdf",
"color": false,
"duplex": false,
"qr_tracking": false,
"sheet_count": 2,
"price_cents": 129,
"price_breakdown": {
"unit_price_cents": 129,
"tier": 1,
"tier_fixed": false,
"volume_30d": 120,
"letters_to_next_tier": 379,
"pages": 1,
"sheets": 1,
"address_sheet": true,
"total_sheets": 2,
"color": false,
"duplex": false,
"speed": "standard",
"destination": "BE",
"options": [],
"total_cents": 129,
"on_request": false,
"currency": "EUR",
"vat_included": false
},
"speed": "standard",
"post_deadline": "2026-11-13",
"created_at": "2026-11-12T10:02:11Z",
"scheduled_at": "2026-11-12T10:02:11Z",
"printed_at": "2026-11-13T09:40:00Z",
"posted_at": "2026-11-13T16:02:11Z",
"returned_at": null,
"post_date": "2026-11-13",
"scan_count": 0,
"batch_photo_url": null
}Download the PDFGET/api/v1/letters/{id}/pdf
Returns the final PDF exactly as we print it (application/pdf), including the address sheet. Only for your own letters. A rejected letter has no PDF and returns 404.
Example request
curl -X GET https://envelopify.lovable.app/api/v1/letters/b1d1a7f7-3db2-43df-a896-bc9dc10c6029/pdf \
-H "Authorization: Bearer test_JOUW_SLEUTEL"List lettersGET/api/v1/letters
Returns your most recent letters, newest first, in data. When there are more, pass next_cursor back as cursor.
| Filter | Type | Description | |
|---|---|---|---|
status | string | Filter by status, e.g. posted. | |
reference | string | Filter by reference. | |
from | string | From date, e.g. 2026-11-01. | |
to | string | Up to and including date. | |
limit | integer | Default 25, max 100. | |
cursor | string | Value of next_cursor. |
Example request
curl -X GET https://envelopify.lovable.app/api/v1/letters?status=posted&limit=50 \
-H "Authorization: Bearer test_JOUW_SLEUTEL"{
"data": [
{
"id": "b1d1a7f7-3db2-43df-a896-bc9dc10c6029",
"status": "posted",
"reference": "factuur-2026-104"
}
],
"next_cursor": "MjAyNi0xMS0xMlQxMDo..."
}Cancel a letterDELETE/api/v1/letters/{id}
Cancels a letter while the batch of that day is still open. The amount is credited back to your Envelopify balance, not paid out and the letter is returned with status rejected. Once the batch is closed you get 409 cutoff_passed.
Example request
curl -X DELETE https://envelopify.lovable.app/api/v1/letters/b1d1a7f7-3db2-43df-a896-bc9dc10c6029 \
-H "Authorization: Bearer test_JOUW_SLEUTEL"Price quotePOST/api/v1/price
Calculates the price of a letter with your current tier, without creating or charging anything. Same options as a letter, but with the number of printed pages instead of a PDF. address_sheet (default true) counts the free address sheet towards the sheet limit, like an uploaded PDF; set it to false for a template letter. Unavailable weight bands return 422 weight_class_unavailable. Use options.address_on_first_page to exclude the extra address sheet.
| Field | Type | Required | Description |
|---|---|---|---|
options.address_on_first_page | boolean | For uploaded PDFs: fixed opaque white address window on page one, without an extra address sheet. Covered content is erased. Defaults to account preference (initially separate sheet). | |
options.flat | boolean | Unfolded in a large envelope, packed by hand. Only when activated. Surcharge from /price. | |
options.registered | boolean | Registered paper mail, BE and standard/express only, when activated. Tracking: registered_tracking, registered_handed_in_at and temporary registered_receipt_url. | |
enclosure_batch_id | string | Received material id from the portal. One copy per letter after activation and receipt inspection. Pre-production cancellation restores stock. | |
pages | integer | yes | Number of printed pages (address sheet not included). |
address_sheet | boolean | Count the free address sheet towards the sheet limit. Default true. | |
options.country | string | Destination country, default BE. Another EU country adds the EU surcharge. | |
options.color | boolean | Colour, surcharge per sheet via /price. | |
options.duplex | boolean | Double-sided. | |
options.qr_tracking | boolean | QR code with scan tracking. | |
options.speed | string | standard, express or flex. |
Example request
curl -X POST https://envelopify.lovable.app/api/v1/price \
-H "Authorization: Bearer test_JOUW_SLEUTEL" \
-H "Content-Type: application/json" \
-d '{
"pages": 3,
"address_sheet": true,
"options": {
"color": false,
"duplex": false,
"qr_tracking": false,
"speed": "standard"
}
}'{
"price_breakdown": {
"unit_price_cents": 129,
"tier": 1,
"tier_fixed": false,
"volume_30d": 120,
"letters_to_next_tier": 379,
"pages": 3,
"sheets": 3,
"address_sheet": true,
"total_sheets": 4,
"color": false,
"duplex": false,
"speed": "standard",
"destination": "BE",
"options": [
{
"code": "extra_sheets",
"label": "Extra vellen (2)",
"amount_cents": 20
}
],
"total_cents": 149,
"on_request": false,
"currency": "EUR",
"vat_included": false
}
}List templatesGET/api/v1/templates
Returns your templates in data.
Example request
curl -X GET https://envelopify.lovable.app/api/v1/templates \
-H "Authorization: Bearer test_JOUW_SLEUTEL"{
"data": [
{
"id": "6c1f...",
"name": "Herinnering",
"code": "herinnering",
"body": "Beste {{voornaam}}, ...",
"created_at": "2026-11-12T10:00:00Z"
}
]
}Create a templatePOST/api/v1/templates
Creates a template. Fields go between double curly braces, e.g. {{voornaam}}. An existing code returns 409 conflict.
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Name. |
code | string | yes | Unique code: letters, digits, - and _. |
body | string | yes | Text with fields. |
Example request
curl -X POST https://envelopify.lovable.app/api/v1/templates \
-H "Authorization: Bearer test_JOUW_SLEUTEL" \
-H "Content-Type: application/json" \
-d '{
"name": "Herinnering",
"code": "herinnering",
"body": "Beste {{voornaam}},\n\nWe ontvingen nog geen betaling van {{bedrag}}."
}'Balance and pricesGET/api/v1/balance
Returns your credit, free trial letters left (free_letters_remaining), current tier, number of real letters in the past 30 days, letters to the next discount and the price list in cents, excl. VAT: price per letter per tier, option surcharges, speeds and the express rules.
Example request
curl -X GET https://envelopify.lovable.app/api/v1/balance \
-H "Authorization: Bearer test_JOUW_SLEUTEL"{
"credit_cents": 4950,
"free_letters_remaining": 3,
"company_verified": true,
"tier": 1,
"tier_fixed": false,
"volume_30d": 120,
"letters_to_next_tier": 380,
"unit_price_cents": 129,
"tiers": [
{
"tier": 1,
"from": 0,
"unit_price_cents": 129
},
{
"tier": 2,
"from": 500,
"unit_price_cents": 109
},
{
"tier": 3,
"from": 5000,
"unit_price_cents": 99
}
],
"options": {
"extra_sheet_cents": 10,
"qr_tracking_cents": 5
},
"speeds": [
{
"speed": "standard",
"name": "Standaard",
"amount_cents": 0,
"working_days": 3
},
{
"speed": "express",
"name": "Express",
"amount_cents": 15,
"working_days": 0
},
{
"speed": "flex",
"name": "Flex",
"amount_cents": -5,
"working_days": 5
}
],
"express": {
"minimum_cents": 1500,
"max_letters_per_day": 1000,
"before_hour": 12
},
"heavy_letter": {
"from_grams": 50,
"amount_cents": 60
},
"eu": {
"surcharge_cents": 150,
"countries": [
"AT",
"DE",
"FR",
"NL",
"..."
],
"delivery": "5 tot 10 werkdagen na verzending",
"express": false
},
"daily_letter_max": 1000,
"max_sheets": 8,
"currency": "EUR",
"vat_included": false
}Your account and company checkGET/api/v1/account
Returns your own account details. company_verified is true when your VAT number has been checked with the EU VAT check (VIES) or manually by us. verification_status is verified, none, pending (check will follow later) or invalid. For a verified company, official_name and official_address come from the official register. GET /balance also includes company_verified. You cannot change the status through the API: it only changes after a real check in the portal.
Example request
curl -X GET https://envelopify.lovable.app/api/v1/account \
-H "Authorization: Bearer test_JOUW_SLEUTEL"{
"customer_id": "4f6c2a1e-...",
"company": "Jouw Bedrijf",
"email": "post@jouwbedrijf.be",
"company_verified": true,
"verification_status": "verified",
"vat_number": "BE0123456749",
"official_name": "Jouw Bedrijf BV",
"official_address": "Kerkstraat 1\n2000 Antwerpen",
"checked_at": "2026-11-12T10:00:00Z",
"free_letters_remaining": 3
}List webhooksGET/api/v1/webhooks
Lists your registered URLs and notification_groups. null means all existing events; an array limits notifications to selected groups.
Example request
curl -X GET https://envelopify.lovable.app/api/v1/webhooks \
-H "Authorization: Bearer test_JOUW_SLEUTEL"{
"data": [
{
"id": "a3c2...",
"url": "https://jouwsite.be/hooks/envelopify",
"active": true,
"created_at": "2026-11-12T10:00:00Z"
}
]
}Create a webhookPOST/api/v1/webhooks
Registers an https URL. The response contains the secret once; use it to verify signatures.
| Field | Type | Required | Description |
|---|---|---|---|
url | string | yes | https URL that receives our POST. |
notification_groups | array | Optional: problems (review/rejected), sent (posted/registered_handed_in), undeliverable (returned), delivered (coming soon, no events yet). Omit to preserve all existing events; [] receives nothing. Editable per URL in the portal. |
Example request
curl -X POST https://envelopify.lovable.app/api/v1/webhooks \
-H "Authorization: Bearer test_JOUW_SLEUTEL" \
-H "Content-Type: application/json" \
-d '{
"url": "https://jouwsite.be/hooks/envelopify"
}'{
"id": "a3c2...",
"url": "https://jouwsite.be/hooks/envelopify",
"active": true,
"created_at": "2026-11-12T10:00:00Z",
"secret": "whsec_..."
}Delete a webhookDELETE/api/v1/webhooks/{id}
Deletes a webhook.
Example request
curl -X DELETE https://envelopify.lovable.app/api/v1/webhooks/a3c2... \
-H "Authorization: Bearer test_JOUW_SLEUTEL"{
"id": "a3c2...",
"deleted": true
}Webhooks
On every event we POST { event, letter_id, status, occurred_at, letter } to all your active webhook URLs. Each request carries the header x-envelopify-signature: the hex HMAC-SHA256 of the raw body with your secret. Verify it before trusting the payload and answer with a 2xx status. Failed deliveries (no 2xx or no answer within 8 seconds) are retried up to 3 times (on your next event and on every production round), so 4 attempts in total. Test letters stay received and send no webhooks.
| Event | When |
|---|---|
letter.scheduled | Scheduled for a post day, credit charged |
letter.review | Address under review: we check it before printing |
letter.checked | Address approved after review, scheduled |
letter.rejected | Rejected after review or cancelled before production, credit refunded |
letter.printed | Printed and stamped |
letter.posted | Handed to bpost |
letter.returned | Returned as undeliverable |
letter.registered_handed_in | Registered hand-over with tracking and proof |
letter.scanned | QR code scanned |
{
"event": "letter.posted",
"letter_id": "b1d1a7f7-3db2-43df-a896-bc9dc10c6029",
"status": "posted",
"occurred_at": "2026-11-13T16:02:12Z",
"letter": {
"id": "b1d1a7f7-3db2-43df-a896-bc9dc10c6029",
"tracking_code": "B1D1A7F7",
"reference": "factuur-2026-104",
"status": "posted",
"posted_at": "2026-11-13T16:02:11Z"
},
"created_at": "2026-11-13T16:02:12Z"
}Verify the signature:
// Node 18+ with Express: verify a webhook signature
import crypto from "node:crypto";
import express from "express";
const SECRET = "whsec_JOUW_GEHEIM"; // returned once when you create the webhook
const app = express();
app.post("/hooks/envelopify", express.raw({ type: "application/json" }), (req, res) => {
const expected = crypto.createHmac("sha256", SECRET).update(req.body).digest("hex");
const given = req.get("x-envelopify-signature") ?? "";
const ok = given.length === expected.length && crypto.timingSafeEqual(Buffer.from(given), Buffer.from(expected));
if (!ok) return res.status(401).end();
const { event, letter } = JSON.parse(req.body.toString("utf8"));
console.log(event, letter.id, letter.status);
res.status(200).end();
});
app.listen(3000);Idempotency
Send an Idempotency-Key header, e.g. your invoice number. Sending the same key again within 24 hours returns the existing letter (status 200) instead of creating a new one, and nothing extra is charged. After 24 hours the key counts as new.
Statuses
| Status | Meaning |
|---|---|
received | Received. Test letters stay in this status. |
scheduled | Scheduled for a post day, credit charged. |
review | Under review: the postal code does not exist or does not match the municipality. We approve or reject the address (credit refunded). See address_warning. |
printed | Printed and stamped. |
posted | Handed to bpost. |
returned | Returned as undeliverable. |
rejected | Rejected or cancelled. The reason is in reject_reason. |
Error codes
Errors always have the shape { error: { code, message } }. The code (error.code) is always the same, whatever the language: use it in your logic. The message (error.message) is in Dutch and can be shown to your user as is.
| Code | HTTP | Meaning |
|---|---|---|
sender_not_verified | 422 | Real shipments, including free postal trials, require a confirmed email and verified company. Test mode remains available. |
weight_class_unavailable | 422 | Weight band is not enabled or exceeds 350 grams. |
service_unavailable | 422 | The selected service or received enclosure stock is not available. |
invalid_post_date | 422 | The posting day is not a valid weekday within 90 days. |
address_incomplete | 422 | An address field is missing or the postal code is invalid. |
insufficient_balance | 422 | Not enough credit. |
pdf_invalid | 422 | The PDF cannot be opened, is not a PDF or exceeds 20 MB. |
too_many_sheets | 422 | More than 8 sheets, address sheet included. |
express_limit | 422 | More than 1000 express letters on one day. |
country_not_supported | 422 | Country outside Belgium and the EU. |
express_not_available | 422 | Express is not available to other EU countries. |
daily_limit | 422 | Daily letter maximum reached (default 1000, can be raised on request). |
template_invalid | 422 | Template not found or fields are missing. |
invalid_request | 400 | Invalid request, e.g. no source or an invalid QR URL. |
unauthorized | 401 | Key missing, invalid or revoked. |
not_found | 404 | Letter, webhook or endpoint does not exist. Also without a key. |
cutoff_passed | 409 | Too late to cancel, the letter is in production. |
not_cancellable | 409 | Cannot cancel, e.g. already rejected or cancelled. |
conflict | 409 | A template with this code already exists. |
rate_limited | 429 | More than 60 requests per minute. See the Retry-After header. |
server_error | 500 | Something went wrong on our side. |
{
"error": {
"code": "address_incomplete",
"message": "Een Belgische postcode bestaat uit 4 cijfers"
}
}Limits
Max 60 requests per minute per key; above that you get 429 rate_limited with a Retry-After header. PDFs up to 20 MB, letters up to 350 g, medium and large only when enabled, max 1000 express letters per day and by default max 1000 letters per day per account. error.code is language-independent and stable; message is in Dutch. CORS is open, but never use your key in browser code.
Pricing
One price per letter, excl. VAT, everything included: 1 sheet (single or double-sided, black and white), envelope, real stamps and posting. By your real letters in a rolling 30-day window, including the new letter (no monthly reset): €1,29 under 500, €1,09 from 500, €0,99 from 5000. Colour: +€0,08 per sheet. Double-sided has no surcharge. Extra sheet: +€0,10 per sheet after the first (double-sided is two pages per sheet). QR code with scan tracking: +€0,05. The address sheet is free and not counted for the price. A standard letter holds up to 8 sheets in total, address sheet included; larger letters on request. Speeds (options.speed): standard (handed to bpost within 3 working days), express (+€0,15, same working day when received before 12:00 Europe/Brussels, otherwise the next working day) and flex (€-0,05, within 5 working days). bpost delivers within 2 to 3 working days after hand-over. Express minimum via the API: if your express letters of one day total less than €15,00 at 12:00, the difference is charged as a ledger line "Aanvulling minimum express"; your credit must cover it when creating the letter. Max 1000 express letters per day (422 express_limit). Examples, tier 1: 1 sheet €1,29; 1 sheet colour €1,37; 3 sheets €1,49; 4 pages double-sided (2 sheets) €1,39; with QR €1,34; express €1,44; flex €1,24. Quotes are estimates for the next real letter; simultaneous orders recheck their tariff atomically. Test letters never count. Every response with a price contains price_breakdown with unit_price_cents, tier, volume_30d, letters_to_next_tier, sheets, total_sheets, speed, options (list of code, label, amount_cents) and total_cents. Prices can change; read them from GET /balance or POST /price. Free trial letters: every new account gets 3 real letters for free, also through the API with your key in live mode. Conditions: speed standard, at most 3 content sheets, confirmed e-mail address and at most 3 trial letters per recipient address. A letter that does not qualify is paid from your credit as usual. A trial letter has free_trial: true and price_cents: 0; cancelling before production gives it back. GET /balance shows free_letters_remaining. Heavy letter: above 50 g we add +€0,60 per letter (option heavy_letter). Other EU countries: +€1,50 per letter (option eu), delivery 5 to 10 working days after dispatch, no express. Address check: for a Belgian address we check that the postal code exists and matches the municipality. When in doubt the letter gets status review with address_warning and we approve or reject it (credit refunded).
All rates and the calculator are on the pricing page.