Přejít na dokumentaci
API

Vlákna

Čtení a organizace pošty.

GETapi.openemail.uk/threads

Spustí kterékoli z 7 volání na této stránce proti vašemu pracovnímu prostoru, s vaším vlastním klíčem.

Výpis

GET /threads?folder=inbox. Předání query prohledává tentýž lokální index. Obyčejná slova musí být přítomna všechna a každé se shoduje volně, bez ohledu na velikost písmen, diakritiku a oddělovače, takže min najde „Benjamin“. Fráze v uvozovkách se shoduje tak, jak je napsaná, až na velikost písmen a diakritiku, takže "ben jamin" nenajde „Ben-Jamin“. Výplňová slova jako the nebo emails se ze seznamu obyčejných slov vypouštějí, pokud zbude něco jiného, co hledat. Zužují ji operátory jako from:, to:, subject:, label:, is:unread, has:pdf, after:2026/01/31 a newer_than:7d a kombinují je OR, závorky a úvodní -. Příjemci se ukládají jako jeden seznam bez rolí a nikdy neobsahují Bcc, takže cc: čte stejné pole jako to: a bcc: samo o sobě neodpovídá ničemu. from:me je pošta, kterou jste odeslali, a to:me je pošta, která má některou z vašich vlastních adres, včetně aliasů, mezi příjemci nebo jako adresu, na kterou byla doručena.

Slova a operátory from:, to:, cc:, subject: a body: čtou nejnovější zprávu v každém vlákně: jejího odesílatele, její příjemce, její předmět a prvních 4 000 znaků jejího těla. filename: a has: čtou všechny přílohy celé konverzace a label:, in: a is: čtou celou konverzaci. folder se stále uplatní, pokud dotaz neuvede složku pomocí in: nebo pomocí is:, které je složkou, například is:sent, a in:anywhere prohledá všechny složky, jak samostatně, tak vedle dalších výrazů. Výjimkou je výpis konceptů, který zůstává v konceptech, ať dotaz jmenuje cokoli.

Hodnota, kterou hledání neumí použít, se ignoruje, místo aby zužovala, takže překlep v hodnotě výsledek rozšíří, místo aby ho vyprázdnil: category:, larger:, smaller:, size:, messagesize:, list:, rfc822msgid:, received:, sent:, kategorie jako is:promotions, slovo za has:, které nepojmenovává žádný druh přílohy, importance: jiné než high nebo low, nečitelné datum a doba, jejíž jednotka není h, d, w, m ani y. Název operátoru, který nezná, například project:, se hledá jako obyčejný text. Data čtou nejnovější aktivitu ve vlákně, v UTC, přičemž after: zahrnuje den, který jmenuje, a before: ho vylučuje; zapište je jako YYYY/MM/DD, YYYY-MM-DD, YYYYMMDD, samotný rok, nebo jako sekundy či milisekundy epochy.

nextPageToken je neprůhledný. Vracejte přesně to, co jste dostali; nikdy si ho nesestavujte ani neupravujte. Jeho tvar není součástí smlouvy.

Načítání

GET /threads/{id} vrací všechny zprávy ve vlákně, ne jen tu nejnovější, spolu s jeho štítky a informací, zda je v něm něco nepřečteného.

Zprávy, které dorazily zašifrované

Toto API nešifruje ani nedešifruje. Nedokáže otevřít zprávu, kterou zašifroval někdo jiný, a nedokáže zašifrovanou zprávu odeslat. Požadavek nesoucí šifrovací příznak se odmítá s 422, protože jediné plochy, které ho smějí nastavit, jsou ty, které drží klíče, a žádný API klient klíč nedrží. Co umí, je ROZPOZNAT zapečetěnou obálku na vstupu, a to jen podle Content-Type nejvyšší úrovně a ničeho dalšího, a pak to u zprávy uvést.

OpenEmail teď sám drží klíče a stojí za to být přesný v tom, kterou polovinu a kde. Majitel schránky si ve svém prohlížeči vygeneruje OpenPGP identitu a VEŘEJNÝ klíč publikuje do adresáře, který si ostatní přihlášení odesílatelé v OpenEmailu umí dohledat. Soukromá polovina vzniká v onom prohlížeči, nikdy se sem neposílá a nikdy ji nelze obnovit, takže nic v tomto API nedokáže nic dešifrovat a žádný požadavek na podporu, soudní příkaz ani naše záloha nevydá klíč, který by to dokázal. Webová aplikace teď dokáže OTEVŘÍT zprávu ve formátu PGP/MIME nebo inline PGP, pokud je klíč v prohlížeči čtenáře, ale toto dešifrování probíhá v záložce a jeho otevřený text se nikdy nezapisuje zpět: uložená zpráva zůstává šifrovaná a žádná odpověď z tohoto API otevřený text nikdy nenese. Aplikace teď také umí novou zprávu v prohlížeči zapečetit a odeslat: editor ji zašifruje publikovanými klíči příjemců a pošta odchází jako PGP/MIME. Toto API stále nedokáže nic zapečetit, takže pole níže popisuje jak poštu, kterou zašifroval někdo jiný, tak poštu zapečetěnou v záložce OpenEmailu.

Vlastní pole si to zaslouží kvůli tomu, jaká byla alternativa. Zapečetěná zpráva neukládá žádné čitelné tělo, takže decodedBody se vrací jako "", tedy stejné bajty jako u zprávy, která skutečně žádný obsah neměla. encryption je to, co vám umožní obojí odlišit dřív, než podle toho začnete jednat, a je to tvrzení o obálce, ne ověření: vidět, že je zpráva zapečetěná, není totéž jako otevřít ji.

Odpověď
{    "object": "thread",    "id": "thread_2f9b…",    "messages": [      {        "id": "msg_7c41…",        "subject": "Q3 numbers",        "decodedBody": "",        "encryption": {          "format": "pgp-mime",          "detectedAt": "2026-08-30T09:14:22.117Z",          "rawRetained": false,          "parts": [            { "index": 0, "attachmentId": "msg_7c41…-0", "role": "version" },            { "index": 1, "attachmentId": "msg_7c41…-1", "role": "ciphertext" }          ]        }      }    ]  }

encryption

format'pgp-mime' | 'pgp-signed' | 'pgp-inline' | 'smime-encrypted' | 'smime-signed'
Která obálka dorazila. Čte se z `Content-Type` nejvyšší úrovně (z jeho parametru `protocol` u PGP, z jeho `smime-type` u S/MIME) nebo, u `pgp-inline`, z těla, které začíná hlavičkou PGP armor. Část `pkcs7-mime`, která nenese žádné `smime-type`, se čte jako `smime-encrypted`, což z ní ve výchozím stavu dělá RFC 8551.
detectedAtstring
ISO 8601, kdy proběhla detekce, tedy kdy sem byla zpráva přijata. Neříká nic o tom, kdy byla zpráva zašifrována ani kým.
rawRetainedboolean
Zda byly zachovány původní bajty RFC822, aby bylo možné zprávu vrátit celou. Dnes je u každé zprávy false, protože tady zatím nic neuchovává surovou poštu. V odpovědi je už teď, aby den, kdy se to změní, nebyl zároveň dnem, kdy se musí znovu migrovat každá uložená zpráva.
partsobject[]
Části obálky, které tento formát používá. Přítomné vždy, když je přítomné `encryption`, a prázdné, když není co jmenovat: `pgp-inline` nemá žádnou samostatnou část, protože jeho armor JE tělo a přichází v `decodedBody`.
parts[].indexnumber
Kterou MIME částí původní zprávy to bylo, počítáno přes části tak, jak dorazily, ne přes `attachments`. Tyto dva seznamy se liší, což je jediný důvod, proč se to zaznamenává.
parts[].attachmentIdstring
Id, které tato část nese v `attachments`, pokud se tam vůbec objeví: id zprávy s připojeným indexem části. Část `ciphertext` je v seznamu a stahuje se jako každý jiný soubor; `version` a `signature` se ze seznamu vynechávají, takže jejich id jen propojují oba pohledy a nic víc. Endpoint pro přílohy je nevrátí.
parts[].role'version' | 'ciphertext' | 'signature'
`version` je řídicí část PGP/MIME, `ciphertext` je zpráva, `signature` je oddělený podpis. Stahovat se vyplatí jen `ciphertext`; ostatní dvě jsou protokolární výplň, která se dřív vykreslovala jako nesmyslné přílohy a už se tak nevykresluje.
formatCo doraziloTělo
pgp-mimeObálka PGP/MIME: multipart/encrypted s protocol=application/pgp-encrypted.Zapečetěné
pgp-inlineArmor přímo v těle. Čte se výhradně z textu těla, takže se za něj nezamění odpověď, která armorovaný blok jen cituje.Zapečetěné
smime-encryptedČást S/MIME pkcs7-mime s smime-type=enveloped-data, nebo taková, která smime-type nemá vůbec.Zapečetěné
pgp-signedOddělený PGP podpis vedle zprávy: multipart/signed s protocol=application/pgp-signature.Čitelné
smime-signedOddělený podpis S/MIME: protokol pkcs7-signature, nebo smime-type=signed-data.Čitelné

Podepsané neznamená zapečetěné a větvit se podle přítomnosti encryption místo podle format znamená pochopit to přesně naopak. Podpis je tvrzení o tom, kdo zprávu napsal, ne obal kolem ní: tělo podepsané zprávy je otevřené a čte se jako kterékoli jiné. pgp-mime, pgp-inline a smime-encrypted považujte za nečitelné a oba podepsané formáty za běžnou poštu.

Co se u zapečetěné zprávy mění

Něco mění jen ty tři zapečetěné formáty a ta změna nastává při příjmu, ne v této odpovědi. Vše, co by četlo tělo, se stáhne, místo aby četlo šifrovaný text a hlásilo výsledek, ke kterému nemohlo dojít:

  • Hledání v těle. Zpráva se indexuje s prázdným úryvkem těla, takže se stále najde podle odesílatele, předmětu, adresy a štítku, ale ne podle čehokoli uvnitř ní.
  • Průchod těla hodnotitelem phishingu. Verdikt stále přijde a řekne, co udělat nešlo: risk.signals nese body-encrypted a risk.aiChecked je false.
  • Kontrola autorství AI, která raději odmítne, než aby hádala: aiWritten.level je unknown a aiWritten.skipped je encrypted.
  • Podmínky nad tělem v pravidlech. Podmínky nad obálkou a hlavičkami běží přesně jako dřív; pravidlo, které se ptalo na tělo, se zaznamená jako nevyhodnocené, místo aby se počítalo jako neshoda, protože „neshodovalo se“ a „nešlo přečíst“ jsou různé odpovědi.
  • Import pozvánky do kalendáře. Pozvánka je uvnitř šifrovaného textu a sestavit událost z obálky by znamenalo vložit chybný záznam do skutečného kalendáře.
  • Shrnutí vláken a embeddingy, a to pro celé vlákno. Stačí jedna zapečetěná odpověď. Shrnutí je modelovo čtení otevřeného textu uložené jako nešifrovaná metadata, což je jediné místo v této pipeline, kde by tělo uniklo do úložiště, o němž nikdo jako o těle nepřemýšlí.

Vše, co tělo nepotřebuje, zůstává nedotčeno:

  • DMARC, DKIM a SPF. Ty se čtou z Authentication-Results, které šifrovaný text neskrývá, takže i zašifrovaná zpráva dostane skutečný ověřovací verdikt, a ne žádný.
  • Řazení do vláken, filtrování spamu a blokovací seznam: vše je práce s obálkou a hlavičkami.
  • Přílohy. Část se šifrovaným textem zůstává v attachments, pojmenovaná encrypted-message.asc, pokud dorazí bez názvu, a stahuje se endpointem níže. Je to přesně to, co si stáhne a v prohlížeči dešifruje čtečka samotné webové aplikace; pro API klienta, který žádný klíč nedrží, zůstává toto stažení jediným způsobem, jak si poštu přečíst. Otevřete ji v klientovi, který klíč má.
  • Podepsaná zpráva o nic z toho nepřijde. Každá z výše uvedených kontrol na ní dál běží a nic se nezadržuje, a proto je seznam zapečetěných formátů seznamem tří, a ne pěti.

Nepřítomnost encryption není tvrzením o otevřeném textu. Znamená, že se nikdo nedíval: zpráva je starší než detekce, nebo se do schránky dostala cestou, která detektor nespouští. Nic se zpětně nedoplňuje, takže pole, které říká „nekontrolovali jsme“, se nikdy nesmí číst jako „kontrolovali jsme a nic jsme nenašli“.

Označování a štítkování

PATCH /threads/{id} přijímá read, addLabelIds a removeLabelIds. Stav přečtení je na každém backendu, který tento produkt podporuje, štítek, takže nastavení read a přesun štítků v jednom volání udržuje pořadí deterministické.

PATCH
{ "read": true, "addLabelIds": ["USER_INVOICES"] }

TRASH a SNOOZED se tu odmítají s label_not_directly_settable. Ani jeden stav nenese samotný štítek (přesun do koše zároveň maže štítky složek a odložení potřebuje vedle sebe uložený čas probuzení), takže jejich ruční nastavení nechá vlákno ve stavu, který aplikace nikdy nevytvoří a ze kterého se nedokáže dostat. Použijte endpointy níže.

Koš a odložení

EndpointCo dělá
POST /threads/{id}/trashPřesune do koše a zároveň odstraní INBOX, SPAM, SNOOZED i ARCHIVE.
POST /threads/{id}/snoozeTělo { "wakeAt": "…" }. Skryje vlákno a naplánuje jeho návrat.
POST /threads/{id}/unsnoozeVrátí ho hned zpět a zruší naplánovaný návrat.

Odložení zapisuje dvě věci: štítek, který vlákno skryje, a záznam, který ho vrátí. Udělat jedno bez druhého je přesně ten důvod, proč jsou tohle endpointy, a ne úpravy štítků.

Přílohy

GET /threads/{id}/messages/{messageId}/attachments vrací každou přílohu s filename, contentType, size a content v base64. content je prázdný řetězec tam, kde se uložené bajty nepodařilo najít, takže před dekódováním zkontrolujte jeho délku.

Zašifrovaná obálka tu není celá. Šifrovaný text ano (je to ta zpráva a jeho stažení je jediný způsob, jak si API klient tuto poštu přečte), ale řídicí část PGP/MIME s verzí a případný oddělený podpis se ze seznamu vynechávají, protože se vykreslovaly jako nesmyslné přílohy a volající s nimi nic nesvede. Obě si své id ponechávají v encryption.parts, což propojuje oba pohledy; tento endpoint je nevrací.