Quickstart
Send your first registered letter in three calls: upload a PDF, create the letter, follow its events. Every example on this page uses a test key, so nothing is printed or charged. Base URL: https://api.sendregistered.com/v1.
sr_test_ run the full flow with test credit and return a sample proof PDF. Swap in your sr_live_ key when you go live; nothing else changes.Create a test key
In your account under API keys, choose New key, mode Test and the scopes
readandwrite. Copy the key now: we show it once.Upload the PDF
Send an A4 PDF up to 8 MB. We check that the address sits in the envelope window and that every page can be printed. You get a document id back.
Create and pay the letter
Pass the document id, the product code and the recipient. With
"pay": truethe letter is paid from your credit in the same call. Send anIdempotency-Keyso a retry never sends twice.Follow it
Add a webhook endpoint in your account. You get
letter.printed,letter.posted,letter.awaiting_pickupandletter.delivered, each signed withSendRegistered-Signature. The proof PDF is ready once the letter is posted.
curl https://api.sendregistered.com/v1/documents \
-H "Authorization: Bearer sr_test_4f2a9Kd7Lq" \
-H "Idempotency-Key: doc-2026-4471" \
-H "Content-Type: application/json" \
-d '{
"filename": "reminder-4471.pdf",
"pdf_base64": "JVBERi0xLjQK..."
}'{
"id": "doc_8Kq2mZ",
"pages": 2,
"sha256": "6e340b9cffb37a989ca544e6bb780a2c...",
"fit": { "ok": true, "warnings": [] }
}curl https://api.sendregistered.com/v1/letters \
-H "Authorization: Bearer sr_test_4f2a9Kd7Lq" \
-H "Idempotency-Key: claim-2026-4471" \
-H "Content-Type: application/json" \
-d '{
"document_id": "doc_8Kq2mZ",
"destination": "DE",
"product_id": "de-einwurf",
"sender": {
"name": "Nordlicht Legal GmbH",
"street": "Friedrichstraße 114",
"postal_code": "10117",
"city": "Berlin",
"country": "DE"
},
"recipient": {
"name": "Hausverwaltung Berger GmbH",
"street": "Kastanienallee 12",
"postal_code": "10435",
"city": "Berlin",
"country": "DE"
},
"pay": true,
"terms": true
}'{
"id": "SR-TEST-2026-00001",
"test": true,
"status": "received",
"destination": "DE",
"product_id": "de-einwurf",
"pages": 2,
"currency": "EUR",
"amount": 795,
"credit_amount": 795,
"tracking_number": "RRTEST000001DE"
}{
"id": "evt_9xL2mK7pQ1vB",
"type": "letter.delivered",
"created_at": "2026-11-10T11:21:04Z",
"livemode": false,
"data": {
"id": "SR-TEST-2026-00001",
"status": "delivered",
"tracking_number": "RRTEST000001DE",
"proof_url": "https://api.sendregistered.com/v1/letters/SR-TEST-2026-00001/proof"
}
}Authentication, idempotency, limits & webhooks
Pass your API key in the Authorization: Bearer sr_live_... (or sr_test_...) header on every call to https://api.sendregistered.com/v1.
Test and live keys
Keys prefixed with sr_test_ run in an isolated sandbox with €1,000 of test credit, deterministic tracking numbers, sample proof PDFs, and test-only webhooks (livemode: false). Live keys (sr_live_) require a verified business account and debit your prepaid EUR credit balance.
Idempotency
Every write call (POST /v1/documents, POST /v1/letters, POST /v1/letters/{id}/pay, POST /v1/letters/{id}/cancel) requires an Idempotency-Key header (8–200 characters), kept for 24 hours. Repeating the same key with the same body replays the original response; reusing a key with a different payload returns 409 idempotency_conflict.
Spending limits
Each API key and each connected MCP assistant has its own optional daily EUR limit and monthly EUR limit in API keys. When a letter would exceed either limit, the API returns 409 limit_exceeded before touching your credit balance.
Webhooks & signature verification
Register HTTPS endpoints in API keys for: letter.paid, letter.printed, letter.posted, letter.awaiting_pickup, letter.delivered, letter.failed, letter.cancelled, balance.low. Every delivery carries a SendRegistered-Signature: t=<unix_ts>,v1=<hmac_sha256> header signed with the endpoint's whsec_... secret over "<t>.<raw_body>". Reject timestamps older than 5 minutes (300 seconds). Failed deliveries retry with exponential backoff for up to 3 days (72 hours).
import { createHmac, timingSafeEqual } from "node:crypto";
export function verifyWebhook(rawBody, header, secret) {
const parts = Object.fromEntries(
header.split(",").map((p) => p.split("="))
);
const age = Math.abs(Date.now() / 1000 - Number(parts.t));
if (!parts.t || !parts.v1 || age > 300) return false;
const expected = createHmac("sha256", secret)
.update(`${parts.t}.${rawBody}`)
.digest("hex");
return timingSafeEqual(
Buffer.from(parts.v1, "hex"),
Buffer.from(expected, "hex")
);
}Create a letter
Creates a registered letter from an uploaded document. With pay set to true the letter is paid from your credit and goes to print on the next posting run. Needs the write scope.
Body (from OpenAPI 3.1 schema)
Two-letter destination country code (DE, FR, NL, BE, ES, GB, CH, US).
DEFRBEESCHNLGBUSProduct code from GET /v1/destinations, such as de-einwurf or fr-lrar. Defaults to the country's primary registered product.
Print in colour. Default false.
Id of a document from POST /v1/documents. A document is used for one letter.
Return address printed on the letter.
Person or company, up to 2 lines.
Street and house number.
City or town.
Two-letter ISO country code.
State or province (required for US).
The address printed in the envelope window. We check that it is complete for the country.
Person or company, up to 2 lines.
Street and house number.
City or town.
Two-letter ISO country code.
State or province (required for US).
Accept the terms of service for this registered letter (must be true when paying).
Pay from your credit now. Default true. Use false to create the letter and pay later with POST /v1/letters/{id}/pay.
Headers
Any unique string (8–200 characters), kept for 24 hours. The same key with the same body returns the first result; a different body returns 409.
Returns
The letter object with status received when paid, or awaiting_confirmation / awaiting_payment when pay is false. Prices are in cents.
Errors
| Code | What to do |
|---|---|
insufficient_credit | Top up your credit, or create the letter with "pay": false. |
limit_exceeded | The key reached its daily or monthly limit. Raise it in your account. |
invalid_request | Complete the address or body; the message names the field. |
fit_confirmation_required | The address is close to the window edge. Pass "confirm_fit": true or upload a new PDF. |
credit_not_available_for_currency | Letters to Switzerland (CHF) are paid by card. Use the checkout flow for CHF orders. |
curl https://api.sendregistered.com/v1/letters \
-H "Authorization: Bearer sr_test_4f2a9Kd7Lq" \
-H "Idempotency-Key: claim-2026-4471" \
-H "Content-Type: application/json" \
-d '{
"document_id": "doc_8Kq2mZ",
"destination": "DE",
"product_id": "de-einwurf",
"sender": {
"name": "Nordlicht Legal GmbH",
"street": "Friedrichstraße 114",
"postal_code": "10117",
"city": "Berlin",
"country": "DE"
},
"recipient": {
"name": "Hausverwaltung Berger GmbH",
"street": "Kastanienallee 12",
"postal_code": "10435",
"city": "Berlin",
"country": "DE"
},
"pay": true,
"terms": true
}'{
"id": "SR-TEST-2026-00001",
"test": true,
"status": "received",
"destination": "DE",
"product_id": "de-einwurf",
"pages": 2,
"currency": "EUR",
"amount": 795,
"credit_amount": 795,
"tracking_number": "RRTEST000001DE"
}{
"error": {
"code": "limit_exceeded",
"message": "Daily spending limit for this API key has been reached.",
"doc_url": "https://sendregistered.com/developers/api#errors"
}
}List destinations and products
Returns all 8 supported destination countries, available postal products, carrier names and prices in cents.
curl https://api.sendregistered.com/v1/destinations \ -H "Authorization: Bearer sr_test_4f2a9Kd7Lq"
Calculate a letter quote
Quotes a letter in cents for the authenticated business account, applying EU reverse charge or non-EU VAT rules when configured on the account.
Body
Two-letter destination country code (DE, FR, NL, BE, ES, GB, CH, US).
DEFRBEESCHNLGBUSProduct code from GET /v1/destinations, such as de-einwurf or fr-lrar. Defaults to the country's primary registered product.
Print in colour. Default false.
Number of A4 pages in the PDF (1 to 20).
curl -X POST https://api.sendregistered.com/v1/quotes \ -H "Authorization: Bearer sr_test_4f2a9Kd7Lq"
Get prepaid credit balance
Returns the account's prepaid credit balance in EUR cents (`paid`, `bonus`, `subscription`, `total` and active `lots`).
curl https://api.sendregistered.com/v1/balance \ -H "Authorization: Bearer sr_test_4f2a9Kd7Lq"
Upload a PDF document
Uploads a base64-encoded PDF (up to 8 MB, 1–20 pages) and checks whether the pages fit the printable area and envelope window.
Body
Original PDF filename (must end with .pdf).
curl -X POST https://api.sendregistered.com/v1/documents \ -H "Authorization: Bearer sr_test_4f2a9Kd7Lq"
List recent letters
Returns up to 100 letters created through the API in the key's mode (`live` or `test`).
curl https://api.sendregistered.com/v1/letters \ -H "Authorization: Bearer sr_test_4f2a9Kd7Lq"
Get a letter
Returns status, price, timestamps and tracking number for one letter.
curl https://api.sendregistered.com/v1/letters/{id} \
-H "Authorization: Bearer sr_test_4f2a9Kd7Lq"Pay an unpaid letter from credit
Pays an existing `awaiting_confirmation` or `awaiting_payment` letter from prepaid credit within the API key's spending limit.
Body
Accept the terms of service for this registered letter (must be true when paying).
curl -X POST https://api.sendregistered.com/v1/letters/{id}/pay \
-H "Authorization: Bearer sr_test_4f2a9Kd7Lq"Cancel a letter before printing
Cancels an unpaid draft or withdraws a paid letter before it has been printed, returning credit to the balance.
curl -X POST https://api.sendregistered.com/v1/letters/{id}/cancel \
-H "Authorization: Bearer sr_test_4f2a9Kd7Lq"Get proof certificate facts or PDF
Returns proof hashes, timestamps and tracking JSON, or streams the proof certificate PDF when `?format=pdf` or `Accept: application/pdf` is passed.
curl https://api.sendregistered.com/v1/letters/{id}/proof \
-H "Authorization: Bearer sr_test_4f2a9Kd7Lq"