{"openapi":"3.1.0","info":{"title":"EchtPost API","version":"v2","description":"REST API v2 for EchtPost — send real postcards via API.\n\n- **Recommended**: OAuth 2.1 with PKCE for user-delegated access. Discovery at\n  `https://api.echtpost.de/.well-known/oauth-protected-resource` (RFC 9728);\n  that document points at the authorization server's metadata at\n  `https://my.echtpost.de/.well-known/oauth-authorization-server` (RFC 8414).\n- **server-to-server**: API key via `X-Api-Key: <key>` or\n  `Authorization: Bearer <key>`. Use this only when there is no human user\n  to consent — for user-delegated access (Claude, ChatGPT, third-party apps),\n  use OAuth.\n- Errors follow RFC 7807 (`application/problem+json`); match on the stable\n  `type` URI, not on free text.\n- List endpoints return a JSON array with `X-Total-Count`, `X-Total-Pages`,\n  and RFC 5988 `Link` headers.\n- See `/v2/docs` for Swagger UI + the AI consumer guide.\n"},"servers":[{"url":"https://api.echtpost.de/v2"}],"tags":[{"name":"Account","description":"Account metadata and balance"},{"name":"Contacts","description":"Manage mailing list contacts"},{"name":"Groups","description":"Organize contacts into groups"},{"name":"Templates","description":"Saved card templates"},{"name":"Motives","description":"Postcard designs (motives) available for card creation"},{"name":"Cards","description":"Create and manage single cards or even mailings for multiple recipients"}],"components":{"securitySchemes":{"oauth2":{"type":"oauth2","description":"Recommended for user-delegated access. OAuth 2.1 with PKCE.\nResource discovery (RFC 9728): https://api.echtpost.de/.well-known/oauth-protected-resource\nAuthorization server discovery (RFC 8414): https://my.echtpost.de/.well-known/oauth-authorization-server\nTokens are bound to a single AccountUser (one user, one account).\n","flows":{"authorizationCode":{"authorizationUrl":"https://my.echtpost.de/oauth/authorize","tokenUrl":"https://my.echtpost.de/oauth/token","refreshUrl":"https://my.echtpost.de/oauth/token","scopes":{"api":"Required for REST API access (postcards, contacts, templates, account data)","mcp":"Required for the MCP server. Request both with `scope=mcp api` if you need cross-resource access."}}}},"api_key_header":{"type":"apiKey","in":"header","name":"X-Api-Key","description":"Suitable for server-to-server scripts without a user context.\nFor user-delegated access (Claude, ChatGPT, third-party apps), use OAuth.\n"},"bearer":{"type":"http","scheme":"bearer","description":"Same key material as `api_key_header`, sent as a Bearer token."}},"schemas":{"ProblemDetails":{"type":"object","description":"RFC 7807 problem details","properties":{"type":{"type":"string","description":"Stable error type URI"},"title":{"type":"string"},"status":{"type":"integer"},"detail":{"type":"string","description":"Imperative fix hint"},"instance":{"type":"string"},"request_id":{"type":"string"},"errors":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"code":{"type":"string"},"message":{"type":"string"}}}}},"required":["type","title","status","detail"],"example":{"type":"https://api.echtpost.de/v2/errors/authentication-required","title":"Authentication required","status":401,"detail":"Send a valid API key in the X-Api-Key header or Authorization Bearer header.","instance":"/v2/me","request_id":"req-example"}},"Font":{"type":"object","properties":{"family":{"type":"string","description":"Font slug. Allowed `architects_daughter`, `reenie_beanie`, `special_elite`."},"size":{"type":"integer","description":"Absolute font size. Valid ranges per family — `architects_daughter` 12..14, `reenie_beanie` 15..17, `special_elite` 11..13."},"color":{"type":"string","description":"`black`, `blue`, or a hex value."},"line_height":{"type":"number"}}},"PreviewFitFont":{"type":"object","description":"Optional. Omitted `family` defaults to architects_daughter; omitted `size` defaults to the middle allowed value for the family. Only these fields affect the fit check (no `color`).","properties":{"family":{"type":"string","description":"Font slug. Allowed `architects_daughter`, `reenie_beanie`, `special_elite`."},"size":{"type":"integer","description":"Absolute font size within the valid range for the chosen family."}}},"Contact":{"type":"object","properties":{"id":{"type":"integer"},"account_id":{"type":"integer"},"first_name":{"type":"string"},"last_name":{"type":"string"},"title":{"type":"string"},"greeting_style":{"type":"string","enum":["formal","informal","custom"]},"custom_salutation":{"type":"string","nullable":true},"greeting":{"type":"string","nullable":true},"gender":{"type":"string","nullable":true},"company_name":{"type":"string","nullable":true},"department":{"type":"string","nullable":true},"street":{"type":"string"},"zip":{"type":"string"},"city":{"type":"string"},"state_code":{"type":"string","nullable":true},"country_code":{"type":"string","description":"ISO 3166-1 alpha-2","lowercase":null},"birthdate":{"type":"string","format":"date","nullable":true},"external_id":{"type":"string","nullable":true},"group_ids":{"type":"array","items":{"type":"integer"},"description":"Only present with `include=groups`"},"card_ids":{"type":"array","items":{"type":"string","format":"uuid"},"description":"Only present with `include=cards`"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"Group":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"external_id":{"type":"string","nullable":true},"recipient_count":{"type":"integer"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"Template":{"type":"object","properties":{"id":{"type":"integer"},"motive_id":{"type":"integer"},"content":{"type":"string"},"content_ps":{"type":"string","nullable":true},"content_vertical":{"type":"string","nullable":true},"font":{"$ref":"#/components/schemas/Font"},"qr_code_url":{"type":"string","nullable":true},"preview_url":{"type":"string","nullable":true},"source":{"type":"string","enum":["regular","auto_save","billing_redirect","card_delete","pre_signup","pre_signup_editor"]},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"Motive":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"desc":{"type":"string","nullable":true},"orientation":{"type":"string","enum":["vertical","landscape"]},"highlight":{"type":"boolean"},"content_available":{"type":"boolean"},"content_ps_available":{"type":"boolean"},"vertical_text_available":{"type":"boolean"},"qr_code_available":{"type":"boolean"},"has_back_side":{"type":"boolean","description":"Whether this motive has a custom back-side image attached."},"url":{"type":"string","nullable":true,"description":"Link to the motive page on echtpost.de"},"preview_image_url":{"type":"string","nullable":true,"format":"uri","description":"URL to a preview image of the motive front (~300-420px). For your own motives this is a signed asset URL that stays valid until the assets are purged (see assets_purged_at), and anyone holding the link can open the image."},"back_side_image_url":{"type":"string","nullable":true,"format":"uri","description":"URL to a preview image of the custom back side (~300-420px), or null when the motive has no custom back side. Signed asset URL with the same lifetime as preview_image_url."},"tags":{"type":"array","items":{"type":"string"}},"temporary":{"type":"boolean"},"assets_expires_at":{"type":"string","format":"date","nullable":true,"description":"Earliest date on which temporary motive assets may be purged. Null for permanent motives or already-purged assets without a pending expiry."},"assets_purged_at":{"type":"string","format":"date-time","nullable":true},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"Card":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"account_id":{"type":"integer"},"user_id":{"type":"integer"},"motive_id":{"type":"integer"},"template_id":{"type":"integer","nullable":true},"status":{"type":"string","enum":["pending","scheduled","sent","canceled"]},"type":{"type":"string","enum":["CardSingle","CardMulti"]},"content":{"type":"string"},"content_ps":{"type":"string","nullable":true},"content_vertical":{"type":"string","nullable":true},"font":{"$ref":"#/components/schemas/Font"},"contacts_count":{"type":"integer"},"deliver_at":{"type":"string","format":"date-time","nullable":true},"qr_code_url":{"type":"string","nullable":true},"cancelable":{"type":"boolean"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"Me":{"type":"object","properties":{"account":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"country_code":{"type":"string"},"default_greeting_style":{"type":"string"}}},"user":{"type":"object","properties":{"id":{"type":"integer"},"email":{"type":"string"}}},"api_key":{"type":"object","properties":{"id":{"type":"integer"},"description":{"type":"string"},"last_used_on":{"type":"string","format":"date","nullable":true},"created_at":{"type":"string","format":"date-time"}}},"balance":{"type":"object","properties":{"credits_eur":{"type":"string"},"cards_regular":{"type":"integer"},"cards_foreign":{"type":"integer"},"currency":{"type":"string"}}}}},"Credits":{"type":"object","properties":{"credits_eur":{"type":"string"},"cards_regular":{"type":"integer"},"cards_foreign":{"type":"integer"},"currency":{"type":"string"},"price_per_card_eur":{"type":"string"},"price_per_foreign_card_eur":{"type":"string"}}},"RecipientInput":{"type":"object","required":["last_name","street","zip","city","country_code"],"properties":{"first_name":{"type":"string"},"last_name":{"type":"string"},"title":{"type":"string"},"greeting_style":{"type":"string","enum":["formal","informal","custom"]},"custom_salutation":{"type":"string"},"gender":{"type":"string"},"company_name":{"type":"string"},"department":{"type":"string"},"street":{"type":"string"},"zip":{"type":"string"},"city":{"type":"string"},"state_code":{"type":"string"},"country_code":{"type":"string"},"birthdate":{"type":"string","format":"date"},"external_id":{"type":"string"}}},"Notification":{"type":"object","required":["type","email"],"properties":{"type":{"type":"string","enum":["after_sent","before_send"],"description":"`after_sent` mails `email` once the card is sent. `before_send` mails a reminder on `send_on`."},"email":{"type":"string","format":"email"},"send_on":{"type":"string","format":"date","description":"`before_send` only. Reminder date, on or before the send date. Defaults to the send date. Ignored for `after_sent`."}}}},"responses":{"Unauthorized":{"description":"Authentication required","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"ValidationError":{"description":"Unprocessable Entity","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"NotFound":{"description":"Resource not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}},"security":[{"oauth2":["mcp"]},{"api_key_header":[]},{"bearer":[]}],"paths":{"/me":{"get":{"tags":["Account"],"summary":"Current account and API key metadata","operationId":"getMe","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Me"},"example":{"account":{"id":1,"name":"Acme","country_code":"de","default_greeting_style":"formal"},"user":{"id":1,"email":"user@example.com"},"api_key":{"id":1,"description":"CI","last_used_on":"2026-04-22","created_at":"2026-01-01T00:00:00Z"},"balance":{"credits_eur":"10.00","cards_regular":20,"cards_foreign":10,"currency":"EUR"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/credits":{"get":{"tags":["Account"],"summary":"Account balance and pricing","operationId":"getCredits","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Credits"},"example":{"credits_eur":"14.50","cards_regular":29,"cards_foreign":14,"currency":"EUR","price_per_card_eur":"0.99","price_per_foreign_card_eur":"1.49"}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/contacts":{"get":{"tags":["Contacts"],"summary":"List contacts","operationId":"listContacts","parameters":[{"name":"page","in":"query","schema":{"type":"integer","minimum":1}},{"name":"per_page","in":"query","schema":{"type":"integer","minimum":1,"maximum":200}},{"name":"country_code","in":"query","schema":{"type":"string"}},{"name":"group_id","in":"query","schema":{"type":"integer"}},{"name":"external_id","in":"query","schema":{"type":"string"}},{"name":"search","in":"query","schema":{"type":"string"}},{"name":"sort","in":"query","schema":{"type":"string","enum":["last_name","created_at","updated_at"]}},{"name":"direction","in":"query","schema":{"type":"string","enum":["asc","desc"]}},{"name":"include","in":"query","description":"Comma-separated. Allowed `groups`, `cards`.","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","headers":{"X-Total-Count":{"schema":{"type":"integer"}},"X-Total-Pages":{"schema":{"type":"integer"}},"Link":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Contact"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"422":{"$ref":"#/components/responses/ValidationError"}}},"post":{"tags":["Contacts"],"summary":"Create contact","operationId":"createContact","requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/RecipientInput"},{"type":"object","properties":{"group_ids":{"type":"array","items":{"type":"integer"}},"group_names":{"type":"array","items":{"type":"string"}}}}]},"example":{"first_name":"Jane","last_name":"Doe","greeting_style":"formal","street":"Musterstr. 1","zip":"10115","city":"Berlin","country_code":"de"}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Contact"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"422":{"$ref":"#/components/responses/ValidationError"}}}},"/contacts/{id}":{"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}},{"name":"include","in":"query","description":"Comma-separated. Allowed `groups`, `cards`.","schema":{"type":"string"}}],"get":{"tags":["Contacts"],"summary":"Show contact","operationId":"showContact","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Contact"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}},"patch":{"tags":["Contacts"],"summary":"Update contact","operationId":"updateContact","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecipientInput"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Contact"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationError"}}},"delete":{"tags":["Contacts"],"summary":"Delete contact","operationId":"deleteContact","responses":{"204":{"description":"No Content"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/groups":{"get":{"tags":["Groups"],"summary":"List groups","operationId":"listGroups","parameters":[{"name":"page","in":"query","schema":{"type":"integer","minimum":1}},{"name":"per_page","in":"query","schema":{"type":"integer","minimum":1,"maximum":200}},{"name":"sort","in":"query","schema":{"type":"string","enum":["name","created_at","updated_at"]}},{"name":"direction","in":"query","schema":{"type":"string","enum":["asc","desc"]}}],"responses":{"200":{"description":"OK","headers":{"X-Total-Count":{"schema":{"type":"integer"}},"X-Total-Pages":{"schema":{"type":"integer"}},"Link":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Group"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}},"post":{"tags":["Groups"],"summary":"Create group","operationId":"createGroup","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"external_id":{"type":"string"}}}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Group"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"422":{"$ref":"#/components/responses/ValidationError"}}}},"/groups/{id}":{"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"get":{"tags":["Groups"],"summary":"Show group","operationId":"showGroup","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Group"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}},"patch":{"tags":["Groups"],"summary":"Update group","operationId":"updateGroup","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"external_id":{"type":"string"}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Group"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/ValidationError"}}},"delete":{"tags":["Groups"],"summary":"Delete group","operationId":"deleteGroup","responses":{"204":{"description":"No Content"},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"Conflict — group has members or is in use","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}}}}},"/templates":{"get":{"tags":["Templates"],"summary":"List templates","operationId":"listTemplates","parameters":[{"name":"page","in":"query","schema":{"type":"integer","minimum":1}},{"name":"per_page","in":"query","schema":{"type":"integer","minimum":1,"maximum":200}},{"name":"source","in":"query","description":"Defaults to `regular`.","schema":{"type":"string","enum":["regular","ai","letter"]}}],"responses":{"200":{"description":"OK","headers":{"X-Total-Count":{"schema":{"type":"integer"}},"X-Total-Pages":{"schema":{"type":"integer"}},"Link":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Template"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/templates/{id}":{"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"get":{"tags":["Templates"],"summary":"Show template","operationId":"showTemplate","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Template"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/motives":{"get":{"tags":["Motives"],"summary":"List available motives","operationId":"listMotives","parameters":[{"name":"page","in":"query","schema":{"type":"integer","minimum":1}},{"name":"per_page","in":"query","schema":{"type":"integer","minimum":1,"maximum":200}},{"name":"search","in":"query","description":"Search across name, description, content, and searchterms.","schema":{"type":"string"}},{"name":"temporary","in":"query","description":"Filter by temporary API motives (`true`) or permanent motives (`false`).","schema":{"type":"string","enum":["true","false"]}},{"name":"sort","in":"query","schema":{"type":"string","enum":["name","created_at"]}},{"name":"direction","in":"query","schema":{"type":"string","enum":["asc","desc"]}}],"responses":{"200":{"description":"OK","headers":{"X-Total-Count":{"schema":{"type":"integer"}},"X-Total-Pages":{"schema":{"type":"integer"}},"Link":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Motive"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"422":{"$ref":"#/components/responses/ValidationError"}}},"post":{"tags":["Motives"],"summary":"Upload a temporary custom motive","description":"Uploads one temporary, account-owned postcard motive. The account must be enabled for API custom motives. The front image must be a readable PNG or JPEG file, at most 5 MB, and exactly 1795x1287 pixels for landscape motives or 1287x1795 pixels for vertical motives. An optional custom back side can be uploaded as a separate `back_side` file when the account has both regular custom back-side bookings active: logo replacement and vertical text replacement. The back side has its own 5 MB limit and must always be exactly 1795x1287 px landscape, independent of `orientation`, because it is printed full-bleed without rotation. When `back_side` is present, EchtPost does not render its standard postmark image or footer logo; the uploaded image must include the franking area, dividing line, footer line, and static branding elements, but not recipient address lines because EchtPost prints the actual address. API clients upload artwork and card data, not the final PDF, and are responsible for checking the final print result in the EchtPost web UI during development. Accounts are limited to 1,000 temporary motive uploads per rolling 24 hours; exceeding that limit returns HTTP 429 and notifies support.","operationId":"createMotive","requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","required":["name","image"],"properties":{"name":{"type":"string","maxLength":60},"orientation":{"type":"string","enum":["landscape","vertical"]},"image":{"type":"string","format":"binary","description":"Front image. PNG or JPEG, max 5 MB. Required dimensions: 1795x1287 px for landscape, 1287x1795 px for vertical."},"back_side":{"type":"string","format":"binary","description":"Optional custom postcard back side. Requires both regular custom back-side bookings for logo replacement and vertical text replacement; otherwise the request returns 422 `feature-not-available`. PNG or JPEG, max 5 MB. Required dimensions: exactly 1795x1287 px landscape for every motive orientation. The image is printed full-bleed and is not rotated."},"content_available":{"type":"boolean","default":true,"description":"Whether EchtPost may print the normal message text on the back side."},"content_ps_available":{"type":"boolean","default":true,"description":"Whether EchtPost may print the P.S. footer line on the back side."},"vertical_text_available":{"type":"boolean","default":true,"description":"Whether EchtPost may print the vertical sender/message line on the back side."},"qr_code_available":{"type":"boolean","default":true,"description":"Whether EchtPost may print a QR code on the back side when a card has one."}}}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Motive"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"422":{"$ref":"#/components/responses/ValidationError"},"429":{"description":"Daily temporary motive upload limit exceeded","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"},"example":{"type":"https://api.echtpost.de/v2/errors/rate-limit-exceeded","title":"Too many requests","status":429,"detail":"Temporary motive upload limit reached. Try again later or contact support with your request_id.","instance":"/v2/motives","request_id":"req-example"}}}}}}},"/motives/{id}":{"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"integer"}}],"get":{"tags":["Motives"],"summary":"Show motive","operationId":"showMotive","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Motive"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/cards":{"get":{"tags":["Cards"],"summary":"List cards","operationId":"listCards","parameters":[{"name":"page","in":"query","schema":{"type":"integer","minimum":1}},{"name":"per_page","in":"query","schema":{"type":"integer","minimum":1,"maximum":200}},{"name":"status","in":"query","description":"One or more of `pending`, `scheduled`, `sent`, `canceled`. Comma-separated.","schema":{"type":"string"}},{"name":"deliver_at","in":"query","description":"Filter cards by delivery date (ISO 8601, e.g. `2026-05-12`).","schema":{"type":"string","format":"date"}},{"name":"motive_id","in":"query","schema":{"type":"integer"}},{"name":"sort","in":"query","schema":{"type":"string","enum":["created_at","deliver_at"]}},{"name":"direction","in":"query","schema":{"type":"string","enum":["asc","desc"]}}],"responses":{"200":{"description":"OK","headers":{"X-Total-Count":{"schema":{"type":"integer"}},"X-Total-Pages":{"schema":{"type":"integer"}},"Link":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Card"}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"422":{"$ref":"#/components/responses/ValidationError"}}},"post":{"tags":["Cards"],"summary":"Create a card (custom)","operationId":"createCard","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["motive_id","content"],"properties":{"motive_id":{"type":"integer"},"content":{"type":"string","description":"Card text. Use {anrede} as a per-recipient salutation placeholder (casing ignored)."},"content_ps":{"type":"string","maxLength":40,"description":"Optional P.S., printed as a single footer line."},"content_vertical":{"type":"string"},"deliver_at":{"type":"string","description":"Delivery date. Accepts: ISO 8601 date (`YYYY-MM-DD`), `today`, `asap` (alias for today), `tomorrow`, or `N-days-from-now` (e.g. `5-days-from-now`). Must resolve to a weekday that is not a printer holiday. Must be at most 90 days in the future. Omitting this field schedules the card for the next available business day."},"qr_code_url":{"type":"string"},"deduplicate_recipients":{"type":"boolean"},"recipient_ids":{"type":"array","items":{"type":"integer"}},"group_ids":{"type":"array","items":{"type":"integer"}},"recipients":{"type":"array","items":{"$ref":"#/components/schemas/RecipientInput"}},"font":{"description":"Optional; defaults to architects_daughter at the middle allowed size. Color defaults to black when omitted.","$ref":"#/components/schemas/Font"},"notifications":{"type":"array","items":{"$ref":"#/components/schemas/Notification"}},"notification":{"deprecated":true,"description":"Single notification. Use `notifications`; sending both returns 422.","allOf":[{"$ref":"#/components/schemas/Notification"}]},"simulate":{"type":"string","enum":["insufficient_funds"],"description":"Sandbox only. Instead of creating the card, force a simulated error so API clients can test their error handling. `insufficient_funds` returns HTTP 402 with the normal insufficient-funds body. Ignored with a live key."}}},"example":{"motive_id":42,"content":"{anrede},\nwir wünschen frohe Ostern.\nHerzlich, das Team\n","font":{"family":"architects_daughter","size":13,"color":"black"},"recipient_ids":[101,102]}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Card"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"description":"Payment Required — insufficient funds","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"},"example":{"type":"https://api.echtpost.de/v2/errors/insufficient-funds","title":"Insufficient funds","status":402,"detail":"Top up your account before retrying this card creation."}}}},"422":{"description":"Unprocessable Entity — validation, text-does-not-fit, invalid-delivery-date, feature-not-available","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"},"examples":{"TextDoesNotFit":{"value":{"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."}},"InvalidDeliveryDate":{"value":{"type":"https://api.echtpost.de/v2/errors/invalid-delivery-date","title":"Invalid delivery date","status":422,"detail":"Pick a date at least one business day from now."}},"FeatureNotAvailable":{"value":{"type":"https://api.echtpost.de/v2/errors/feature-not-available","title":"Feature not available","status":422,"detail":"Enable QR-code cards on your account or remove the `qr_code_url`."}}}}}}}}},"/cards/from_template":{"post":{"tags":["Cards"],"summary":"Create a card from a template","operationId":"createCardFromTemplate","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["template_id"],"properties":{"template_id":{"type":"integer"},"deliver_at":{"type":"string","description":"Delivery date. Accepts: ISO 8601 date (`YYYY-MM-DD`), `today`, `asap` (alias for today), `tomorrow`, or `N-days-from-now` (e.g. `5-days-from-now`). Must resolve to a weekday that is not a printer holiday. Must be at most 90 days in the future. Omitting this field schedules the card for the next available business day."},"recipient_ids":{"type":"array","items":{"type":"integer"}},"group_ids":{"type":"array","items":{"type":"integer"}},"recipients":{"type":"array","items":{"$ref":"#/components/schemas/RecipientInput"}},"simulate":{"type":"string","enum":["insufficient_funds"],"description":"Sandbox only. Instead of creating the card, force a simulated error so API clients can test their error handling. `insufficient_funds` returns HTTP 402 with the normal insufficient-funds body. Ignored with a live key."}}}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Card"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"description":"Payment Required — insufficient funds","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"}}}},"422":{"$ref":"#/components/responses/ValidationError"}}}},"/cards/preview_fit":{"post":{"tags":["Cards"],"summary":"Check whether card content fits at a given font","operationId":"previewCardFit","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["content"],"properties":{"content":{"type":"string","description":"Plain text in the same shape as POST /v2/cards content; use JSON line breaks (\\n) between paragraphs."},"sample_greeting":{"type":"string","description":"Optional greeting prefix to prepend for fit-check."},"font":{"$ref":"#/components/schemas/PreviewFitFont"}}},"example":{"content":"Liebe Grüße aus Berlin,\ndas Team\n","font":{"family":"architects_daughter","size":13}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"fits":{"type":"boolean"},"lines_used":{"type":"integer"},"max_lines":{"type":"integer"},"overflow_line":{"type":"integer","nullable":true},"suggested_font_size":{"type":"integer","nullable":true}}},"examples":{"Fits":{"value":{"fits":true,"lines_used":3,"max_lines":18}},"DoesNotFit":{"value":{"fits":false,"lines_used":20,"max_lines":18,"overflow_line":19,"suggested_font_size":12}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"}}}},"/cards/{id}":{"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"get":{"tags":["Cards"],"summary":"Show card","operationId":"showCard","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Card"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"}}},"delete":{"tags":["Cards"],"summary":"Cancel card (async)","operationId":"cancelCard","responses":{"202":{"description":"Accepted — cancellation queued","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["pending","scheduled","sent","canceled"]},"cancelable":{"type":"boolean"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"Card not cancelable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ProblemDetails"},"example":{"type":"https://api.echtpost.de/v2/errors/card-not-cancelable","title":"Card not cancelable","status":409,"detail":"This card is already sent."}}}}}}}}}