{"openapi":"3.1.0","jsonSchemaDialect":"https://spec.openapis.org/oas/3.1/dialect/base","info":{"title":"Poštárna API","version":"1.0.0","description":"Poštárna is a multi-tenant e-mail marketing platform with statistics for companies and e-shops (Czech and Slovak). This document describes every\nroute an integration can call. Guides in Czech and Slovak: https://www.postarna.cz/docs/api (index for AI agents:\nhttps://www.postarna.cz/llms.txt, everything in one Markdown file: https://www.postarna.cz/llms-full.txt).\n\n## Three APIs\n* **API v1** (`/api/v1/tenants/{tenant}/...`): the full API, the same one the Poštárna web app uses. JSON in UTF-8.\n* **SmartEmailing compatible API v3** (`/api/v3/...`) and its CSV statistics (`/api/csv/...`): the same paths,\n  envelopes and errors as SmartEmailing's REST API v3, so an existing SE integration only changes the address, the\n  user name and the key. Read operations plus account webhooks.\n* **MCP server** (`POST /mcp`): Model Context Protocol for AI assistants (ChatGPT, Claude, Cursor ...), OAuth 2.1 or an\n  API key.\n\n## Authentication\nCreate an API key in the app: Nastavení › Datové zdroje › API klíče (Settings › Data sources › API keys). The key\n(`pst_` + 43 characters) is shown once. Send it as `Authorization: Bearer pst_...`, or as HTTP Basic with the e-mail\nof an active member of the company as the user name and the key as the password (both work on v1 and v3). A key\nworks only in its own company (tenant), with the roles it was given (never more than its creator has), and with\nscope `full` (read and write) or `read` (only routes whose permission level is read). Keys never reach the members,\ninvitations, API keys and connected apps (those need a signed-in person: 403 `forbidden` / `api_key`).\n\n`{tenant}` in v1 paths is the company's public id (a UUID). Find it with `GET /api/v3/account-info` (`data.guid`) or\nin the app on the API keys page.\n\n## Conventions (API v1)\n* IDs are strings. Times are ISO 8601 in UTC with milliseconds (`2026-09-30T08:00:00.000Z`); local calendar days of\n  the company are `YYYY-MM-DD`; automation settings use local `YYYY-MM-DDTHH:MM` and `HH:MM`.\n* Money is a decimal string next to its `currency` (`\"1290.00\"`, `\"CZK\"`). CZK and EUR are never added up or\n  converted: every sum is per currency.\n* Lists are paged: `page` from 1, `pageSize` 1 to 100 (default 25); the answer is `{items, total, page, pageSize}`.\n  Some grids take `sort` (`name,-createdAt`, at most 3 fields, `-` = descending).\n* Request bodies of POST, PUT and PATCH are `application/json` (415 `validation` / `contentType` otherwise); CSV\n  import and file upload also take `multipart/form-data`. Unknown body fields are ignored.\n* Errors: `{\"error\": {\"code\": \"...\", \"message\": \"<field or detail>\", \"params\": {...}}}`. `code` is stable and\n  machine readable, `message` names the field or the reason, `params` carries details (for example the issues of an\n  automation graph). The API never sends texts for people.\n* No idempotency keys: retry only requests that are safe to repeat (GET, PUT of a whole object, DELETE). For POST,\n  read the state first (for example list the contacts by e-mail) before you retry after a timeout. Concurrent\n  edits of content and automation graphs are guarded by versions (`baseVersion`, 409 `conflict` / `version`).\n\n## Limits\nPer company (all keys together): a bucket of 2 500 requests refilled with 4 requests a second, and 100 000 requests a\nday (refilled continuously). Every answer to a key carries `X-RateLimit-Limit`, `X-RateLimit-Remaining` and\n`X-RateLimit-Reset` (unix time when the bucket is full again) of the bucket nearest to running out. Over the limit:\n429 `rate_limited` with `Retry-After` (seconds). Some actions have their own limits (test e-mails 10 per day per\ncampaign or template, contact events 60 per contact in 10 minutes, MCP 300 messages a minute).\n\n## Consent and sending\nPoštárna never e-mails a contact who unsubscribed, bounced, complained or is suppressed, and campaigns only reach\ncontacts with a valid marketing consent. Sending to real recipients (approving a campaign, activating an automation)\nis irreversible: check the audience first (`POST .../campaigns/audience`, `POST .../automations/{id}/test`). Test\ne-mails go only to the company's test addresses (`GET .../test-recipients`).\n","contact":{"name":"deriverge s.r.o.","url":"https://www.postarna.cz/docs/api"},"x-guides":{"cs":"https://www.postarna.cz/docs/api","sk":"https://www.postarna.sk/docs/api","llms":"https://www.postarna.cz/llms.txt","llmsFull":"https://www.postarna.cz/llms-full.txt"}},"servers":[{"url":"https://www.postarna.cz","description":"Production (the same API answers on www.postarna.sk)"}],"security":[{"bearerAuth":[]},{"basicAuth":[]}],"tags":[{"name":"Contacts","x-displayName":"Contacts","description":"Contacts of the company: profile fields, consent state, suppressions, custom fields, purposes of processing (consents), activities and GDPR export and erasure."}],"x-error-codes":{"conflict":{"meaning":"The change would break the state of the object. `message`: `name` (name taken), `locked` (a list bound to an e-shop), `status` (wrong state, e.g. editing a sent campaign), `recipients` (the audience changed more than 5 % since you counted it, `params.expected` and `params.actual`), `version` (stale `baseVersion`, `params.current`), `confirm` (saving a running automation needs `confirmRunning: true`), `waiting` (contacts wait in a node you remove, `params.nodes`), `runs` (deleting an automation that ran), `trigger` (no manual trigger), `limit` (too many objects).","fix":"Read the object again, then repeat with current data; for `recipients` count the audience again and send the new `expectedRecipients`."},"count_mismatch":{"meaning":"`expectedRecipients` does not match the audience.","fix":"Count again with `POST /campaigns/audience` and send the new number."},"csrf":{"meaning":"Browser session request without the CSRF header (never with an API key).","fix":"Send the API key in `Authorization` instead of cookies."},"empty_segment":{"meaning":"The campaign audience has no recipient.","fix":"Change the audience."},"expired":{"meaning":"The object is no longer available (an export file older than its retention).","fix":"Create it again (POST /exports)."},"forbidden":{"meaning":"The credentials are valid but not allowed here. `message`: `tenant` (another company in the path), `scope` (a read-only key on a write route), `api_key` (members, invitations and keys need a signed-in person), a module name (`contacts`, `campaigns`, `content`, `settings`, `billing`: the key's roles lack the level), `read_only` (support view).","fix":"Use the tenant id of the key (`GET /api/v3/account-info`), a key with scope `full`, or a key whose roles include the module (for example marketing_manager)."},"method_not_allowed":{"meaning":"The path exists but not with this HTTP method.","fix":"Check the method in this document."},"not_found":{"meaning":"The object does not exist in this company (objects of other companies answer the same).","fix":"Check the id; list the objects first."},"not_implemented":{"meaning":"The feature is not available yet.","fix":"Do not rely on it."},"rate_limited":{"meaning":"Too many requests: the company's API limit, or the limit of one action (test e-mails, contact events, sign-in attempts). `Retry-After` says after how many seconds to retry.","fix":"Wait `Retry-After` seconds; spread requests (4 a second sustained)."},"recently_sent":{"meaning":"HTTP 423: the contact cannot be deleted yet, a campaign e-mail to it is queued or went out in the last 24 hours (SmartEmailing answers the same).","fix":"Delete it after 24 hours. A request to be forgotten (GDPR) uses POST /contacts/{id}/erase, which is never refused."},"server":{"meaning":"Unexpected server error.","fix":"Retry later; the request may be repeated if it is safe to repeat."},"suppression_locked":{"meaning":"The address is suppressed for another reason (bounce, complaint, unsubscribe), so the blacklist cannot be added or removed; `params.reason` names it.","fix":"Leave it: such suppressions protect the sender and are removed only one by one (DELETE /suppressions/{email}) when the reason is gone."},"unauthorized":{"meaning":"No valid credentials: the API key is unknown, revoked, expired, idle for 180 days, its creator left the company, or the HTTP Basic user name is not an active member.","fix":"Check the key and the header (`Authorization: Bearer pst_...`). Create a new key in Nastavení › Datové zdroje › API klíče."},"unavailable":{"meaning":"A service the action needs is not available (`mail`: the mail backend refused a test e-mail, `data_key`: encryption key for webhook passwords missing).","fix":"Retry later; tell support when it persists."},"upstream":{"meaning":"An outside service failed (downloading a file by URL, the stock photo service); `params.reason` says why.","fix":"Check the URL or retry later."},"validation":{"meaning":"A parameter or body field is missing, has the wrong type or an invalid value. `message` names it (`email`, `definition.1.0.listId`, `graph` ...), `params` may carry details such as `params.issues` of an automation graph or `params.reason`.","fix":"Fix the named field. For 415 `contentType` send `Content-Type: application/json`; for `body` send a JSON object."}},"paths":{"/api/v1/tenants/{tenant}/contacts":{"get":{"operationId":"contacts_list","summary":"List contacts (search, filters, paging)","tags":["Contacts"],"description":"Searches contacts. Filters: q (e-mail, name or #id), tab (all, bounced, no_list, blacklisted, complained), status (pending, subscribed, unsubscribed, bounced, complained), engagement (A to E, N), shop. Paged, newest first.\n\nContract notes (cs):\n\nParametry (všechny volitelné):\n• `q`: e-mail, jméno, příjmení nebo `#id`; bez ohledu na velká písmena a diakritiku, více slov = všechna musí sedět.\n• `tab`: `all` (výchozí), `bounced`, `no_list`, `blacklisted`, `complained`.\n• `status`: `pending`, `subscribed`, `unsubscribed`, `bounced`, `complained` (hodnoty `contacts.status`).\n• `engagement`: `A` až `E`, `N`.\n• `shop`: id e-shopu tenanta (hlavní e-shop kontaktu `contacts.shop_id`); bez parametru = všechny e-shopy. E-shop jiného tenanta = 400.\n• `ff.<pole>.<operátor>=<hodnota>` (SE Filtry každého pole, KG-05, opakovatelné, nejvýš 40): pole `id, email, firstName, lastName, createdAt, domain, titleBefore, titleAfter, vocativeFirstName, vocativeLastName, salutation, gender, company, city, street, country, postalCode, phone, mobile, language, blacklisted, hardBounce, softBounces, birthday, nameDay` a `cf_<id>`; operátor podle typu: text `contains` (bez ohledu na velikost písmen a diakritiku), číslo a datum `gte` / `lte` (datum YYYY-MM-DD, Do = celý den), výčet `in` (čárkami, `gender`: `none`, `m`, `f`), ano/ne `is` (`1` / `0`). Vlastní pole: číslo, datum a zaškrtávátko podle typu, ostatní jako text. Neznámé pole, operátor nebo hodnota = 400 `validation` / `ff.<pole>.<operátor>`.\n• `page`, `pageSize`.\nŘazení: nejnovější (nejvyšší id) první.\n\nPoložka `ContactListItem`: `{ \"id\", \"email\", \"firstName\", \"lastName\", \"status\", \"engagement\", \"shopId\", \"suppressionReason\", \"createdAt\" }`. `suppressionReason` je vyplněný, když je adresa v `suppressions` (důvody jako v DB).\n\nMapování záložek gridu na data (návrh, potvrdit s jádrem):\n\n| tab | Text v UI | Podmínka |\n|---|---|---|\n| `bounced` | Vrácené | `status = 'bounced'` |\n| `no_list` | Bez seznamu | žádný řádek v `list_memberships` |\n| `blacklisted` | Na blacklistu | adresa v `suppressions` s `reason = 'manual'` |\n| `complained` | Vyloučené | `status = 'complained'` |","parameters":[{"$ref":"#/components/parameters/tenant"},{"name":"createdFrom","in":"query","required":false,"schema":{"type":"string"}},{"name":"createdTo","in":"query","required":false,"schema":{"type":"string"}},{"name":"engagement","in":"query","required":false,"schema":{"type":"string"},"description":"Engagement grade A to E or N (unknown)."},{"name":"engagementOp","in":"query","required":false,"schema":{"type":"string"}},{"name":"fields","in":"query","required":false,"schema":{"type":"string"}},{"name":"import","in":"query","required":false,"schema":{"type":"string"}},{"name":"list","in":"query","required":false,"schema":{"type":"string"}},{"name":"listOp","in":"query","required":false,"schema":{"type":"string"}},{"$ref":"#/components/parameters/page"},{"$ref":"#/components/parameters/pageSize"},{"name":"q","in":"query","required":false,"schema":{"type":"string","maxLength":200},"description":"Search text."},{"name":"shop","in":"query","required":false,"schema":{"type":"string"},"description":"Shop id filter."},{"$ref":"#/components/parameters/sort"},{"name":"source","in":"query","required":false,"schema":{"type":"string"}},{"name":"sourceOp","in":"query","required":false,"schema":{"type":"string"}},{"name":"state","in":"query","required":false,"schema":{"type":"string"}},{"name":"stateOp","in":"query","required":false,"schema":{"type":"string"}},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["pending","subscribed","unsubscribed","bounced","complained"]},"description":"Contact status."},{"name":"tab","in":"query","required":false,"schema":{"type":"string","enum":["all","bounced","no_list","blacklisted","complained"]},"description":"Tab filter."}],"x-postarna-permission":{"module":"contacts","level":"read","keyScope":"read or full"},"x-mcp-tool":"search_contacts","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/ContactListItem"}},"total":{"type":"number"},"page":{"type":"number"},"pageSize":{"type":"number"}},"required":["items","total","page","pageSize"]},"examples":{"contacts-search":{"summary":"docs/api-examples/contacts.json search","value":{"items":[{"id":"1202","email":"jana.novakova@example.cz","firstName":"Jana","lastName":"Nováková","status":"subscribed","engagement":"N","excluded":false,"shopId":"3","suppressionReason":null,"blacklisted":false,"blacklistedAt":null,"state":"active","createdAt":"2026-10-01T08:00:00.000Z"}],"total":1,"page":1,"pageSize":25}}}}}},"400":{"description":"`validation` (message: q)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"getting-started-bad-page-size":{"summary":"docs/api-examples/getting-started.json bad-page-size","value":{"error":{"code":"validation","message":"pageSize"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"contacts_create","summary":"Create a contact","tags":["Contacts"],"description":"Creates a contact by hand (like one import row). Needs a purpose of processing and consentConfirmed true (the user confirms a documented consent). Other profile fields by their API names: titleBefore, titleAfter, mobile, companyId, street, postalCode, country, nameDay, note, salutation, salutationFormal, vocativeFirstName, vocativeLastName, affiliateId.\n\nContract notes (cs):\n\nRuční založení kontaktu (UI: „Vytvořit kontakt\"), jako jeden řádek importu. Tělo `{ \"email\", \"shopId\", \"purposeId\", \"consentConfirmed\", \"firstName\"?, \"lastName\"?, … ostatní `df_*` profilová pole nepovinně, \"customFields\"?, \"listIds\"? }`.\n• `purposeId` = účel tenanta (400 `purposeId`), `consentConfirmed` musí být `true` (400 `consentConfirmed`; na rozdíl od importu tu není opt-out režim, pole jsou vždy povinná). `listIds` jen ruční a formulářové seznamy (400 `listIds`). Adresa, která už je kontaktem, vrátí 409 `email`.\n• Nový kontakt: `source` = `manual`, `consentBasis` = `imported` (stejně jako u importu), jazyk bez vlastní hodnoty padá na jazyk e-shopu. Adresa už vrácená nebo potlačená dostane odpovídající stav (`unsubscribed` apod.) a potlačení zůstává; jinak `subscribed`.\n• Odpověď 201 `ContactDetail`.","parameters":[{"$ref":"#/components/parameters/tenant"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactCreateInput"},"examples":{"abandoned-cart-contact":{"summary":"docs/api-examples/abandoned-cart.json contact","value":{"email":"lucie.mala@example.cz","shopId":"1","purposeId":"1","consentConfirmed":true,"firstName":"Lucie","vocativeFirstName":"Lucie"}}}}},"description":"Required by the server: email, shopId."},"x-postarna-permission":{"module":"contacts","level":"full","keyScope":"full"},"x-mcp-confirm":"consent","x-mcp-tool":"create_contact","responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactDetail"},"examples":{"abandoned-cart-contact":{"summary":"docs/api-examples/abandoned-cart.json contact","value":{"id":"1201","email":"lucie.mala@example.cz","firstName":"Lucie","lastName":"","status":"subscribed","engagement":"N","excluded":false,"shopId":"1","suppressionReason":null,"blacklisted":false,"blacklistedAt":null,"state":null,"createdAt":"2026-10-01T08:00:00.000Z","phone":"","mobile":"","titleBefore":"","titleAfter":"","salutation":"","salutationFormal":"","vocativeFirstName":"Lucie","vocativeLastName":"","gender":null,"language":"cs","company":"","companyId":"","street":"","city":"","postalCode":"","note":"","affiliateId":"","country":null,"birthday":null,"nameDay":null,"source":"manual","consentBasis":"imported","statusAt":"2026-10-01T08:00:00.000Z","consentAt":"2026-10-01T08:00:00.000Z","consentWeak":false,"softBounces":0,"lastSentAt":null,"lastOpenAt":null,"lastClickAt":null,"suppressedAt":null,"engagementAt":null,"suppressionNote":null,"customFields":{},"updatedAt":"2026-10-01T08:00:00.000Z","stats90d":{"received":0,"opened":0,"clicked":0,"bestHour":null},"lists":[],"recentActivities":[{"id":"consent-1","type":"status_change","at":"2026-10-01T08:00:00.000Z","title":"purpose_assigned"}],"orders":[],"orderTotals":[],"editedFields":[],"sources":[]}}}}}},"400":{"description":"`validation`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/api/v1/tenants/{tenant}/contacts/bulk":{"post":{"operationId":"contacts_bulk_start","summary":"Start a bulk action over contacts in the background","tags":["Contacts"],"description":"SE's bulk actions of the contacts grid as a background job (core/contacts_bulk.py, KG-08): body {action: add_to_list | blacklist | delete, contactIds: [ids] (the ticked rows) or all: true (every contact matching the grid filters given as the query string of GET /contacts), listIds for add_to_list}. Contacts on the blacklist are left out. 202 {jobId, count}; the end comes as the notification contacts_bulk_done.","parameters":[{"$ref":"#/components/parameters/tenant"},{"name":"createdFrom","in":"query","required":false,"schema":{"type":"string"}},{"name":"createdTo","in":"query","required":false,"schema":{"type":"string"}},{"name":"engagement","in":"query","required":false,"schema":{"type":"string"}},{"name":"engagementOp","in":"query","required":false,"schema":{"type":"string"}},{"name":"fields","in":"query","required":false,"schema":{"type":"string"}},{"name":"import","in":"query","required":false,"schema":{"type":"string"}},{"name":"list","in":"query","required":false,"schema":{"type":"string"}},{"name":"listOp","in":"query","required":false,"schema":{"type":"string"}},{"$ref":"#/components/parameters/page"},{"$ref":"#/components/parameters/pageSize"},{"$ref":"#/components/parameters/q"},{"$ref":"#/components/parameters/shop"},{"$ref":"#/components/parameters/sort"},{"name":"source","in":"query","required":false,"schema":{"type":"string"}},{"name":"sourceOp","in":"query","required":false,"schema":{"type":"string"}},{"name":"state","in":"query","required":false,"schema":{"type":"string"}},{"name":"stateOp","in":"query","required":false,"schema":{"type":"string"}},{"name":"status","in":"query","required":false,"schema":{"type":"string"},"description":"Status filter."},{"name":"tab","in":"query","required":false,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactsBulkRequest"}}}},"x-postarna-permission":{"module":"contacts","level":"full","keyScope":"full"},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactsBulkStarted"}}}},"400":{"description":"`validation` (message: all, contactIds, q)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/api/v1/tenants/{tenant}/contacts/duplicates":{"get":{"operationId":"contacts_duplicates","summary":"Find possible duplicate contacts","tags":["Contacts"],"description":"Possible duplicates (core/contacts.possible_duplicates): groups of contacts with the same phone or the same name and company. A report only, nothing is merged.\n\nContract notes (cs):\n\nMožné duplicity: `{ \"groups\": [ { \"kind\": \"phone\" | \"name_company\", \"contacts\": [ { \"id\", \"email\", \"firstName\", \"lastName\", \"company\", \"phone\", \"status\", \"createdAt\" } ] } ], \"truncated\" }`. Skupina = kontakty se stejným telefonem (telefon nebo mobil, posledních 9 číslic) nebo se stejným jménem, příjmením a firmou (bez diakritiky a velikosti písmen), každý s jiným e-mailem. Nejvýš 200 skupin po 20 kontaktech. Jen přehled, nic se nesloučí.","parameters":[{"$ref":"#/components/parameters/tenant"}],"x-postarna-permission":{"module":"contacts","level":"read","keyScope":"read or full"},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DuplicateReport"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/api/v1/tenants/{tenant}/contacts/summary":{"get":{"operationId":"contacts_summary","summary":"Count contacts by status","tags":["Contacts"],"description":"Contract notes (cs):\n\n`{ \"total\": 240, \"byStatus\": { \"pending\": 0, \"subscribed\": 0, \"unsubscribed\": 0, \"bounced\": 0, \"complained\": 0 }, \"blacklisted\": 0 }`","parameters":[{"$ref":"#/components/parameters/tenant"},{"name":"createdFrom","in":"query","required":false,"schema":{"type":"string"}},{"name":"createdTo","in":"query","required":false,"schema":{"type":"string"}},{"name":"engagement","in":"query","required":false,"schema":{"type":"string"}},{"name":"engagementOp","in":"query","required":false,"schema":{"type":"string"}},{"name":"fields","in":"query","required":false,"schema":{"type":"string"}},{"name":"list","in":"query","required":false,"schema":{"type":"string"}},{"name":"listOp","in":"query","required":false,"schema":{"type":"string"}},{"$ref":"#/components/parameters/page"},{"$ref":"#/components/parameters/pageSize"},{"$ref":"#/components/parameters/q"},{"$ref":"#/components/parameters/shop"},{"$ref":"#/components/parameters/sort"},{"name":"source","in":"query","required":false,"schema":{"type":"string"}},{"name":"sourceOp","in":"query","required":false,"schema":{"type":"string"}},{"name":"state","in":"query","required":false,"schema":{"type":"string"}},{"name":"stateOp","in":"query","required":false,"schema":{"type":"string"}},{"name":"status","in":"query","required":false,"schema":{"type":"string"},"description":"Status filter."},{"name":"tab","in":"query","required":false,"schema":{"type":"string"}}],"x-postarna-permission":{"module":"contacts","level":"read","keyScope":"read or full"},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactSummary"}}}},"400":{"description":"`validation`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/api/v1/tenants/{tenant}/contacts/{contact_id}":{"delete":{"operationId":"contacts_delete","summary":"Delete a contact","tags":["Contacts"],"description":"Deletes a contact (its suppression stays, so it is never mailed again by mistake). For a data erasure request use erase_contact.\n\n204, 404, or 423 recently_sent: a campaign e-mail to the contact is queued or went out within 24 hours (SE's HTTP 423, core/contact_data.py RECENT_SEND).","parameters":[{"$ref":"#/components/parameters/tenant"},{"name":"contact_id","in":"path","required":true,"description":"Id of the contact (string of digits).","schema":{"type":"string"}}],"x-postarna-permission":{"module":"contacts","level":"full","keyScope":"full"},"x-mcp-confirm":"delete","x-mcp-tool":"delete_contact","responses":{"204":{"description":"No content"},"400":{"description":"`validation`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"423":{"description":"`recently_sent`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}},"get":{"operationId":"contact_detail","summary":"Get a contact","tags":["Contacts"],"description":"Contact card: profile fields, status and consent, lists, last 90 days of e-mail activity, recent activities and orders (totals per currency).\n\nContract notes (cs):\n\n`ContactDetail` = `ContactListItem` a navíc:\n• systémová pole `df_*`: `phone`, `mobile`, `titleBefore`, `titleAfter`, `salutation`, `salutationFormal`, `vocativeFirstName`, `vocativeLastName`, `gender` (`f`, `m`, null), `language`, `company`, `companyId`, `street`, `city`, `postalCode`, `country`, `birthday` (`YYYY-MM-DD`), `nameDay` (`MM-DD`), `note`, `affiliateId`;\n• původ a souhlas: `source` (`erp_order`, `signup_form`, `import`, `api`, `manual`, `shoptet`, `smartemailing`), `consentBasis` (`soft_opt_in_customer`, `double_opt_in`, `imported`, `none` = zákazník bez souhlasu), `consentWeak`, `sources` (záznamy zdrojů `{ source, consent, consentAt, firstSeenAt, lastSeenAt }`: co o souhlasu říká každý zdroj, ze kterého kontakt přišel; platí nejpřísnější stav, odhlášení a blacklist kdekoli vyhrává), `editedFields` (pole, která upravil člověk v Poštárně; import ani synchronizace je nepřepíše), `statusAt` (čas poslední změny stavu, vždy vyplněný), `consentAt` (čas posledního souhlasu, null bez něj);\n• doručování: `softBounces`, `lastSentAt`, `lastOpenAt`, `lastClickAt`, `suppressedAt`;\n• `stats90d`: `{ \"received\", \"opened\", \"clicked\", \"bestHour\" }` za posledních 90 dní (`bestHour` 0 až 23 v zóně tenanta, null bez dat);\n• `lists`: `[ { \"listId\", \"name\", \"state\", \"changedAt\", \"listSize\" } ]`, `state` = `confirmed`, `unconfirmed`, `unsubscribed`, `removed`;\n• `recentActivities`: `[ { \"id\", \"type\", \"at\", \"title\" } ]`, nejnovější první, max 20; `type` = `sent`, `opened`, `clicked`, `unsubscribed`, `doi`, `form`, `order`, `status_change`, `note`;\n• `orders`: max 20, nejnovější první: `{ \"id\", \"shopId\", \"code\", \"status\", \"sourceStatus\", \"currency\", \"totalWithVat\", \"orderedAt\", \"paidAt\", \"items\": [ { \"name\", \"quantity\", \"priceWithVat\" } ], \"itemCount\" }`; `status` = `pending`, `completed`, `cancelled`, `other`, `items` jen první 3 položky;\n• `orderTotals`: `[ { \"currency\", \"count\", \"totalWithVat\" } ]`, jeden řádek na měnu, součet objednávek bez stornovaných. `totalWithVat` je cena s DPH tak, jak ji poslal e-shop (zda obsahuje dopravu, určí konektor; v UI zatím „Celkem s DPH“).","parameters":[{"$ref":"#/components/parameters/tenant"},{"name":"contact_id","in":"path","required":true,"description":"Id of the contact (string of digits).","schema":{"type":"string"}}],"x-postarna-permission":{"module":"contacts","level":"read","keyScope":"read or full"},"x-mcp-tool":"get_contact","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactDetail"}}}},"400":{"description":"`validation`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}},"patch":{"operationId":"contacts_update","summary":"Update a contact","tags":["Contacts"],"description":"Changes profile fields and custom fields of a contact (never its e-mail, status or consent). Other profile fields by their API names: titleBefore, titleAfter, mobile, companyId, street, postalCode, country, nameDay, note, salutation, salutationFormal, vocativeFirstName, vocativeLastName, affiliateId.\n\nContract notes (cs):\n\nStejná profilová pole a `customFields` jako založení, žádné nejsou povinné; mění jen profil a vlastní pole, nikdy stav, souhlas ani adresu (`email` v těle = 400 `email`). Neznámý kontakt 404.","parameters":[{"$ref":"#/components/parameters/tenant"},{"name":"contact_id","in":"path","required":true,"description":"Id of the contact (string of digits).","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactPatch"},"examples":{"contacts-update":{"summary":"docs/api-examples/contacts.json update","value":{"phone":"+420 600 000 000","customFields":{}}}}}}},"x-postarna-permission":{"module":"contacts","level":"full","keyScope":"full"},"x-mcp-confirm":"trigger","x-mcp-tool":"update_contact","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactDetail"},"examples":{"contacts-update":{"summary":"docs/api-examples/contacts.json update","value":{"id":"1202","email":"jana.novakova@example.cz","firstName":"Jana","lastName":"Nováková","status":"subscribed","engagement":"N","excluded":false,"shopId":"3","suppressionReason":null,"blacklisted":false,"blacklistedAt":null,"state":null,"createdAt":"2026-10-01T08:00:00.000Z","phone":"+420 600 000 000","mobile":"","titleBefore":"","titleAfter":"","salutation":"","salutationFormal":"","vocativeFirstName":"","vocativeLastName":"","gender":null,"language":"cs","company":"","companyId":"","street":"","city":"Brno","postalCode":"","note":"","affiliateId":"","country":null,"birthday":null,"nameDay":null,"source":"manual","consentBasis":"imported","statusAt":"2026-10-01T08:00:00.000Z","consentAt":"2026-10-01T08:00:00.000Z","consentWeak":false,"softBounces":0,"lastSentAt":null,"lastOpenAt":null,"lastClickAt":null,"suppressedAt":null,"engagementAt":null,"suppressionNote":null,"customFields":{},"updatedAt":"2026-10-01T08:00:00.000Z","stats90d":{"received":0,"opened":0,"clicked":0,"bestHour":null},"lists":[],"recentActivities":[{"id":"consent-2","type":"status_change","at":"2026-10-01T08:00:00.000Z","title":"purpose_assigned"}],"orders":[],"orderTotals":[],"editedFields":[],"sources":[]}}}}}},"400":{"description":"`validation`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/api/v1/tenants/{tenant}/contacts/{contact_id}/activities":{"get":{"operationId":"contact_activities","summary":"List activities of a contact","tags":["Contacts"],"parameters":[{"$ref":"#/components/parameters/tenant"},{"name":"contact_id","in":"path","required":true,"description":"Id of the contact (string of digits).","schema":{"type":"string"}},{"name":"from","in":"query","required":false,"schema":{"type":"string"},"description":"Start of the period (YYYY-MM-DD local day, or ISO time where noted)."},{"$ref":"#/components/parameters/page"},{"$ref":"#/components/parameters/pageSize"},{"$ref":"#/components/parameters/q"},{"name":"to","in":"query","required":false,"schema":{"type":"string"},"description":"End of the period (YYYY-MM-DD local day, or ISO time where noted)."},{"name":"type","in":"query","required":false,"schema":{"type":"string"}}],"x-postarna-permission":{"module":"contacts","level":"read","keyScope":"read or full"},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/ContactActivity"}},"total":{"type":"number"},"page":{"type":"number"},"pageSize":{"type":"number"}},"required":["items","total","page","pageSize"]}}}},"400":{"description":"`validation` (message: q, type)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"contact_activity_add","summary":"Add a manual activity (note) to a contact","tags":["Contacts"],"description":"Adds a note, call, deal or other activity to a contact.","parameters":[{"$ref":"#/components/parameters/tenant"},{"name":"contact_id","in":"path","required":true,"description":"Id of the contact (string of digits).","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomActivityInput"}}},"description":"Required by the server: kind, title."},"x-postarna-permission":{"module":"contacts","level":"full","keyScope":"full"},"x-mcp-tool":"add_contact_activity","responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactActivity"}}}},"400":{"description":"`validation`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/api/v1/tenants/{tenant}/contacts/{contact_id}/automation-runs":{"get":{"operationId":"contact_automation_runs","summary":"The real journey of a contact through the automations (runs and steps)","tags":["Contacts"],"description":"The real journey of one contact through the automations (contact card, tab Automatizace): its runs, newest first, each with {automation: {id, name, status}, nodes: {key: {type, name}}} and its real steps (outcome, branch, next node, reason). Paged. 404 for a contact of another tenant.","parameters":[{"$ref":"#/components/parameters/tenant"},{"name":"contact_id","in":"path","required":true,"description":"Id of the contact (string of digits).","schema":{"type":"string"}},{"$ref":"#/components/parameters/page"},{"$ref":"#/components/parameters/pageSize"}],"x-postarna-permission":{"module":"campaigns","level":"read","keyScope":"read or full"},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/ContactAutomationRun"}},"total":{"type":"number"},"page":{"type":"number"},"pageSize":{"type":"number"}},"required":["items","total","page","pageSize"]}}}},"400":{"description":"`validation`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/api/v1/tenants/{tenant}/contacts/{contact_id}/blacklist":{"delete":{"operationId":"contact_unblacklist","summary":"Remove a contact from the blacklist","tags":["Contacts"],"description":"Takes the contact off the blacklist (it stays unsubscribed until it subscribes again).\n\nCard menu \"Odebrat z blacklistu\": 204; 404 when the contact is not on the blacklist; 409 suppression_locked when its suppression has another reason (only a confirmed double opt-in lifts those).","parameters":[{"$ref":"#/components/parameters/tenant"},{"name":"contact_id","in":"path","required":true,"description":"Id of the contact (string of digits).","schema":{"type":"string"}}],"x-postarna-permission":{"module":"contacts","level":"full","keyScope":"full"},"x-mcp-confirm":"delete","x-mcp-tool":"unblacklist_contact","responses":{"204":{"description":"No content"},"400":{"description":"`validation`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`suppression_locked`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"contact_blacklist","summary":"Blacklist a contact","tags":["Contacts"],"description":"Puts the contact on the blacklist (manual suppression: never mailed).\n\nCard menu \"Přidat na blacklist\" (KK-02): 201 newly added, 200 already there (the note stays); 409 suppression_locked with params.reason when the address is suppressed for another reason.","parameters":[{"$ref":"#/components/parameters/tenant"},{"name":"contact_id","in":"path","required":true,"description":"Id of the contact (string of digits).","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"note":{"type":"string"}},"required":["note"]}}}},"x-postarna-permission":{"module":"contacts","level":"full","keyScope":"full"},"x-mcp-tool":"blacklist_contact","responses":{"201":{"description":"Created (200 when it already existed or nothing changed)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlacklistResult"}}}},"200":{"description":"OK: it already existed or nothing changed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlacklistResult"}}}},"400":{"description":"`validation` (message: email, note)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`suppression_locked`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/api/v1/tenants/{tenant}/contacts/{contact_id}/coupons":{"get":{"operationId":"contact_coupons","summary":"List coupons given to a contact","tags":["Contacts"],"description":"The codes of one contact (tab Slevové kupóny): code, stack, status, assigned and used.","parameters":[{"$ref":"#/components/parameters/tenant"},{"name":"contact_id","in":"path","required":true,"description":"Id of the contact (string of digits).","schema":{"type":"string"}}],"x-postarna-permission":{"module":"contacts","level":"read","keyScope":"read or full"},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/ContactCoupon"}}},"required":["items"]}}}},"400":{"description":"`validation`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/api/v1/tenants/{tenant}/contacts/{contact_id}/erase":{"post":{"operationId":"contact_erase","summary":"Erase a contact (GDPR, irreversible)","tags":["Contacts"],"description":"Erasure on request (GDPR): erases the person and their data, keeps only a keyed hash that blocks a new import.\n\nErasure on request: the tenant's data in the request's transaction, then the consent log as the platform role (only after the tenant transaction committed). When that second step fails, the same request again finishes it (review of batch 4): the contact is gone, but its consent log rows still name the address (cd.erasure_state); a contact that was never erased here stays 404.","parameters":[{"$ref":"#/components/parameters/tenant"},{"name":"contact_id","in":"path","required":true,"description":"Id of the contact (string of digits).","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"confirm":{"type":"boolean"}},"required":["confirm"]}}},"description":"Required by the server: confirm."},"x-postarna-permission":{"module":"contacts","level":"full","keyScope":"full"},"x-mcp-confirm":"erase","x-mcp-tool":"erase_contact","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EraseResult"}}}},"400":{"description":"`validation` (message: confirm)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/api/v1/tenants/{tenant}/contacts/{contact_id}/export":{"get":{"operationId":"contact_export","summary":"Export all data of a contact (GDPR access)","tags":["Contacts"],"description":"Everything stored about one contact (data access request), as JSON.","parameters":[{"$ref":"#/components/parameters/tenant"},{"name":"contact_id","in":"path","required":true,"description":"Id of the contact (string of digits).","schema":{"type":"string"}}],"x-postarna-permission":{"module":"contacts","level":"full","keyScope":"full"},"x-mcp-tool":"export_contact_data","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":{}}}}},"400":{"description":"`validation`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/api/v1/tenants/{tenant}/contacts/{contact_id}/neighbors":{"get":{"operationId":"contact_neighbors","summary":"Previous and next contact of a filtered list","tags":["Contacts"],"description":"Contract notes (cs):\n\nStejné filtry jako seznam (bez `page` a `pageSize`). Odpověď `{ \"prevId\": \"…\" | null, \"nextId\": \"…\" | null }` pro šipky předchozí a další kontakt v detailu.","parameters":[{"$ref":"#/components/parameters/tenant"},{"name":"contact_id","in":"path","required":true,"description":"Id of the contact (string of digits).","schema":{"type":"string"}},{"name":"createdFrom","in":"query","required":false,"schema":{"type":"string"}},{"name":"createdTo","in":"query","required":false,"schema":{"type":"string"}},{"name":"engagement","in":"query","required":false,"schema":{"type":"string"}},{"name":"engagementOp","in":"query","required":false,"schema":{"type":"string"}},{"name":"fields","in":"query","required":false,"schema":{"type":"string"}},{"name":"import","in":"query","required":false,"schema":{"type":"string"}},{"name":"list","in":"query","required":false,"schema":{"type":"string"}},{"name":"listOp","in":"query","required":false,"schema":{"type":"string"}},{"$ref":"#/components/parameters/page"},{"$ref":"#/components/parameters/pageSize"},{"$ref":"#/components/parameters/q"},{"$ref":"#/components/parameters/shop"},{"$ref":"#/components/parameters/sort"},{"name":"source","in":"query","required":false,"schema":{"type":"string"}},{"name":"sourceOp","in":"query","required":false,"schema":{"type":"string"}},{"name":"state","in":"query","required":false,"schema":{"type":"string"}},{"name":"stateOp","in":"query","required":false,"schema":{"type":"string"}},{"name":"status","in":"query","required":false,"schema":{"type":"string"},"description":"Status filter."},{"name":"tab","in":"query","required":false,"schema":{"type":"string"}}],"x-postarna-permission":{"module":"contacts","level":"read","keyScope":"read or full"},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactNeighbors"}}}},"400":{"description":"`validation` (message: q)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/api/v1/tenants/{tenant}/contacts/{contact_id}/purposes":{"get":{"operationId":"contact_purposes","summary":"List consents (purposes) of a contact","tags":["Contacts"],"description":"Consents (purposes of processing) of a contact.","parameters":[{"$ref":"#/components/parameters/tenant"},{"name":"contact_id","in":"path","required":true,"description":"Id of the contact (string of digits).","schema":{"type":"string"}}],"x-postarna-permission":{"module":"contacts","level":"read","keyScope":"read or full"},"x-mcp-tool":"get_contact_consents","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/ContactConsent"}}},"required":["items"]}}}},"400":{"description":"`validation`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"contact_purpose_assign","summary":"Record a consent (purpose) of a contact","tags":["Contacts"],"parameters":[{"$ref":"#/components/parameters/tenant"},{"name":"contact_id","in":"path","required":true,"description":"Id of the contact (string of digits).","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"validFrom":{"type":"string"},"validUntil":{"type":"string"},"purposeId":{"type":"string"}},"required":["purposeId"]}}},"description":"Required by the server: purposeId."},"x-postarna-permission":{"module":"contacts","level":"full","keyScope":"full"},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/ContactConsent"}}},"required":["items"]}}}},"400":{"description":"`validation`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/api/v1/tenants/{tenant}/contacts/{contact_id}/purposes/{consent_id}/withdraw":{"post":{"operationId":"contact_purpose_withdraw","summary":"Withdraw a consent of a contact","tags":["Contacts"],"parameters":[{"$ref":"#/components/parameters/tenant"},{"name":"contact_id","in":"path","required":true,"description":"Id of the contact (string of digits).","schema":{"type":"string"}},{"name":"consent_id","in":"path","required":true,"description":"Id of the consent (string of digits).","schema":{"type":"string"}}],"x-postarna-permission":{"module":"contacts","level":"full","keyScope":"full"},"x-mcp-confirm":"withdraw","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/ContactConsent"}}},"required":["items"]}}}},"400":{"description":"`validation`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict` (message: status)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/api/v1/tenants/{tenant}/contacts/{contact_id}/segments":{"get":{"operationId":"contact_segments","summary":"List segments a contact belongs to","tags":["Contacts"],"parameters":[{"$ref":"#/components/parameters/tenant"},{"name":"contact_id","in":"path","required":true,"description":"Id of the contact (string of digits).","schema":{"type":"string"}}],"x-postarna-permission":{"module":"contacts","level":"read","keyScope":"read or full"},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/ContactSegment"}}},"required":["items"]}}}},"400":{"description":"`validation`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/api/v1/tenants/{tenant}/contacts/{contact_id}/unsubscribe":{"post":{"operationId":"contacts_unsubscribe","summary":"Unsubscribe a contact from everything","tags":["Contacts"],"description":"Unsubscribes a contact from all e-mails, as its own unsubscribe link would (only a new double opt-in subscribes it again).\n\nUnsubscribes the contact from everything, as its own unsubscribe link would (store.unsubscribe: status unsubscribed, the address suppressed, queued e-mails skipped, the consent log). Only a new double opt-in subscribes it again. Body optional {note}: up to 500 characters for the consent log. Answers the contact detail.","parameters":[{"$ref":"#/components/parameters/tenant"},{"name":"contact_id","in":"path","required":true,"description":"Id of the contact (string of digits).","schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"note":{"type":"string","description":"Reason for the consent log, up to 500 characters.","maxLength":500}}},"examples":{"contacts-unsubscribe":{"summary":"docs/api-examples/contacts.json unsubscribe","value":{"note":"Požádala telefonicky"}}}}}},"x-postarna-permission":{"module":"contacts","level":"full","keyScope":"full"},"x-mcp-confirm":"unsubscribe","x-mcp-tool":"unsubscribe_contact","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContactDetail"},"examples":{"contacts-unsubscribe":{"summary":"docs/api-examples/contacts.json unsubscribe","value":{"id":"1202","email":"jana.novakova@example.cz","firstName":"Jana","lastName":"Nováková","status":"unsubscribed","engagement":"N","excluded":false,"shopId":"3","suppressionReason":"unsubscribed","blacklisted":false,"blacklistedAt":null,"state":null,"createdAt":"2026-10-01T08:00:00.000Z","phone":"+420 600 000 000","mobile":"","titleBefore":"","titleAfter":"","salutation":"","salutationFormal":"","vocativeFirstName":"","vocativeLastName":"","gender":null,"language":"cs","company":"","companyId":"","street":"","city":"Brno","postalCode":"","note":"","affiliateId":"","country":null,"birthday":null,"nameDay":null,"source":"manual","consentBasis":"imported","statusAt":"2026-10-01T08:00:00.000Z","consentAt":"2026-10-01T08:00:00.000Z","consentWeak":false,"softBounces":0,"lastSentAt":null,"lastOpenAt":null,"lastClickAt":null,"suppressedAt":"2026-10-01T08:00:00.000Z","engagementAt":null,"suppressionNote":null,"customFields":{},"updatedAt":"2026-10-01T08:00:00.000Z","stats90d":{"received":0,"opened":0,"clicked":0,"bestHour":null},"lists":[{"listId":"41","name":"VIP zákazníci","state":"confirmed","changedAt":"2026-10-01T08:00:00.000Z","listSize":1}],"recentActivities":[{"id":"consent-4","type":"unsubscribed","at":"2026-10-01T08:00:00.000Z","title":"admin"},{"id":"consent-3","type":"status_change","at":"2026-10-01T08:00:00.000Z","title":"suppressed"},{"id":"consent-2","type":"status_change","at":"2026-10-01T08:00:00.000Z","title":"purpose_assigned"}],"orders":[],"orderTotals":[],"editedFields":[],"sources":[]}}}}}},"400":{"description":"`validation` (message: note)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/api/v1/tenants/{tenant}/custom-fields":{"get":{"operationId":"custom_fields_index","summary":"List custom fields","tags":["Contacts"],"description":"Custom contact fields (id, name, type, options).","parameters":[{"$ref":"#/components/parameters/tenant"}],"x-postarna-permission":{"module":"contacts","level":"read","keyScope":"read or full"},"x-mcp-tool":"list_custom_fields","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/CustomField"}}},"required":["items"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"custom_fields_create","summary":"Create a custom field","tags":["Contacts"],"description":"Creates a custom contact field.","parameters":[{"$ref":"#/components/parameters/tenant"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomFieldInput"}}}},"x-postarna-permission":{"module":"contacts","level":"full","keyScope":"full"},"x-mcp-tool":"create_custom_field","responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomField"}}}},"400":{"description":"`validation`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/api/v1/tenants/{tenant}/custom-fields/{field_id}":{"delete":{"operationId":"custom_fields_delete","summary":"Delete a custom field","tags":["Contacts"],"description":"Deletes a custom field and its values.","parameters":[{"$ref":"#/components/parameters/tenant"},{"name":"field_id","in":"path","required":true,"description":"Id of the field (string of digits).","schema":{"type":"string"}}],"x-postarna-permission":{"module":"contacts","level":"full","keyScope":"full"},"x-mcp-confirm":"delete","x-mcp-tool":"delete_custom_field","responses":{"204":{"description":"No content"},"400":{"description":"`validation`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}},"patch":{"operationId":"custom_fields_update","summary":"Update a custom field","tags":["Contacts"],"description":"Renames a custom field or changes its options.","parameters":[{"$ref":"#/components/parameters/tenant"},{"name":"field_id","in":"path","required":true,"description":"Id of the field (string of digits).","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"options":{"type":"array","items":{"type":"string"}}}}}}},"x-postarna-permission":{"module":"contacts","level":"full","keyScope":"full"},"x-mcp-tool":"update_custom_field","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomField"}}}},"400":{"description":"`validation`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/api/v1/tenants/{tenant}/purposes":{"get":{"operationId":"purposes_index","summary":"List purposes of processing (consent types)","tags":["Contacts"],"description":"Purposes of processing (consents) with legal basis and validity.\n\nContract notes (cs):\n\n`{ \"items\": [ { \"id\", \"name\", \"legalBasis\", \"validityDays\" } ] }`, `legalBasis` jako `purposes.legal_basis` (`consent`, `contract`, `legal_obligation`, `legitimate_interest`, `vital_interest`, `public_interest`), `validityDays` null = bez pevné lhůty.\n• Vlna account (migrace 0261, 0262): položka má navíc `validity` `{ \"value\", \"unit\" }` (jednotky SE `seconds`, `minutes`, `hours`, `days`, `weeks`, `months`, `years`; null = bez pevné lhůty; účel uložený dřív ukáže dny, celé roky když jsou dělitelné 365) a `notes`, `consents`, `createdAt`. `POST …/purposes` a `PATCH …/purposes/{id}` přijímají `validity` místo `validityDays` (měsíc = 30 dní, rok = 365 dní jako dosud, nejvýš 36 500 dní; chyba 400 `validity`).\n• `POST …/purposes/{id}/to-contact-list` `{ \"listId\" }` (SE „Přidat ke kontaktům v seznamu“): souhlas s účelem od teď (konec podle délky účelu) pro potvrzené a nepotvrzené členy seznamu, kteří ho ještě nemají; odhlášené a odebrané vynechá, nikoho nepřihlásí. Odpověď `{ \"count\" }`, notifikace `purpose_assigned_list`. Cizí nebo neznámý účel či seznam 404, špatné `listId` 400.\n• `POST /tenants/{t}/notifications/unread` `{ \"ids\": [...] }` (SE „Označit jako nepřečtené“, nejvýš 500 id): notifikace firmy znovu nepřečtené, cizí id se ignorují; 204.\n• `GET /tenants/{t}/running-actions` (SE „Probíhající akce“, UI-03): `{ \"items\": [ { \"id\", \"kind\", \"status\": \"queued\" | \"running\", \"since\" } ] }` = úlohy firmy na pozadí, které běží nebo čekají na spuštění (`import_run`, `import_doi`, `export_run`, `feed_refresh`, `order_feed_refresh`, `se_discover`, `se_sync_suppressions`, `engagement_recompute`), nejstarší první, nejvýš 20; bez obsahu úlohy.\n• `GET …/rfm` → `{ \"enabled\": false, \"requested\", \"requestedAt\" }`; `POST …/rfm/enable-request` (SE „Zapnout RFM analýzu“) zapíše žádost jednou (druhé volání vrátí první čas), nic nezapne.","parameters":[{"$ref":"#/components/parameters/tenant"}],"x-postarna-permission":{"module":"contacts","level":"read","keyScope":"read or full"},"x-mcp-tool":"list_purposes","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/Purpose"}}},"required":["items"]}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"purposes_create","summary":"Create a purpose of processing","tags":["Contacts"],"description":"Creates a purpose of processing.","parameters":[{"$ref":"#/components/parameters/tenant"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PurposeInput"}}}},"x-postarna-permission":{"module":"contacts","level":"full","keyScope":"full"},"x-mcp-tool":"create_purpose","responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Purpose"}}}},"400":{"description":"`validation`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/api/v1/tenants/{tenant}/purposes/{purpose_id}":{"delete":{"operationId":"purposes_delete","summary":"Delete a purpose of processing","tags":["Contacts"],"description":"Deletes an unused purpose of processing.","parameters":[{"$ref":"#/components/parameters/tenant"},{"name":"purpose_id","in":"path","required":true,"description":"Id of the purpose (string of digits).","schema":{"type":"string"}}],"x-postarna-permission":{"module":"contacts","level":"full","keyScope":"full"},"x-mcp-confirm":"delete","x-mcp-tool":"delete_purpose","responses":{"204":{"description":"No content"},"400":{"description":"`validation`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conflict` (message: in_use)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}},"patch":{"operationId":"purposes_update","summary":"Update a purpose of processing","tags":["Contacts"],"description":"Changes a purpose of processing.","parameters":[{"$ref":"#/components/parameters/tenant"},{"name":"purpose_id","in":"path","required":true,"description":"Id of the purpose (string of digits).","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"legalBasis":{"type":"string","enum":["consent","contract","legal_obligation","legitimate_interest","vital_interest","public_interest"]},"validityDays":{"type":["number","null"],"description":"Older callers send whole days; the purpose form sends validity."},"validity":{"anyOf":[{"$ref":"#/components/schemas/ValidityLength"},{"type":"null"}]},"notes":{"type":"string"}}}}}},"x-postarna-permission":{"module":"contacts","level":"full","keyScope":"full"},"x-mcp-tool":"update_purpose","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Purpose"}}}},"400":{"description":"`validation`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/api/v1/tenants/{tenant}/purposes/{purpose_id}/to-contact-list":{"post":{"operationId":"purposes_to_list","summary":"Create a list of the contacts with a purpose","tags":["Contacts"],"description":"SE \"Přidat ke kontaktům v seznamu\": body {listId}; answers {count} of the consents added.","parameters":[{"$ref":"#/components/parameters/tenant"},{"name":"purpose_id","in":"path","required":true,"description":"Id of the purpose (string of digits).","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"listId":{"type":"string"}},"required":["listId"]}}},"description":"Required by the server: listId."},"x-postarna-permission":{"module":"contacts","level":"full","keyScope":"full"},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"count":{"type":"number"}},"required":["count"]}}}},"400":{"description":"`validation` (message: listId)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/api/v1/tenants/{tenant}/search":{"get":{"operationId":"search","summary":"Search contacts, campaigns, templates and more","tags":["Contacts"],"description":"Quick search over contacts and lists (at least 2 characters).\n\nContract notes (cs):\n\nHledání pro paletu ⌘K. `q` aspoň 2 znaky (jinak prázdný výsledek), bez ohledu na velká písmena a diakritiku. Odpověď `{ \"contacts\": [ContactListItem] (max 5), \"lists\": [ { \"id\", \"name\", \"size\" } ] (max 5) }`.","parameters":[{"$ref":"#/components/parameters/tenant"},{"name":"q","in":"query","required":false,"schema":{"type":"string","maxLength":200},"description":"Search text."}],"x-postarna-permission":{"module":"contacts","level":"read","keyScope":"read or full"},"x-mcp-tool":"search","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchResults"}}}},"400":{"description":"`validation` (message: q)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/api/v1/tenants/{tenant}/suppressions":{"get":{"operationId":"suppressions_list","summary":"List suppressed addresses","tags":["Contacts"],"description":"Suppression list (addresses never mailed) with reason.","parameters":[{"$ref":"#/components/parameters/tenant"},{"$ref":"#/components/parameters/page"},{"$ref":"#/components/parameters/pageSize"},{"name":"q","in":"query","required":false,"schema":{"type":"string","maxLength":200},"description":"Search text."},{"name":"reason","in":"query","required":false,"schema":{"type":"string"},"description":"Reason filter (manual, unsubscribed, hard_bounce, complaint...)."}],"x-postarna-permission":{"module":"contacts","level":"read","keyScope":"read or full"},"x-mcp-tool":"list_suppressions","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"email":{"type":"string"},"reason":{"type":"string","description":"manual, unsubscribe, bounce, complaint ..."},"source":{"type":"string"},"detail":{"type":"string"},"blacklisted":{"type":"boolean"},"createdAt":{"type":"string"}},"description":"routes_contacts.suppression_json"}},"total":{"type":"integer"},"page":{"type":"integer"},"pageSize":{"type":"integer"}}},"examples":{"contacts-suppressions":{"summary":"docs/api-examples/contacts.json suppressions","value":{"items":[{"email":"jana.novakova@example.cz","reason":"unsubscribed","source":"admin","detail":"","blacklisted":false,"createdAt":"2026-10-01T08:00:00.000Z"}],"total":1,"page":1,"pageSize":25}}}}}},"400":{"description":"`validation` (message: q)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"suppressions_add","summary":"Suppress an address","tags":["Contacts"],"description":"Adds an address to the suppression list (manual blacklist), also when it is not a contact.","parameters":[{"$ref":"#/components/parameters/tenant"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","description":"Address."},"detail":{"type":"string","description":"Note."}},"required":["email"]}}}},"x-postarna-permission":{"module":"contacts","level":"full","keyScope":"full"},"x-mcp-tool":"add_suppression","responses":{"201":{"description":"Created (200 when it already existed or nothing changed)","content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string"},"reason":{"type":"string","description":"manual, unsubscribe, bounce, complaint ..."},"source":{"type":"string"},"detail":{"type":"string"},"blacklisted":{"type":"boolean"},"createdAt":{"type":"string"},"created":{"type":"boolean"}},"description":"routes_contacts.suppression_json"}}}},"200":{"description":"OK: it already existed or nothing changed","content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string"},"reason":{"type":"string","description":"manual, unsubscribe, bounce, complaint ..."},"source":{"type":"string"},"detail":{"type":"string"},"blacklisted":{"type":"boolean"},"createdAt":{"type":"string"},"created":{"type":"boolean"}},"description":"routes_contacts.suppression_json"}}}},"400":{"description":"`validation` (message: detail, email)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/api/v1/tenants/{tenant}/suppressions/blacklist-import":{"post":{"operationId":"suppressions_blacklist_import","summary":"Import addresses to the blacklist","tags":["Contacts"],"description":"SmartEmailing's blacklist on the tenant's blacklist (core/blacklist.py, migration 0180): body {contacts: the SmartEmailing backup list (blacklisted.json) or {emails: [addresses]}, apply: true to write (default: a dry run that writes nothing)}. Idempotent; nothing is sent, no automation starts, no other suppression or contact changes. 200 with the counts (never an address).","parameters":[{"$ref":"#/components/parameters/tenant"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"apply":{},"contacts":{},"emails":{}}}}}},"x-postarna-permission":{"module":"contacts","level":"full","keyScope":"full"},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"description":"Counts (never addresses), notBlacklisted, applied."}}}},"400":{"description":"`validation` (message: apply, contacts)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/api/v1/tenants/{tenant}/suppressions/{email}":{"delete":{"operationId":"suppressions_remove","summary":"Remove one suppression","tags":["Contacts"],"description":"Removes a manual suppression of an address (other reasons are locked).","parameters":[{"$ref":"#/components/parameters/tenant"},{"name":"email","in":"path","required":true,"description":"E-mail address (URL encoded).","schema":{"type":"string"}}],"x-postarna-permission":{"module":"contacts","level":"full","keyScope":"full"},"x-mcp-confirm":"delete","x-mcp-tool":"remove_suppression","responses":{"204":{"description":"No content"},"400":{"description":"`validation`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`suppression_locked`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"pst_ API key","description":"An API key of the company: Authorization: Bearer pst_... (Nastavení › Datové zdroje › API klíče). Scope read or full, roles of the key."},"basicAuth":{"type":"http","scheme":"basic","description":"HTTP Basic as SmartEmailing: user name = e-mail of an active member of the company, password = the API key."},"oauth2":{"type":"oauth2","description":"OAuth 2.1 for MCP clients (AI assistants) only: authorization code with PKCE S256, dynamic registration.","flows":{"authorizationCode":{"authorizationUrl":"https://www.postarna.cz/oauth/authorize","tokenUrl":"https://www.postarna.cz/oauth/token","refreshUrl":"https://www.postarna.cz/oauth/token","scopes":{"read":"Read only (routes whose permission level is read).","full":"Everything the member's roles allow."}}}}},"parameters":{"tenant":{"name":"tenant","in":"path","required":true,"description":"Public id of the company (tenant), a UUID: GET /api/v3/account-info answers it as data.guid; the app shows it on the API keys page.","schema":{"type":"string","format":"uuid"}},"page":{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1},"description":"Page number, from 1."},"pageSize":{"name":"pageSize","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":25},"description":"Items per page, 1 to 100."},"q":{"name":"q","in":"query","required":false,"schema":{"type":"string","maxLength":200},"description":"Search text (case and diacritics insensitive)."},"shop":{"name":"shop","in":"query","required":false,"schema":{"type":"string"},"description":"Only objects of this shop (shop id)."},"sort":{"name":"sort","in":"query","required":false,"schema":{"type":"string"},"description":"Sort fields separated by commas, \"-\" = descending, at most 3 (e.g. name,-id)."}},"headers":{"X-RateLimit-Limit":{"description":"Size of the bucket nearest to running out (2 500 per second bucket or 100 000 daily).","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in that bucket.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"Unix time when that bucket is full again.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"responses":{"Unauthorized":{"description":"401 unauthorized: missing, unknown, revoked, expired or idle API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"unauthorized","message":"api_key"}}}}},"Forbidden":{"description":"403 forbidden: message tenant (another company), scope (read-only key), api_key (person only), or the module the key's roles lack.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"forbidden","message":"scope"}}}}},"RateLimited":{"description":"429 rate_limited: wait Retry-After seconds.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/X-RateLimit-Reset"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":{"code":"rate_limited","message":"per-second"}}}}}},"schemas":{"Activity":{"type":"object","properties":{"id":{"type":"string"},"type":{"$ref":"#/components/schemas/ActivityType"},"at":{"type":"string"},"title":{"type":"string","description":"Campaign, form or order name; tenant data, shown as is."}},"required":["id","type","at","title"]},"ActivityDetail":{"type":"object","properties":{"email":{"type":["string","null"]},"messageKind":{"type":["string","null"],"enum":["campaign","automation",null]},"campaignId":{"type":["string","null"]},"campaignName":{"type":["string","null"]},"automationId":{"type":["string","null"]},"automationName":{"type":["string","null"]},"templateId":{"type":["string","null"]},"templateName":{"type":["string","null"]},"source":{"type":["string","null"]},"orderId":{"type":["string","null"]}},"required":["email","messageKind","campaignId","campaignName","automationId","automationName","templateId","templateName","source","orderId"],"description":"What the expanded activity card shows (SE: E-mail, Newsletter or Automatizace, Šablona, Zdroj); null = none."},"ActivityType":{"type":"string","enum":["other","unsubscribed","bounced","form","sent","opened","clicked","doi","order","status_change","web_tracking","note","call","deal"]},"AutomationRunStatus":{"type":"string","enum":["active","completed","exited"]},"AutomationStatus":{"type":"string","enum":["active","draft","paused"]},"AutomationStepOutcome":{"type":"string","enum":["expired","queued","failed","skipped","goal","reset","end","yes","no","entered","passed","variant","released","jumped"]},"BlacklistResult":{"type":"object","properties":{"email":{"type":"string"},"reason":{"type":"string","enum":["invalid","manual","unsubscribed","hard_bounce","complaint","soft_bounce_limit"]},"source":{"type":"string"},"detail":{"type":"string","description":"The note why (empty when none was given)."},"createdAt":{"type":"string"},"created":{"type":"boolean","description":"False when the contact was already on the blacklist (its note stays)."}},"required":["email","reason","source","detail","createdAt","created"],"description":"A blacklist entry made from the contact card (POST /contacts/{id}/blacklist)."},"ConsentBasis":{"type":"string","enum":["none","soft_opt_in_customer","double_opt_in","imported"]},"ContactActivity":{"type":"object","properties":{"body":{"type":"string","description":"Text of a custom activity, '' for the others."},"detail":{"allOf":[{"$ref":"#/components/schemas/ActivityDetail"}],"description":"References of the activity (missing from older servers)."},"id":{"type":"string"},"type":{"$ref":"#/components/schemas/ActivityType"},"at":{"type":"string"},"title":{"type":"string","description":"Campaign, form or order name; tenant data, shown as is."}},"required":["body","id","type","at","title"]},"ContactAutomationRun":{"type":"object","properties":{"automation":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"status":{"$ref":"#/components/schemas/AutomationStatus"}},"required":["id","name","status"]},"nodes":{"type":"object","additionalProperties":{"type":"object","properties":{"type":{"type":"string"},"name":{"type":"string"}},"required":["type","name"]},"description":"The nodes of the automation by key: type and name (the name may be empty)."},"id":{"type":"string"},"contact":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string"}},"required":["id","email"]},"status":{"$ref":"#/components/schemas/AutomationRunStatus"},"node":{"type":["string","null"]},"entryKey":{"type":"string"},"enteredAt":{"type":"string"},"dueAt":{"type":["string","null"]},"finishedAt":{"type":["string","null"]},"exitReason":{"type":["string","null"]},"steps":{"type":"array","items":{"type":"object","properties":{"node":{"type":"string"},"outcome":{"$ref":"#/components/schemas/AutomationStepOutcome"},"at":{"type":"string"},"branch":{"type":["string","null"],"enum":["next","yes","no","v1","v2","v3","v4","v5","v6","v7","v8","v9","v10",null]},"nextNode":{"type":["string","null"]},"reason":{"type":["string","null"],"enum":["not_mailable","exception_expired","suppressed","node_window","customizationSyntaxError","contactSetFieldFail",null]},"messageId":{"type":["string","null"]}},"required":["node","outcome","at"]},"description":"The real steps (automation_steps): reason = why a step was skipped or failed, messageId = the e-mail it queued."},"trigger":{"type":["string","null"],"description":"The trigger node the run came in through."}},"required":["automation","nodes","id","contact","status","node","entryKey","enteredAt","dueAt","finishedAt","exitReason","steps"],"description":"A run of one contact with its automation (contact card, tab Automatizace)."},"ContactConsent":{"type":"object","properties":{"id":{"type":"string"},"purposeId":{"type":"string"},"name":{"type":"string"},"legalBasis":{"type":"string","enum":["consent","contract","legal_obligation","legitimate_interest","vital_interest","public_interest"]},"validFrom":{"type":"string"},"validUntil":{"type":["string","null"]},"withdrawnAt":{"type":["string","null"]},"source":{"type":"string"},"state":{"type":"string","enum":["expired","valid","future","withdrawn"]}},"required":["id","purposeId","name","legalBasis","validFrom","validUntil","withdrawnAt","source","state"]},"ContactCoupon":{"type":"object","properties":{"id":{"type":"string"},"code":{"type":"string"},"status":{"$ref":"#/components/schemas/CouponStatus"},"assignedAt":{"type":["string","null"]},"usedAt":{"type":["string","null"]},"stackId":{"type":"string"},"stackName":{"type":"string"},"shopId":{"type":"string"}},"required":["id","code","status","assignedAt","usedAt","stackId","stackName","shopId"]},"ContactCreateInput":{"type":"object","properties":{"email":{"type":"string"},"shopId":{"type":"string"},"purposeId":{"type":"string"},"consentConfirmed":{"type":"boolean"},"listIds":{"type":"array","items":{"type":"string"}},"validFrom":{"type":"string","description":"SE's \"Od kdy účel platí\" (KK-17): a day (YYYY-MM-DD) or an ISO date-time; default now."},"firstName":{"type":"string"},"lastName":{"type":"string"},"titleBefore":{"type":"string"},"titleAfter":{"type":"string"},"salutation":{"type":"string"},"salutationFormal":{"type":"string"},"vocativeFirstName":{"type":"string"},"vocativeLastName":{"type":"string"},"gender":{"type":["string","null"],"enum":["f","m",null]},"language":{"type":"string"},"company":{"type":"string"},"companyId":{"type":"string"},"phone":{"type":"string"},"mobile":{"type":"string"},"street":{"type":"string"},"city":{"type":"string"},"postalCode":{"type":"string"},"country":{"type":["string","null"]},"birthday":{"type":["string","null"]},"nameDay":{"type":["string","null"]},"note":{"type":"string"},"affiliateId":{"type":"string"},"customFields":{"type":"object","additionalProperties":{"anyOf":[{"type":"boolean"},{"type":"string"},{"type":"number"},{"type":"array","items":{"type":"string"}},{"type":"null"}]}}},"required":["email","shopId","purposeId","consentConfirmed"],"description":"A contact added by hand: like a one-row import (purpose and confirmation required)."},"ContactDetail":{"type":"object","properties":{"guid":{"type":["string","null"],"description":"SE's contact GUID (32 hex characters): the grid search finds it as #GUID (KG-07)."},"sources":{"type":"array","items":{"$ref":"#/components/schemas/ContactSourceRecord"},"description":"Source records (CSV import, Shoptet, SmartEmailing ...); the contact's own state is the most restrictive one."},"editedFields":{"type":"array","items":{"type":"string"},"description":"Fields a person edited in Poštárna: imports and syncs never overwrite them."},"phone":{"type":"string"},"mobile":{"type":"string"},"titleBefore":{"type":"string"},"titleAfter":{"type":"string"},"salutation":{"type":"string"},"salutationFormal":{"type":"string"},"vocativeFirstName":{"type":"string"},"vocativeLastName":{"type":"string"},"gender":{"type":["string","null"],"enum":["f","m",null]},"language":{"type":"string"},"company":{"type":"string"},"companyId":{"type":"string"},"street":{"type":"string"},"city":{"type":"string"},"postalCode":{"type":"string"},"country":{"type":["string","null"]},"birthday":{"type":["string","null"]},"nameDay":{"type":["string","null"],"description":"MM-DD"},"note":{"type":"string"},"affiliateId":{"type":"string"},"source":{"$ref":"#/components/schemas/ContactSource"},"consentBasis":{"$ref":"#/components/schemas/ConsentBasis"},"consentWeak":{"type":"boolean"},"statusAt":{"type":"string","description":"When the current status (ContactListItem.status) started; always set."},"consentAt":{"type":["string","null"],"description":"When the current consent was given; null without one (e.g. weak consent)."},"softBounces":{"type":"number"},"lastSentAt":{"type":["string","null"]},"lastOpenAt":{"type":["string","null"]},"lastClickAt":{"type":["string","null"]},"suppressedAt":{"type":["string","null"]},"suppressionNote":{"type":["string","null"],"description":"Note given when the address was put on the blacklist by hand (suppressionReason 'manual'), else null."},"stats90d":{"$ref":"#/components/schemas/ContactStats"},"lists":{"type":"array","items":{"$ref":"#/components/schemas/ContactListMembership"}},"recentActivities":{"type":"array","items":{"$ref":"#/components/schemas/Activity"},"description":"Newest first, at most 20."},"orders":{"type":"array","items":{"$ref":"#/components/schemas/ContactOrder"},"description":"Newest first, at most 20."},"orderTotals":{"type":"array","items":{"$ref":"#/components/schemas/OrderTotal"}},"engagementAt":{"type":["string","null"],"description":"When the engagement grade last changed (weekly recomputation)."},"customFields":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/CustomFieldValue"},"description":"Values of custom fields by field id."},"updatedAt":{"type":"string"},"id":{"type":"string"},"email":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"status":{"type":"string","enum":["pending","subscribed","unsubscribed","bounced","complained","customer"]},"engagement":{"type":"string","enum":["A","B","C","D","E","N"]},"excluded":{"type":"boolean","description":"SE \"Vyloučené\": long inactive yet mailed often (recomputed weekly with the engagement)."},"shopId":{"type":["string","null"],"description":"Main e-shop (contacts.shop_id), null when unknown."},"suppressionReason":{"type":["string","null"],"enum":["invalid","manual","unsubscribed","hard_bounce","complaint","soft_bounce_limit",null],"description":"Set when the address is on the tenant suppression list."},"blacklisted":{"type":"boolean","description":"On the tenant's blacklist (\"Na blacklistu\"): a manual entry, or an entry of SmartEmailing's blacklist that keeps\nits own reason (migration 0180). Older servers leave it out: then suppressionReason 'manual' decides."},"blacklistedAt":{"type":["string","null"],"description":"When the address went on the blacklist (the suppression's time), null when it is not on it (\"Přidán na blacklist\")."},"state":{"type":"string","enum":["active","bounced","blacklisted"],"description":"SmartEmailing's 3-value contact state; older servers leave it out (contactState derives it)."},"createdAt":{"type":"string"},"extra":{"type":"object","additionalProperties":{"anyOf":[{"type":"boolean"},{"type":"string"},{"type":"number"},{"type":"null"}]},"description":"Values of the extra columns asked for (ContactQuery.fields): dates as YYYY-MM-DD, times as ISO."}},"required":["phone","mobile","titleBefore","titleAfter","salutation","salutationFormal","vocativeFirstName","vocativeLastName","gender","language","company","companyId","street","city","postalCode","country","birthday","nameDay","note","affiliateId","source","consentBasis","consentWeak","statusAt","consentAt","softBounces","lastSentAt","lastOpenAt","lastClickAt","suppressedAt","suppressionNote","stats90d","lists","recentActivities","orders","orderTotals","engagementAt","customFields","updatedAt","id","email","firstName","lastName","status","engagement","excluded","shopId","suppressionReason","createdAt"]},"ContactListItem":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"status":{"type":"string","enum":["pending","subscribed","unsubscribed","bounced","complained","customer"]},"engagement":{"type":"string","enum":["A","B","C","D","E","N"]},"excluded":{"type":"boolean","description":"SE \"Vyloučené\": long inactive yet mailed often (recomputed weekly with the engagement)."},"shopId":{"type":["string","null"],"description":"Main e-shop (contacts.shop_id), null when unknown."},"suppressionReason":{"type":["string","null"],"enum":["invalid","manual","unsubscribed","hard_bounce","complaint","soft_bounce_limit",null],"description":"Set when the address is on the tenant suppression list."},"blacklisted":{"type":"boolean","description":"On the tenant's blacklist (\"Na blacklistu\"): a manual entry, or an entry of SmartEmailing's blacklist that keeps\nits own reason (migration 0180). Older servers leave it out: then suppressionReason 'manual' decides."},"blacklistedAt":{"type":["string","null"],"description":"When the address went on the blacklist (the suppression's time), null when it is not on it (\"Přidán na blacklist\")."},"state":{"type":"string","enum":["active","bounced","blacklisted"],"description":"SmartEmailing's 3-value contact state; older servers leave it out (contactState derives it)."},"createdAt":{"type":"string"},"extra":{"type":"object","additionalProperties":{"anyOf":[{"type":"boolean"},{"type":"string"},{"type":"number"},{"type":"null"}]},"description":"Values of the extra columns asked for (ContactQuery.fields): dates as YYYY-MM-DD, times as ISO."}},"required":["id","email","firstName","lastName","status","engagement","excluded","shopId","suppressionReason","createdAt"]},"ContactListMembership":{"type":"object","properties":{"listId":{"type":"string"},"name":{"type":"string"},"state":{"$ref":"#/components/schemas/ListMembershipState"},"changedAt":{"type":"string"},"listSize":{"type":"number"}},"required":["listId","name","state","changedAt","listSize"]},"ContactNeighbors":{"type":"object","properties":{"prevId":{"type":["string","null"]},"nextId":{"type":["string","null"]}},"required":["prevId","nextId"]},"ContactOrder":{"type":"object","properties":{"id":{"type":"string"},"shopId":{"type":"string"},"code":{"type":"string"},"status":{"$ref":"#/components/schemas/OrderStatus"},"sourceStatus":{"type":"string","description":"State name in the e-shop"},"currency":{"$ref":"#/components/schemas/Currency"},"totalWithVat":{"type":["string","null"],"description":"Decimal string with VAT in the order currency, as sent by the e-shop."},"orderedAt":{"type":"string"},"paidAt":{"type":["string","null"]},"items":{"type":"array","items":{"$ref":"#/components/schemas/OrderItemPreview"},"description":"First items only; itemCount is the full number of lines."},"itemCount":{"type":"number"}},"required":["id","shopId","code","status","sourceStatus","currency","totalWithVat","orderedAt","paidAt","items","itemCount"]},"ContactPatch":{"type":"object","properties":{"firstName":{"type":"string"},"lastName":{"type":"string"},"titleBefore":{"type":"string"},"titleAfter":{"type":"string"},"salutation":{"type":"string"},"salutationFormal":{"type":"string"},"vocativeFirstName":{"type":"string"},"vocativeLastName":{"type":"string"},"gender":{"type":["string","null"],"enum":["f","m",null]},"language":{"type":"string"},"company":{"type":"string"},"companyId":{"type":"string"},"phone":{"type":"string"},"mobile":{"type":"string"},"street":{"type":"string"},"city":{"type":"string"},"postalCode":{"type":"string"},"country":{"type":["string","null"]},"birthday":{"type":["string","null"]},"nameDay":{"type":["string","null"]},"note":{"type":"string"},"affiliateId":{"type":"string"},"customFields":{"type":"object","additionalProperties":{"anyOf":[{"type":"boolean"},{"type":"string"},{"type":"number"},{"type":"array","items":{"type":"string"}},{"type":"null"}]}}},"description":"Profile changes; the address, state and consent never change here. A null custom field value removes it."},"ContactSegment":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"shopId":{"type":"string"},"refresh":{"type":"string","enum":["static","daily"]},"computedAt":{"type":["string","null"]},"updatedAt":{"type":"string"},"tags":{"type":"array","items":{"type":"string"},"description":"The segment's labels (SE's column Štítky)."},"active":{"type":"number","description":"Contacts of the segment that can be e-mailed (SE's Aktivní kontakty)."}},"required":["id","name","shopId","refresh","computedAt","updatedAt","tags","active"]},"ContactSource":{"type":"string","enum":["manual","shoptet","smartemailing","api","erp_order","signup_form","import"]},"ContactSourceRecord":{"type":"object","properties":{"source":{"type":"string","enum":["manual","shoptet","csv","smartemailing","api","form","erp"]},"consent":{"type":"string","enum":["unsubscribed","hard_bounce","blacklisted","opt_in","none"]},"consentAt":{"type":["string","null"]},"firstSeenAt":{"type":"string"},"lastSeenAt":{"type":"string"}},"required":["source","consent","consentAt","firstSeenAt","lastSeenAt"],"description":"One source record of a contact (provenance): what that source says about consent."},"ContactStats":{"type":"object","properties":{"received":{"type":"number"},"opened":{"type":"number"},"clicked":{"type":"number"},"bestHour":{"type":["number","null"],"description":"Hour of day (0 to 23, tenant time zone) with the most opens, null without data."},"preferredDeliveryTime":{"type":["string","null"],"description":"SE's preferredDeliveryTime 'HH:MM' (KK-20): the same ideal time the automations' Individuální čas and the\ncampaigns' Ideální čas odeslání use; null without an open."}},"required":["received","opened","clicked","bestHour"]},"ContactSummary":{"type":"object","properties":{"total":{"type":"number"},"byStatus":{"type":"object","properties":{"pending":{"type":"number"},"subscribed":{"type":"number"},"unsubscribed":{"type":"number"},"bounced":{"type":"number"},"complained":{"type":"number"},"customer":{"type":"number"}},"required":["pending","subscribed","unsubscribed","bounced","complained","customer"]},"blacklisted":{"type":"number"}},"required":["total","byStatus","blacklisted"]},"ContactsBulkRequest":{"type":"object","properties":{"action":{"type":"string","enum":["blacklist","add_to_list","delete"]},"contactIds":{"type":"array","items":{"type":"string"},"description":"The ticked contacts; or all: every contact matching the grid query (no cap)."},"all":{"type":"boolean"},"listIds":{"type":"array","items":{"type":"string"},"description":"add_to_list: the manual lists."}},"required":["action"]},"ContactsBulkStarted":{"type":"object","properties":{"jobId":{"type":"string"},"count":{"type":"number","description":"Contacts the action will go over (blacklisted ones left out)."}},"required":["jobId","count"]},"CouponStatus":{"type":"string","enum":["free","assigned","used"]},"Currency":{"type":"string","enum":["CZK","EUR"]},"CustomActivityInput":{"type":"object","properties":{"kind":{"type":"string","enum":["other","note","call","deal"]},"title":{"type":"string"},"body":{"type":"string"},"at":{"type":"string","description":"ISO time; default now."}},"required":["kind","title"]},"CustomField":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","enum":["number","text","date","checkbox","textarea","select","multi_checkbox","radio"],"description":"Fixed once created."},"options":{"type":"array","items":{"type":"string"}},"used":{"type":"number","description":"Contacts with a value."},"createdAt":{"type":"string"}},"required":["id","name","type","options","used","createdAt"]},"CustomFieldInput":{"type":"object","properties":{"name":{"type":"string"},"type":{"type":"string","enum":["number","text","date","checkbox","textarea","select","multi_checkbox","radio"]},"options":{"type":"array","items":{"type":"string"}}},"required":["name","type"]},"CustomFieldValue":{"anyOf":[{"type":"boolean"},{"type":"string"},{"type":"number"},{"type":"array","items":{"type":"string"}}]},"DuplicateGroup":{"type":"object","properties":{"kind":{"type":"string","enum":["phone","name_company"]},"contacts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"company":{"type":"string"},"phone":{"type":"string"},"status":{"type":"string","enum":["pending","subscribed","unsubscribed","bounced","complained","customer"]},"createdAt":{"type":"string"}},"required":["id","email","firstName","lastName","company","phone","status","createdAt"]}}},"required":["kind","contacts"],"description":"Possible duplicates: contacts with the same phone or the same name and company (a report, nothing is merged)."},"DuplicateReport":{"type":"object","properties":{"groups":{"type":"array","items":{"$ref":"#/components/schemas/DuplicateGroup"}},"truncated":{"type":"boolean","description":"More groups exist than the report shows (200)."}},"required":["groups","truncated"]},"EraseResult":{"type":"object","properties":{"erased":{"type":"boolean"},"consentLogRows":{"type":"number","description":"Consent log rows whose address and evidence were blanked."}},"required":["erased","consentLogRows"]},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","enum":["conflict","count_mismatch","csrf","empty_segment","expired","forbidden","method_not_allowed","not_found","not_implemented","rate_limited","recently_sent","server","suppression_locked","unauthorized","unavailable","upstream","validation"],"description":"Stable machine readable code (see the error catalog in the guides)."},"message":{"type":"string","description":"The field or the reason (technical, not for people), e.g. \"email\", \"definition.1.0.listId\", \"name\"."},"params":{"type":"object","additionalProperties":true,"description":"Details: issues of a graph, expected and actual counts, retryAfter ..."}}}},"examples":[{"error":{"code":"validation","message":"email"}},{"error":{"code":"conflict","message":"version","params":{"current":4}}}]},"ListMembershipState":{"type":"string","enum":["unsubscribed","confirmed","unconfirmed","removed"]},"OrderItemPreview":{"type":"object","properties":{"name":{"type":"string"},"quantity":{"type":"string","description":"decimal string, orders can have fractional quantities"},"priceWithVat":{"type":["string","null"]}},"required":["name","quantity","priceWithVat"]},"OrderStatus":{"type":"string","enum":["cancelled","other","pending","completed"]},"OrderTotal":{"type":"object","properties":{"currency":{"$ref":"#/components/schemas/Currency"},"count":{"type":"number"},"totalWithVat":{"type":"string"}},"required":["currency","count","totalWithVat"],"description":"One row per currency. CZK and EUR are never added together or converted."},"Purpose":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"legalBasis":{"type":"string","enum":["consent","contract","legal_obligation","legitimate_interest","vital_interest","public_interest"]},"validityDays":{"type":["number","null"],"description":"How long a consent lasts in whole days; null = no fixed period."},"validity":{"anyOf":[{"$ref":"#/components/schemas/ValidityLength"},{"type":"null"}],"description":"The same length as entered (number and unit); null = no fixed period."},"notes":{"type":"string"},"consents":{"type":"number","description":"Active (not withdrawn) consents."},"createdAt":{"type":"string"}},"required":["id","name","legalBasis","validityDays","notes","consents","createdAt"]},"PurposeInput":{"type":"object","properties":{"name":{"type":"string"},"legalBasis":{"type":"string","enum":["consent","contract","legal_obligation","legitimate_interest","vital_interest","public_interest"]},"validityDays":{"type":["number","null"],"description":"Older callers send whole days; the purpose form sends validity."},"validity":{"anyOf":[{"$ref":"#/components/schemas/ValidityLength"},{"type":"null"}]},"notes":{"type":"string"}},"required":["name","legalBasis","notes"]},"SearchResults":{"type":"object","properties":{"contacts":{"type":"array","items":{"$ref":"#/components/schemas/ContactListItem"}},"lists":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"size":{"type":"number"}},"required":["id","name","size"]}}},"required":["contacts","lists"]},"ValidityLength":{"type":"object","properties":{"value":{"type":"number"},"unit":{"type":"string","enum":["seconds","minutes","hours","days","weeks","months","years"]}},"required":["value","unit"],"description":"Length of a purpose's validity as entered (months count 30 days and years 365 for the consent ends)."}}}}