API v1

Letter API documentation (REST, v1)

Our RESTful JSON API lets you send letters straight from your application. Use the test environment with free sandbox keys before going to production. The full OpenAPI/Swagger documentation is at the end of this page.

Getting started

Three steps to your first API letter

1

Create an account

Sign up for free on briefsenden.com and confirm your email address. With a test key, letters are then neither printed nor charged.

2

Create an API key

In the dashboard go to Settings → Developer and create an API key. For development use a test key (bv_test_*), for production a live key (bv_live_*).

3

Send your first letter

Send a PDF with the recipient address via POST to /api/v1/letters/simple. With a test key no physical letter is sent and there is no cost.

Authentication

API keys and the X-Api-Key header

The briefsenden API authenticates every request with an API key. Send it in the X-Api-Key header to the base URL https://www.briefsenden.com/api/v1. All requests must use HTTPS – HTTP requests are rejected.

HTTP Header
X-Api-Key: bv_live_YourApiKeyHere

Test keys bv_test_*

For development and testing. Letters are simulated but not physically sent. No cost, no coin deduction – ideal for your development environment.

Live keys bv_live_*

For production. Letters are printed, enveloped and sent via Deutsche Post. The cost is deducted from your coin balance.

You can create, view and revoke API keys at any time in the dashboard under Settings → Developer. Keep your live keys safe and do not share them publicly.

Code examples

Send a letter in your preferred language

curl -X POST https://www.briefsenden.com/api/v1/letters/simple \
  -H "X-Api-Key: bv_test_xxx" \
  -F "file=@invoice.pdf" \
  -F "recipientLine1=Max Mustermann" \
  -F "recipientLine2=Musterstraße 1" \
  -F "recipientZip=12345" \
  -F "recipientCity=Berlin" \
  -F "color=bw" \
  -F "duplex=simplex"

All examples send a letter via POST /api/v1/letters/simple as multipart/form-data. Replace bv_test_xxx with your API key – a test key costs nothing.

Endpoint overview

The most important API endpoints at a glance

MethodEndpointDescription
POST/api/v1/letters/simpleSend a letter (PDF file or PDF URL)
POST/api/v1/lettersSend a letter (JSON, PDF as Base64)
POST/api/v1/letters/bulkUp to 50 letters in one request
GET/api/v1/lettersList letters
GET/api/v1/letters/:idGet a letter’s status (with tracking number for registered mail)
POST/api/v1/estimateCalculate the cost of a letter
POST/api/v1/serial/estimateCalculate the cost of a mail merge
GET/api/v1/balanceQuery the balance
GET/api/v1/serialList mail merges
GET/api/v1/serial/:idGet a mail merge’s status
GET/api/v1/serial/:id/lettersList the letters of a mail merge
DELETE/api/v1/serial/:idCancel a mail merge while it is being processed
GET/api/v1/sender-profilesList sender profiles (also POST, PUT, DELETE, set-default)
GET/api/v1/addressesList the address book (also POST, PUT, DELETE)
POST/api/v1/webhooksRegister a webhook
GET/api/v1/webhooksList webhooks
DELETE/api/v1/webhooks/:idDelete a webhook

The full documentation of all endpoints with request and response schemas is in the interactive Swagger UI below.

Pricing, use cases and a quick start for the API: letter and post API for Germany at a glance.

Webhooks

Real-time notifications on status changes

Webhooks notify your application automatically when a letter’s status changes. Set your webhook URL in the dashboard under Settings → Developer. We send a POST request to your URL on every status change.

Event types

  • letter.created
  • letter.uploaded
  • letter.processing
  • letter.in_print
  • letter.sent
  • letter.error
  • serial.completed
  • serial.error

Retry logic

If your server does not respond with a 2xx status code, we retry delivery up to 3 times after 1, 5 and 25 seconds. After 10 consecutive failed deliveries the webhook is deactivated; create it again then.

Payload example

JSON
{
  "event_type": "letter.sent",
  "timestamp": "2026-10-16T14:30:00.1234567Z",
  "data": {
    "LetterId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "Status": "SENT",
    "OldStatus": "IN_PRINT",
    "TrackingNumber": null
  }
}

TrackingNumber contains the tracking number for registered mail once it has been assigned (from handover to the print centre), otherwise null.

Verify the signature

Every delivery carries three headers. Compute the signature over the raw body and compare it in constant time with X-Webhook-Signature. You receive the secret once when creating the webhook.

X-Webhook-Signature
sha256= followed by the HMAC-SHA256 (hex, lower case) over the raw request body; the key is your webhook secret as a UTF-8 string
X-Webhook-Event
Event type, e.g. letter.sent (same as event_type in the body)
X-Webhook-Timestamp
Time of the delivery attempt (ISO 8601, UTC); not part of the signature
Node.js
const crypto = require('crypto')

// rawBody: the unmodified request body (Buffer), not re-serialised
function isValidSignature(rawBody, signatureHeader, secret) {
  const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(rawBody).digest('hex')
  const a = Buffer.from(expected)
  const b = Buffer.from(signatureHeader || '')
  return a.length === b.length && crypto.timingSafeEqual(a, b)
}

Rate limits & error handling

Limits, status codes and error format

Rate limits

Each API key has its own rate limit: one request can be processed immediately, then another request is released every 500 ms. Up to 10 further requests wait in a queue and are processed in order of arrival. If the queue is full, further requests receive a 429 status code. Wait briefly before retrying.

Idempotency keys

For POST requests you can send an Idempotency-Key header. Successful responses are stored for 24 hours per API key and idempotency key. Repeat the same job with the same key only after the first request has finished; parallel requests are not blocked by it.

Idempotency-Key: unique-request-id-123

Error format (RFC 7807 ProblemDetails)

All errors are returned in the standard RFC 7807 format (ProblemDetails). Validation errors name the affected field under errors; other errors describe the problem in detail. The traceId helps our support find the request. The messages themselves are currently in German, as in this real response:

JSON – error response
{
  "type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "errors": {
    "pdfUrl": ["pdfUrl muss eine gültige HTTP(S)-URL sein."]
  },
  "traceId": "de4f753c1a2f48115a926dd3081be05e"
}

HTTP status codes

CodeMeaning
200OK – request successful
201Created – resource created (e.g. a letter)
400Bad Request – invalid parameters
401Unauthorized – missing or invalid API key
403Forbidden – no permission
404Not Found – resource not found
429Too Many Requests – rate limit exceeded
500Internal Server Error – server error

API questions

Answers to the most important developer questions

Interactive API documentation

Test endpoints directly in the Swagger UI. The OpenAPI descriptions are in German.

Free test mode – no credit card needed

Integrate now

Start with a few lines of code and automate your letter sending via the REST API.

No monthly fee No minimum order Pay per letter