Wątki
Czytaj i porządkuj pocztę.
Uruchamia dowolne z 7 wywołań na tej stronie na twojej przestrzeni roboczej, twoim własnym kluczem.
Listowanie
GET /threads?folder=inbox. Przekazanie query przeszukuje ten sam lokalny indeks. Wszystkie zwykłe słowa muszą wystąpić, a każde dopasowuje się swobodnie, ignorując wielkość liter, znaki diakrytyczne i separatory, więc min znajduje „Benjamin”. Fraza w cudzysłowie jest dopasowywana dosłownie, poza wielkością liter i znakami diakrytycznymi, więc "ben jamin" nie znajduje „Ben-Jamin”. Słowa wypełniające, takie jak the czy emails, są pomijane na liście zwykłych słów, o ile zostaje coś innego do wyszukania. Operatory takie jak from:, to:, subject:, label:, is:unread, has:pdf, after:2026/01/31 i newer_than:7d zawężają wyniki, a OR, nawiasy i wiodący - je łączą. Odbiorcy są przechowywani jako jedna lista bez ról i nigdy nie zawierają Bcc, więc cc: czyta to samo pole co to:, a bcc: nie dopasowuje niczego własnego. from:me to poczta wysłana przez Ciebie, a to:me to poczta, która wśród odbiorców lub jako adres doręczenia zawiera jeden z Twoich własnych adresów, wraz z aliasami.
Zwykłe słowa oraz operatory from:, to:, cc:, subject: i body: czytają najnowszą wiadomość w każdym wątku: jej nadawcę, odbiorców, temat i pierwsze 4000 znaków treści. filename: i has: czytają każdy załącznik w całej konwersacji, a label:, in: i is: czytają całą konwersację. folder nadal obowiązuje, chyba że zapytanie wskaże folder przez in: albo przez is: będące folderem, na przykład is:sent; in:anywhere przeszukuje każdy folder, zarówno samodzielnie, jak i obok innych warunków. Wyjątkiem jest lista wersji roboczych, która pozostaje w wersjach roboczych niezależnie od tego, co wskazuje zapytanie.
Wartość, której wyszukiwarka nie potrafi użyć, jest ignorowana zamiast zawężać wyniki, więc literówka w wartości poszerza wynik, zamiast go opróżniać: category:, larger:, smaller:, size:, messagesize:, list:, rfc822msgid:, received:, sent:, słowa kategorii takie jak is:promotions, słowo po has: nienazywające żadnego rodzaju załącznika, importance: inne niż high lub low, nieczytelna data oraz czas trwania, którego jednostką nie jest h, d, w, m ani y. Nieznana nazwa operatora, na przykład project:, jest wyszukiwana jako zwykły tekst. Daty czytają najnowszą aktywność w wątku, w UTC, przy czym after: obejmuje wskazany dzień, a before: go wyklucza; zapisz ją jako YYYY/MM/DD, YYYY-MM-DD, YYYYMMDD, sam rok albo sekundy lub milisekundy epoki.
nextPageToken jest nieprzejrzysty. Odsyłaj dokładnie to, co otrzymałeś; nigdy go nie twórz ani nie modyfikuj. Jego postać nie jest częścią kontraktu.
Pobieranie
GET /threads/{id} zwraca każdą wiadomość w wątku, nie tylko najnowszą, wraz z jego etykietami i informacją, czy cokolwiek w nim jest nieprzeczytane.
Wiadomości, które przyszły zaszyfrowane
To API nie szyfruje ani nie odszyfrowuje. Nie potrafi otworzyć wiadomości zaszyfrowanej przez kogoś innego ani wysłać zaszyfrowanej. Żądanie niosące znacznik szyfrowania jest odrzucane z 422, ponieważ jedyne powierzchnie, które mogą go ustawić, to te trzymające klucze, a żaden klient API klucza nie ma. Potrafi natomiast ROZPOZNAĆ zapieczętowaną kopertę na wejściu, wyłącznie po nagłówku Content-Type najwyższego poziomu i po niczym więcej, a następnie zaznaczyć to na wiadomości.
OpenEmail sam trzyma teraz klucze i warto dokładnie powiedzieć, którą połowę i gdzie. Właściciel skrzynki generuje tożsamość OpenPGP w swojej przeglądarce i publikuje klucz PUBLICZNY do katalogu, z którego mogą go pobrać inni zalogowani nadawcy OpenEmail. Prywatna połowa powstaje w tej przeglądarce, nigdy nie trafia tutaj i nigdy nie da się jej odzyskać, więc nic w tym API nie może niczego odszyfrować i żadne zgłoszenie do wsparcia, wezwanie sądowe ani nasza kopia zapasowa nie wytworzy klucza, który by to umożliwił. Aplikacja webowa potrafi już OTWORZYĆ wiadomość PGP/MIME lub inline-PGP, gdy klucz jest w przeglądarce czytającego, ale to odszyfrowanie dzieje się w karcie, a jego tekst jawny nigdy nie jest zapisywany z powrotem: przechowywana wiadomość pozostaje szyfrogramem, a żadna odpowiedź tego API nigdy nie niesie otwartego tekstu. Aplikacja potrafi już zapieczętować nową wiadomość w przeglądarce i ją wysłać: kompozytor szyfruje do opublikowanych kluczy odbiorców, a poczta wychodzi jako PGP/MIME. To API nadal nie potrafi niczego zapieczętować, więc poniższe pole opisuje zarówno pocztę zaszyfrowaną przez kogoś innego, jak i pocztę zapieczętowaną w karcie OpenEmail.
To zasługuje na osobne pole ze względu na to, jaka była alternatywa. Zapieczętowana wiadomość nie przechowuje czytelnej treści, więc decodedBody wraca jako "", te same bajty co wiadomość, która naprawdę nie miała treści. encryption pozwala odróżnić jedno od drugiego, zanim zaczniesz działać, i jest stwierdzeniem o kopercie, a nie weryfikacją: zobaczenie, że wiadomość jest zapieczętowana, to nie to samo co jej otwarcie.
{ "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'- Jaka koperta przyszła. Odczytywane z nagłówka `Content-Type` najwyższego poziomu (parametr `protocol` dla PGP, `smime-type` dla S/MIME) albo, dla `pgp-inline`, z treści zaczynającej się nagłówkiem armor PGP. Część `pkcs7-mime` bez żadnego `smime-type` jest czytana jako `smime-encrypted`, bo takim czyni ją domyślnie RFC 8551.
detectedAtstring- ISO 8601, kiedy zadziałał detektor, czyli kiedy wiadomość została tu przyjęta. Nie mówi nic o tym, kiedy wiadomość zaszyfrowano ani przez kogo.
rawRetainedboolean- Czy zachowano oryginalne bajty RFC822, tak by można było zwrócić wiadomość w całości. Dziś false na każdej wiadomości, ponieważ nic tutaj nie przechowuje jeszcze surowej poczty. Pole jest w odpowiedzi już teraz, żeby dzień, w którym to się zmieni, nie był zarazem dniem ponownej migracji każdej zapisanej wiadomości.
partsobject[]- Części koperty, których używa ten format. Obecne zawsze wtedy, gdy obecne jest `encryption`, i puste, gdy nie ma czego nazwać: `pgp-inline` nie ma żadnej osobnej części, bo jego armor JEST treścią i przychodzi w `decodedBody`.
parts[].indexnumber- Która część MIME oryginalnej wiadomości to była, liczona po częściach w postaci, w jakiej przyszły, a nie po `attachments`. Obie listy się różnią i właśnie dlatego jest to zapisywane.
parts[].attachmentIdstring- Identyfikator, jaki ta część nosi w `attachments`, o ile w ogóle się tam pojawia: identyfikator wiadomości z doklejonym indeksem części. Część `ciphertext` jest wymieniona i pobiera się ją jak każdy inny plik; `version` i `signature` są trzymane poza listą, więc ich identyfikatory jedynie korelują oba widoki i nic więcej. Endpoint załączników ich nie zwróci.
parts[].role'version' | 'ciphertext' | 'signature'- `version` to część sterująca PGP/MIME, `ciphertext` to wiadomość, a `signature` to oddzielony podpis. Warto pobierać tylko `ciphertext`; pozostałe dwie to protokolarne umeblowanie, które kiedyś renderowało się jako śmieciowe załączniki, a teraz już nie.
| format | Co przyszło | Treść |
|---|---|---|
| pgp-mime | Koperta PGP/MIME: multipart/encrypted z protocol=application/pgp-encrypted. | Zapieczętowana |
| pgp-inline | Armor w samej treści. Odczytywany wyłącznie z tekstu treści, więc odpowiedź, która jedynie cytuje blok armor, nie zostanie za taką wzięta. | Zapieczętowana |
| smime-encrypted | Część S/MIME pkcs7-mime z smime-type=enveloped-data albo taka bez żadnego smime-type. | Zapieczętowana |
| pgp-signed | Oddzielony podpis PGP obok wiadomości: multipart/signed z protocol=application/pgp-signature. | Czytelna |
| smime-signed | Oddzielony podpis S/MIME: protokół pkcs7-signature albo smime-type=signed-data. | Czytelna |
Podpisana to nie zapieczętowana, a rozgałęzianie się na obecności encryption zamiast na format odwraca to dokładnie na opak. Podpis to twierdzenie o tym, kto napisał wiadomość, a nie opakowanie wokół niej: treść podpisanej wiadomości jest jawna i czyta się jak każda inna. Traktuj pgp-mime, pgp-inline i smime-encrypted jako nieczytelne, a oba formaty podpisane jako zwykłą pocztę.
Co zmienia się w zapieczętowanej wiadomości
Tylko trzy zapieczętowane formaty cokolwiek zmieniają, a zmiana zachodzi przy przyjęciu, nie w tej odpowiedzi. Wszystko, co czytałoby treść, wycofuje się, zamiast czytać szyfrogram i raportować wynik, którego nie mogłoby uzyskać:
- Wyszukiwanie po treści. Wiadomość jest indeksowana z pustym fragmentem treści, więc wciąż da się ją znaleźć po nadawcy, temacie, adresie i etykiecie, a nie po czymkolwiek w środku.
- Przebieg oceny phishingu po treści. Werdykt nadal przychodzi i mówi, czego nie mógł zrobić:
risk.signalsniesiebody-encrypted, arisk.aiCheckedjest false. - Sprawdzenie autorstwa AI, które odmawia zamiast zgadywać:
aiWritten.leveltounknown, aaiWritten.skippedtoencrypted. - Warunki dotyczące treści w regułach. Warunki koperty i nagłówków działają dokładnie jak wcześniej; reguła pytająca o treść jest zapisywana jako nieoceniona, a nie liczona jako niedopasowanie, ponieważ „nie pasowało” i „nie dało się odczytać” to różne odpowiedzi.
- Import zaproszeń kalendarzowych. Zaproszenie jest wewnątrz szyfrogramu, a zbudowanie wydarzenia z koperty wstawiłoby błędny wpis do prawdziwego kalendarza.
- Podsumowania wątków i osadzenia (embeddings), dla całego wątku. Wystarczy jedna zapieczętowana odpowiedź. Podsumowanie to odczytanie tekstu jawnego przez model, zapisane jako metadane w postaci jawnej, czyli jedyne miejsce w tym potoku, w którym treść przeciekłaby do magazynu, o którym nikt nie myśli jak o treści.
Wszystko, co nie potrzebuje treści, pozostaje nietknięte:
- DMARC, DKIM i SPF. Są odczytywane z
Authentication-Results, którego szyfrogram nie ukrywa, więc zaszyfrowana wiadomość i tak dostaje prawdziwy werdykt uwierzytelnienia, a nie żaden. - Wątkowanie, kwalifikowanie spamu i lista blokad: wszystko to praca na kopercie i nagłówkach.
- Załączniki. Część z szyfrogramem zostaje w
attachments, nazwanaencrypted-message.asc, gdy przychodzi bez nazwy, i pobiera się ją przez poniższy endpoint. To dokładnie to, co pobiera i odszyfrowuje w przeglądarce czytnik aplikacji webowej; dla klienta API, który nie ma klucza, to pobranie pozostaje jedynym sposobem odczytania tej poczty. Otwórz ją w kliencie, który klucz ma. - Podpisana wiadomość nic z tego nie traci. Każde z powyższych sprawdzeń nadal na niej działa i nic nie jest wstrzymywane — dlatego lista zapieczętowanych formatów liczy trzy pozycje, a nie pięć.
Brak encryption nie jest twierdzeniem o jawności. Znaczy, że nikt nie sprawdzał: wiadomość jest starsza niż detekcja albo trafiła do skrzynki drogą, która detektora nie uruchamia. Nic tego nie uzupełnia wstecz, więc pola mówiącego „nie sprawdziliśmy” nigdy nie wolno czytać jako „sprawdziliśmy i nic nie znaleźliśmy”.
Oznaczanie i etykietowanie
PATCH /threads/{id} przyjmuje read, addLabelIds i removeLabelIds. Stan przeczytania jest etykietą na każdym backendzie wspieranym przez ten produkt, więc ustawienie read i przeniesienie etykiet w jednym wywołaniu utrzymuje deterministyczną kolejność.
{ "read": true, "addLabelIds": ["USER_INVOICES"] }TRASH i SNOOZED są tu odrzucane z label_not_directly_settable. Żaden z tych stanów nie jest niesiony przez samą etykietę (przeniesienie do kosza czyści też etykiety folderów, a drzemka wymaga zapisanego obok czasu wybudzenia), więc ustawienie ich ręcznie zostawia wątek w stanie, którego aplikacja nigdy nie produkuje i z którego nie umie wyjść. Użyj poniższych endpointów.
Kosz i drzemka
| Endpoint | Działanie |
|---|---|
| POST /threads/{id}/trash | Przenosi do Kosza, czyszcząc jednocześnie INBOX, SPAM, SNOOZED i ARCHIVE. |
| POST /threads/{id}/snooze | Treść { "wakeAt": "…" }. Ukrywa wątek i planuje jego powrót. |
| POST /threads/{id}/unsnooze | Przywraca go teraz i anuluje zaplanowany powrót. |
Drzemka zapisuje dwie rzeczy: etykietę, która ukrywa wątek, i wpis, który go przywraca. Zrobienie jednego bez drugiego to dokładnie powód, dla którego są to endpointy, a nie edycje etykiet.
Załączniki
GET /threads/{id}/messages/{messageId}/attachments zwraca każdy załącznik z filename, contentType, size i content w base64. content jest pustym ciągiem tam, gdzie nie udało się odnaleźć zapisanych bajtów, więc sprawdź jego długość przed dekodowaniem.
Zaszyfrowana koperta nie jest tu w całości. Szyfrogram tak (to jest wiadomość, a jego pobranie to jedyny sposób, w jaki klient API przeczyta tę pocztę), ale część wersji PGP/MIME i ewentualny oddzielony podpis są trzymane poza listą, bo renderowały się jako śmieciowe załączniki i nie ma z nimi co zrobić. Oba zachowują swoje identyfikatory w encryption.parts, co koreluje oba widoki; ten endpoint ich nie zwraca.