EchtPost API v2 — AI consumer guide

This guide is written for LLM-based clients and developers wiring up integrations. Endpoints, error shapes, and examples are kept as copy-paste-ready as possible.

Base URL

https://api.echtpost.de/v2

Authentication

The API accepts two credential types — both sent in a header. Query-string parameters such as ?apikey= are not accepted on v2.

Use OAuth when the integration acts on behalf of a real EchtPost user (Claude, ChatGPT, third-party apps with a UI). Tokens are short-lived, scoped to the user's chosen account, and can be revoked from the user's side without touching API keys.

Discovery URLs:

https://api.echtpost.de/.well-known/oauth-protected-resource   # RFC 9728
https://my.echtpost.de/.well-known/oauth-authorization-server  # RFC 8414

A 401 response from any v2 endpoint sets WWW-Authenticate: Bearer resource_metadata="https://api.echtpost.de/.well-known/oauth-protected-resource", which points at the protected-resource document. That document declares https://my.echtpost.de as the authorization server; clients then walk the RFC 8414 metadata there to find the authorize / token / register endpoints. Tokens must include the api scope to be accepted on the REST API. A token scoped only for mcp will be rejected here (and vice versa on the MCP server). Request scope=mcp api if you need both.

The flow is OAuth 2.1 Authorization Code with PKCE. Once you have an access token:

curl -sS -H "Authorization: Bearer $ECHTPOST_OAUTH_TOKEN" \
  "https://api.echtpost.de/v2/me"

Dynamic client registration (RFC 7591) is supported at POST https://my.echtpost.de/oauth/register if you don't want to manually register a client.

Alternative: API key (server-to-server)

Use an API key for server-side scripts and automations where there is no end-user to consent. Create keys in the EchtPost web app under Account → API keys.

Supported headers (in order of precedence):

  1. X-Api-Key: <your key>
  2. Authorization: Bearer <your key>

V1's X-apikey header is not accepted on v2.

curl -sS -H "X-Api-Key: $ECHTPOST_API_KEY" \
  "https://api.echtpost.de/v2/me"

Rate limits

Requests are throttled per API key. Exceeding a limit returns 429 Too Many Requests with a Retry-After header (seconds until the window resets).

Scope Limit
All endpoints 100 requests / minute
POST /v2/cards 10 requests / minute
POST /v2/cards/from_template 10 requests / minute
POST /v2/cards/preview_fit 30 requests / minute

Spread bulk card creation over time or batch recipients into a single card request using recipient_ids to stay within limits.

Error format (RFC 7807)

Errors use Content-Type: application/problem+json. Each response includes:

Example:

{
  "type": "https://api.echtpost.de/v2/errors/text-does-not-fit",
  "title": "Message does not fit on the card",
  "status": 422,
  "detail": "Shorten the message or reduce the font size; 1 line overflows.",
  "instance": "/v2/cards",
  "request_id": "req-abc123",
  "errors": [
    { "path": "message", "code": "overflow", "message": "line 17 overflows" }
  ]
}

Retry guidance

detail is phrased imperatively — it tells you exactly what to change before retrying. Treat 422 responses as recoverable after applying the fix described in detail. 401 is terminal (bad key). 402 means top-up required. 409 on card cancel means the state transition is no longer possible.

Pagination

List endpoints return a JSON array. Read pagination from response headers:

Request parameters:

List filters (query parameters)

Use top-level query parameters only. v2 does not support nested filter[...].

Endpoint Parameters
GET /v2/contacts country_code, group_id, external_id, search, plus sorting
GET /v2/cards status (pending, scheduled, sent, canceled; comma-separated allowed), deliver_at, motive_id, plus sorting
GET /v2/motives search (name, description, content, searchterms), plus sorting
GET /v2/groups sorting only
GET /v2/templates source (defaults to regular)

List sorting

List endpoints accept optional sort (one allowed field name) and direction (asc or desc, case-insensitive). Omitted values use the defaults below. Results always include a stable tie-breaker on the row id ascending.

Unknown sort or invalid direction (when direction is sent) returns 422 with RFC 7807 application/problem+json.

Endpoint Default sort Default direction Allowed sort values
GET /v2/contacts created_at desc last_name, created_at, updated_at
GET /v2/cards created_at desc created_at, deliver_at
GET /v2/motives priority¹ name, created_at
GET /v2/groups name asc name, created_at, updated_at

¹ When no sort parameter is given, motives are ordered by relevance: account-specific motives first, then highlighted, then by popularity.

Card status in JSON

Card responses expose four statuses: pending (draft through uploaded), scheduled, sent, canceled (includes canceling). Internal workflow states are not exposed in the API.

Fonts

Three families, each with its own valid size range:

family valid sizes
architects_daughter 12..14
reenie_beanie 15..17
special_elite 11..13

Colors: black, blue, or a hex value.


Line breaks in card content

Card content is plain text. Use \n (Unix newline) as the line-break character in your JSON strings — the API normalizes it to the internal \r\n format used by the print renderer. Sending \r\n directly is also accepted.

A card with no line breaks at all will render as one unbroken text block. The create endpoint returns a warnings array in the 201 response with a no_line_breaks entry if this is detected — treat it as a hint to add paragraph breaks before submitting.

Quick flow — one card in three calls

  1. GET /v2/me — confirm balance and account metadata.
  2. POST /v2/cards/preview_fit — iterate content + font until fits: true.
  3. POST /v2/cards — create the mailing with recipient_ids or inline recipients.

Step 1 — check balance

curl -sS -H "X-Api-Key: $ECHTPOST_API_KEY" \
  "https://api.echtpost.de/v2/me"

Step 2 — check that the text fits

curl -sS -H "X-Api-Key: $ECHTPOST_API_KEY" \
     -H "Content-Type: application/json" \
     -X POST "https://api.echtpost.de/v2/cards/preview_fit" \
     -d '{
       "message": "Liebe Grüße aus Berlin,\ndas Team",
       "font": { "family": "architects_daughter", "size": 13 }
     }'

Response on success:

{ "fits": true, "lines_used": 2, "max_lines": 16 }

On overflow the response includes overflow_line and a suggested_font_size that would make the text fit:

{
  "fits": false,
  "lines_used": 18,
  "max_lines": 16,
  "overflow_line": 17,
  "suggested_font_size": 11
}

Step 3 — create the card

curl -sS -H "X-Api-Key: $ECHTPOST_API_KEY" \
     -H "Content-Type: application/json" \
     -X POST "https://api.echtpost.de/v2/cards" \
     -d '{
       "motive_id": 42,
       "content": "{anrede},\nwir wünschen frohe Ostern.\nHerzlich, das Team",
       "font": { "family": "architects_daughter", "size": 13 },
       "recipient_ids": [101, 102]
     }'

The {anrede} token is substituted per recipient at render time with the recipient's salutation (e.g. "Sehr geehrte Frau Müller" or "Liebe Jana"). Use it anywhere in content, content_ps, or content_vertical. The canonical form is {anrede} (lowercase); casing is ignored at render time.


Endpoint quick reference

GET /v2/me

Account, user, API key, balance.

curl -sS -H "X-Api-Key: $KEY" "https://api.echtpost.de/v2/me"

GET /v2/credits

Balance plus current prices:

curl -sS -H "X-Api-Key: $KEY" "https://api.echtpost.de/v2/credits"

Contacts

# list with filter + sort
curl -sS -H "X-Api-Key: $KEY" \
  "https://api.echtpost.de/v2/contacts?country_code=de&sort=last_name&direction=asc"

# create
curl -sS -H "X-Api-Key: $KEY" -H "Content-Type: application/json" \
     -X POST "https://api.echtpost.de/v2/contacts" \
     -d '{
       "last_name":"Doe","first_name":"Jane",
       "street":"Musterstr. 1","zip":"10115","city":"Berlin","country_code":"de",
       "greeting_style":"formal"
     }'

# update
curl -sS -H "X-Api-Key: $KEY" -H "Content-Type: application/json" \
     -X PATCH "https://api.echtpost.de/v2/contacts/42" \
     -d '{ "city":"München" }'

# delete
curl -sS -H "X-Api-Key: $KEY" -X DELETE "https://api.echtpost.de/v2/contacts/42"

Groups

# list
curl -sS -H "X-Api-Key: $KEY" "https://api.echtpost.de/v2/groups"

# create
curl -sS -H "X-Api-Key: $KEY" -H "Content-Type: application/json" \
     -X POST "https://api.echtpost.de/v2/groups" -d '{ "name": "VIPs" }'

Deleting a group that still has members returns 409 Conflict.

Templates

curl -sS -H "X-Api-Key: $KEY" "https://api.echtpost.de/v2/templates"
curl -sS -H "X-Api-Key: $KEY" "https://api.echtpost.de/v2/templates/3"

Motives

Browse available postcard designs. Use the returned id as motive_id when creating cards.

# list all motives
curl -sS -H "X-Api-Key: $KEY" "https://api.echtpost.de/v2/motives"

# search by keyword
curl -sS -H "X-Api-Key: $KEY" \
  "https://api.echtpost.de/v2/motives?search=geburtstag"

# show a specific motive
curl -sS -H "X-Api-Key: $KEY" "https://api.echtpost.de/v2/motives/42"

Each motive includes feature flags (content_available, content_ps_available, vertical_text_available, qr_code_available) that indicate which content fields are supported, plus a url linking to the motive's page on echtpost.de.

Cards

deliver_at field

The deliver_at field accepts several formats:

Value Resolves to
"YYYY-MM-DD" Exact date
"today" Today
"asap" Today (alias)
"tomorrow" Tomorrow
"N-days-from-now" N calendar days from now, e.g. "5-days-from-now"

Constraints: must be a weekday; must not be a printer holiday. If omitted, the card is scheduled for the next available business day.

Natural-language strings like "in two days" are not accepted — use the numeric hyphenated form "2-days-from-now" instead.

# list only scheduled + sent
curl -sS -H "X-Api-Key: $KEY" \
  "https://api.echtpost.de/v2/cards?status=scheduled,sent&sort=deliver_at"

# show
curl -sS -H "X-Api-Key: $KEY" \
  "https://api.echtpost.de/v2/cards/555"

# create from template
curl -sS -H "X-Api-Key: $KEY" -H "Content-Type: application/json" \
     -X POST "https://api.echtpost.de/v2/cards/from_template" \
     -d '{
       "template_id": 3,
       "recipient_ids": [101, 102],
       "deliver_at": "2026-05-12"
     }'

# cancel (async)
curl -sS -H "X-Api-Key: $KEY" -X DELETE "https://api.echtpost.de/v2/cards/555"

Cancel returns 202 Accepted when the cancellation is queued, or 409 Conflict with type: card-not-cancelable if the state transition is no longer possible (e.g. the card is already sent).

Notifications (optional on create)

"notification": { "type": "on_send", "email": "ops@acme.de", "send_on": "send" }

Stable error types

Match integrations on these type URIs:

URI suffix HTTP When
authentication-required 401 Missing or invalid API key
not-found 404 Resource not found or not in scope
validation 422 Generic validation — see errors[]
text-does-not-fit 422 Message overflows card at given font
invalid-delivery-date 422 deliver_at is not a valid business date — use YYYY-MM-DD, today, tomorrow, or N-days-from-now and pick a weekday that is not a public holiday
template-not-available 422 Template not usable by this account
feature-not-available 422 E.g. QR-code cards not enabled
insufficient-funds 402 Balance too low for the requested cards
card-not-cancelable 409 Card is past the cancelable window
rate-limited 429 Too many requests — check Retry-After header

All full URIs are prefixed with https://api.echtpost.de/v2/errors/.

Send Your First Postcard via the EchtPost API

This tutorial walks you through creating a real, printed postcard using the EchtPost REST API v2. By the end you'll have made four HTTP calls that result in a physical card in someone's mailbox.

What you need - An EchtPost account (sign up at echtpost.de) - A credential — either an OAuth access token (preferred for user-facing apps; see llm_guide.md for the full flow) or an API key for server-to-server scripts (create one at Account → API keys in the web app) - Enough credit to send at least one card (top up under Account → Credits) - curl (any OS) or your HTTP client of choice

Set your credential in the shell. The examples below use an OAuth token, but you can substitute an API key with -H "X-Api-Key: $ECHTPOST_API_KEY" on every request:

export ECHTPOST_TOKEN=your_oauth_access_token_or_api_key

Step 1 — Confirm your account and balance

Before anything else, verify your key works and that your account has credit.

curl -sS \
  -H "Authorization: Bearer $ECHTPOST_TOKEN" \
  "https://api.echtpost.de/v2/me"

A successful response looks like this:

{
  "account": { "id": 1, "name": "Acme GmbH", "country_code": "de" },
  "user":    { "id": 7, "email": "you@acme.de" },
  "api_key": { "id": 3, "description": "CI pipeline" },
  "balance": {
    "credits_eur": "12.50",
    "cards_regular": 5,
    "cards_foreign": 0,
    "currency": "EUR"
  }
}

balance.cards_regular is the number of cards you can still send with your current credit. If it's 0, top up before continuing.


Step 2 — Pick a motive

Every postcard needs a motive_id — the front-side image. Browse the full motive gallery at echtpost.de/postkarten, or list the motives available to your account via the templates endpoint:

curl -sS \
  -H "Authorization: Bearer $ECHTPOST_TOKEN" \
  "https://api.echtpost.de/v2/templates"

Each item in the response includes id and motive_id. We'll use motive_id: 42 for the rest of this tutorial.


A postcard has a fixed printable area, and it's worth checking that your message fits before submitting — especially for longer texts or when you're building an automated flow. The preview_fit endpoint tells you exactly how many lines your content will use and suggests a smaller font size if it overflows.

curl -sS \
  -H "Authorization: Bearer $ECHTPOST_TOKEN" \
  -H "Content-Type: application/json" \
  -X POST "https://api.echtpost.de/v2/cards/preview_fit" \
  -d '{
    "content": "Liebe {anrede},\n\nwir möchten uns herzlich bei Ihnen für Ihr Vertrauen bedanken und wünschen Ihnen eine wunderbare Zeit.\n\nMit freundlichen Grüßen,\nIhr Acme-Team",
    "font": { "family": "architects_daughter", "size": 13 }
  }'

Success — text fits: json { "fits": true, "lines_used": 7, "max_lines": 16 }

Text overflows: json { "fits": false, "lines_used": 18, "max_lines": 16, "overflow_line": 17, "suggested_font_size": 11 }

If fits is false, you have two easy options: trim the text a little, or pass the suggested_font_size as size in your create request — the API will handle the rest.

Valid font options are:

family Valid size range
architects_daughter 12 – 14
reenie_beanie 15 – 17
special_elite 11 – 13

Colors: black, blue, or any hex value (e.g. "#1a2b3c").

Tip: Pass "sample_greeting": "Sehr geehrte Frau Müller" to preview_fit for an accurate line-count when your content starts with {anrede}.


Step 4 — Create the card

There are two ways to create a card via the API. Choose based on how much control you need over the content.


The easy path: send from a saved template

If the postcard content stays the same and you just want to trigger a send via the API — perhaps on a schedule or from an external system — use the from-template endpoint. Design the card once in the EchtPost editor, save it as a template, then reference its ID here. No message text, no font settings, no length checks needed.

curl -sS \
  -H "Authorization: Bearer $ECHTPOST_TOKEN" \
  -H "Content-Type: application/json" \
  -X POST "https://api.echtpost.de/v2/cards/from_template" \
  -d '{
    "template_id": 3,
    "deliver_at": "2026-05-12",
    "recipient_ids": [101, 102]
  }'

Find your template_id via GET /v2/templates (Step 2), or copy it from the template URL in the EchtPost editor. The same recipient options apply as below — recipient_ids, group_ids, and inline recipients can all be combined in a single request.

deliver_at formats — the field accepts more than just ISO dates:

Value Meaning
"YYYY-MM-DD" Exact date, e.g. "2026-05-12"
"today" or "asap" Today's date
"tomorrow" Tomorrow
"N-days-from-now" N calendar days ahead, e.g. "5-days-from-now"

The chosen date must be a weekday and not a printer holiday. Omitting deliver_at schedules the card for the next available business day.


The flexible path: compose content inline

Use this when the card content varies per send — for example, a dynamic message generated by your system. This is where preview_fit (Step 3) is worth running first.

{anrede} is optional. If you include it in content, the API replaces it per recipient with their personalised salutation at print time (e.g. "Sehr geehrte Frau Müller" or "Lieber Max") — pulled from the contact's name and greeting_style. You don't have to use it at all: for a single inline recipient where you're composing the greeting yourself, simply write it out directly in content. {anrede} becomes most valuable in mailings (Option B or C) where the greeting needs to differ for each recipient without you having to generate personalised texts yourself.

Option A: Inline recipients (no prior contact needed)

Best for one-off sends or when recipient data comes from an external system.

curl -sS \
  -H "Authorization: Bearer $ECHTPOST_TOKEN" \
  -H "Content-Type: application/json" \
  -X POST "https://api.echtpost.de/v2/cards" \
  -d '{
    "motive_id": 42,
    "content": "{anrede},\n\nwir wünschen Ihnen alles Gute.\n\nHerzlich,\nIhr Acme-Team",
    "font": { "family": "architects_daughter", "size": 13, "color": "black" },
    "deliver_at": "2026-05-12",
    "recipients": [
      {
        "first_name": "Maria",
        "last_name": "Müller",
        "street": "Musterstraße 1",
        "zip": "10115",
        "city": "Berlin",
        "country_code": "de",
        "greeting_style": "formal"
      }
    ]
  }'

Option B: Saved contacts (by ID)

Use this when the recipient already exists in your EchtPost contacts.

curl -sS \
  -H "Authorization: Bearer $ECHTPOST_TOKEN" \
  -H "Content-Type: application/json" \
  -X POST "https://api.echtpost.de/v2/cards" \
  -d '{
    "motive_id": 42,
    "content": "{anrede},\n\nwir bedanken uns für Ihr Vertrauen.\n\nHerzlich,\nIhr Team",
    "font": { "family": "architects_daughter", "size": 13 },
    "recipient_ids": [101, 102, 103]
  }'

Option C: An entire contact group

Perfect for mailings — send to everyone in a group with a single call.

curl -sS \
  -H "Authorization: Bearer $ECHTPOST_TOKEN" \
  -H "Content-Type: application/json" \
  -X POST "https://api.echtpost.de/v2/cards" \
  -d '{
    "motive_id": 42,
    "content": "{anrede},\n\nwir freuen uns, Sie als Kunden begrüßen zu dürfen.\n\nMit freundlichen Grüßen,\nIhr Acme-Team",
    "font": { "family": "architects_daughter", "size": 13 },
    "group_ids": [7]
  }'

You can combine recipient_ids, group_ids, and inline recipients in a single request. Add "deduplicate_recipients": true to avoid sending duplicate cards if contacts appear in multiple groups.

Successful response (HTTP 201)

{
  "id": "a1b2c3d4-0000-0000-0000-111122223333",
  "status": "pending",
  "type": "CardSingle",
  "motive_id": 42,
  "content": "{anrede},\n\nwir wünschen Ihnen alles Gute.\n\nHerzlich,\nIhr Acme-Team",
  "font": { "family": "architects_daughter", "size": 13, "color": "black" },
  "contacts_count": 1,
  "deliver_at": "2026-05-12T00:00:00Z",
  "cancelable": true,
  "created_at": "2026-04-28T09:00:00Z",
  "warnings": []
}

Save the id — you'll need it to check status or cancel the card.

warnings array: A no_line_breaks warning means your content has no \n characters. The card will render but may look like a wall of text. Add paragraph breaks for better readability.


Step 5 — Verify the card

Poll the card at any time to check its status:

curl -sS \
  -H "Authorization: Bearer $ECHTPOST_TOKEN" \
  "https://api.echtpost.de/v2/cards/a1b2c3d4-0000-0000-0000-111122223333"

The status field follows this lifecycle:

pending → scheduled → sent
               ↓
           canceled

A card is cancelable as long as it hasn't reached the print queue. Cancel it with a DELETE request:

curl -sS \
  -H "Authorization: Bearer $ECHTPOST_TOKEN" \
  -X DELETE \
  "https://api.echtpost.de/v2/cards/a1b2c3d4-0000-0000-0000-111122223333"

This returns 202 Accepted — cancellation is asynchronous. If the card is already past the cancelable window, you'll get a 409 Conflict.


Handling errors

All errors use Content-Type: application/problem+json. Always match on the type URI — the detail field is a human-readable, imperative hint telling you exactly what to fix before retrying.

type (suffix) HTTP What to do
authentication-required 401 Check your API key
insufficient-funds 402 Top up your account balance
validation 422 See errors[] for per-field details
text-does-not-fit 422 Shorten the message or use suggested_font_size
invalid-delivery-date 422 Use a valid format (YYYY-MM-DD, today, tomorrow, N-days-from-now) and pick a weekday that is not a public holiday
feature-not-available 422 Remove qr_code_url or enable the feature
card-not-cancelable 409 Card is past the cancellable window — cannot cancel

Example error response:

{
  "type": "https://api.echtpost.de/v2/errors/text-does-not-fit",
  "title": "Message does not fit on the card",
  "status": 422,
  "detail": "Shorten the message or reduce the font size; 1 line overflows.",
  "instance": "/v2/cards",
  "request_id": "req-abc123"
}

Optional: get notified when the card is sent

Add a notification object to the create request:

"notification": {
  "type": "on_send",
  "email": "ops@acme.de",
  "send_on": "send"
}
type When you get notified
on_send Card transitions to sent
on_error An error occurs during sending
on_both Either of the above
none Never (default)

Full reference

EchtPost MCP server — AI consumer guide

This guide is for developers and AI agents connecting to the EchtPost Model Context Protocol (MCP) server. It exposes the same postcard capabilities as the REST API v2 (account, contacts, groups, templates, cards) — but as MCP tools that AI clients (Claude, Mistral, or any MCP-compatible host) can call directly.

If you are integrating from your own backend, prefer the REST API v2 (see the AI consumer guide tab). Use the MCP server when you want an LLM-driven client to send EchtPost postcards as part of a chat or agent workflow.

Endpoint

https://mcp.echtpost.de

Transport: Streamable HTTP (MCP spec 2025-06-18). The MCP server runs on its own subdomain; the OAuth authorization server lives at https://my.echtpost.de (where the user consent screen needs the session).

Authentication

The MCP server accepts two credential types. Use whichever fits your client:

Use OAuth when the MCP client acts on behalf of a real EchtPost user — this is the standard for Claude, ChatGPT, and any client that enforces user consent. Tokens are short-lived, scoped to one account, and revocable without touching API keys.

An unauthenticated request returns 401 Unauthorized with a WWW-Authenticate header pointing to the protected-resource metadata document on the MCP server's own origin:

WWW-Authenticate: Bearer realm="EchtPost MCP",
  resource_metadata="https://mcp.echtpost.de/.well-known/oauth-protected-resource"

MCP clients that support OAuth (Claude, ChatGPT) follow the discovery chain automatically:

  1. Fetch https://mcp.echtpost.de/.well-known/oauth-protected-resource (RFC 9728) — declares https://my.echtpost.de as the authorization server.
  2. Fetch https://my.echtpost.de/.well-known/oauth-authorization-server (RFC 8414) — returns authorization_endpoint, token_endpoint, registration_endpoint, revocation_endpoint, supported scopes, PKCE methods, etc.
  3. Walk the Authorization Code + PKCE flow against those endpoints.
  4. Attach the resulting token as Authorization: Bearer <token> on subsequent MCP requests.

Dynamic client registration (RFC 7591) — clients can self-register instead of requiring a pre-shared client_id. The exact URL is advertised by the authorization-server metadata as registration_endpoint (currently https://my.echtpost.de/oauth/register).

Key details:

Alternative: API key (server-to-server)

Use an API key for server-side scripts, automations, or clients that don't support OAuth (e.g. Mistral). Two header formats are accepted:

Format Example
Authorization: Bearer <key> Authorization: Bearer ep_live_abc123
X-Api-Key: <key> X-Api-Key: ep_live_abc123

Create keys in the EchtPost web app under Account → API keys. Keys are tied to a single account and user; both must be active.

Connecting an MCP client

Claude supports OAuth-based MCP servers natively. Just provide the URL — Claude handles discovery, registration, and the consent flow:

{
  "mcpServers": {
    "echtpost": {
      "type": "http",
      "url": "https://mcp.echtpost.de"
    }
  }
}

On first connection Claude will open a browser for you to log in and authorize access. No API key needed.

Claude Code (CLI):

claude mcp add --transport http echtpost https://mcp.echtpost.de

Claude Code (CLI) — API key

If you prefer a static key (no browser consent flow):

claude mcp add --transport http echtpost https://mcp.echtpost.de \
  --header "Authorization: Bearer $ECHTPOST_API_KEY"

Claude API / Desktop config — API key

{
  "mcpServers": {
    "echtpost": {
      "type": "http",
      "url": "https://mcp.echtpost.de",
      "headers": {
        "Authorization": "Bearer YOUR_ECHTPOST_API_KEY"
      }
    }
  }
}

Mistral

Provide the URL https://mcp.echtpost.de. Mistral reads the WWW-Authenticate: Bearer header from the 401 response and prompts for a key automatically.

Quick connectivity check (raw HTTP)

# 401 — confirms the server is reachable and auth is required
curl -i https://mcp.echtpost.de

# Tool list — confirms your key works (API key or OAuth token)
curl -sS \
  -H "Authorization: Bearer $ECHTPOST_API_KEY" \
  -H "Content-Type: application/json" \
  -X POST https://mcp.echtpost.de \
  -d '{"jsonrpc":"2.0","method":"tools/list","id":1}'

Tools

All tools resolve through the same business logic as the REST API v2, so concepts such as recipients, fonts, delivery dates, and error types behave identically. Where this guide is silent, fall back to the AI consumer guide tab on this page — it is the canonical reference.

Account

Tool Purpose
get_me Account, user, API key metadata, and current credit balance. Start here to verify the connection.
list_credits Current balance plus how many local/foreign postcards you can send.

Contacts

Tool Purpose
list_contacts List contacts with optional search and pagination.
get_contact Full details of a single contact, including group memberships.
create_contact Create a contact. Required: last_name, street, zip, city, country_code.
update_contact Update fields on a contact. Omitted fields stay unchanged.
delete_contact Permanently delete a contact.

Groups

Tool Purpose
list_groups List contact groups (usable as recipients on cards).
get_group Group details: name, external_id, recipient count.
create_group Create a group with optional external_id.
update_group Rename a group or change its external_id.
delete_group Delete a group. Returns an error if attached workflows exist.

Templates

Tool Purpose
list_templates List saved card templates (pre-configured content, font, motive).
get_template Template details by ID.

Motives

Tool Purpose
list_motives Browse available postcard designs with optional search.
get_motive Motive details, including which content fields it supports.

Cards

Tool Purpose
preview_fit Check whether a message fits at a given font before creating a card. Returns lines_used, max_lines, and a suggested_font_size on overflow.
create_card Create a postcard with custom content and font.
create_card_from_template Create a card from a saved template (template provides content and font).
list_cards List cards with optional status filter (pending, scheduled, sent, canceled) and pagination.
get_card Card details by ID, including delivery status and the cancelable flag.
cancel_card Cancel a scheduled card. Only works while cancelable is true.

Recipient specification

create_card and create_card_from_template accept three recipient sources, all combinable; at least one must yield ≥ 1 contact:

Group membership on update_contact

Fonts

Family Valid sizes
architects_daughter 12–14 (default 14)
reenie_beanie 15–17
special_elite 11–13

Colors: black (default) or blue.

Delivery dates

The deliver_at field accepts:

Value Resolves to
"YYYY-MM-DD" Exact date
"today" / "asap" Today
"tomorrow" Tomorrow
"N-days-from-now" N calendar days from now (e.g. "5-days-from-now")

Must be a weekday and not a printer holiday. Natural-language strings like "in two days" are not accepted — use the numeric hyphenated form.

Errors

Tool errors are returned as MCP tool results with isError: true and a human-readable message in the content text. The underlying error categories mirror the REST API's stable error types — for example a message that overflows the card maps to the same overflow shape and includes a suggested_font_size. See the REST AI consumer guide's Stable error types section for the full list.

401 Unauthorized is terminal: the credential (API key or OAuth token) is missing, invalid, expired, or the account/user is deactivated. For OAuth tokens, the client should attempt a token refresh or re-run the authorization flow. For API keys, verify the key in the EchtPost web app.

Rate limits

Rate limits are shared with the REST API v2 and applied per credential (API key or OAuth token). The strict limits (10/min on card creation, 30/min on preview_fit) apply to the matching MCP tools (create_card, create_card_from_template, preview_fit). Spread bulk runs over time or batch recipients with recipient_ids / group_ids on a single card call.

See also