Threads
Post lesen und organisieren.
Führt jeden der 7 Aufrufe auf dieser Seite gegen Ihren Workspace aus, mit Ihrem eigenen Schlüssel.
Auflisten
GET /threads?folder=inbox. Die Übergabe von query durchsucht denselben lokalen Index. Einfache Wörter müssen alle vorkommen, und jedes passt unscharf, ohne Rücksicht auf Groß- und Kleinschreibung, Akzente und Trennzeichen, min findet also "Benjamin". Eine zitierte Phrase wird bis auf Groß-/Kleinschreibung und Akzente so abgeglichen, wie sie geschrieben steht, "ben jamin" findet also nicht "Ben-Jamin". Füllwörter wie the oder emails werden aus einer Liste einfacher Wörter gestrichen, wenn sonst noch etwas zum Suchen übrig bleibt. Operatoren wie from:, to:, subject:, label:, is:unread, has:pdf, after:2026/01/31 und newer_than:7d grenzen ein, und OR, Klammern und ein vorangestelltes - kombinieren sie. Empfänger werden als eine Liste ohne Rollen gespeichert und enthalten nie ein Bcc, cc: liest also dasselbe Feld wie to:, und bcc: trifft nichts Eigenes. from:me ist Post, die Sie versendet haben, und to:me ist Post, die eine Ihrer eigenen Adressen, Aliasse eingeschlossen, unter ihren Empfängern führt oder als Zustelladresse trägt.
Wörter und die Operatoren from:, to:, cc:, subject: und body: lesen die neueste Nachricht jedes Threads: ihren Absender, ihre Empfänger, ihren Betreff und die ersten 4.000 Zeichen ihres Bodys. filename: und has: lesen jeden Anhang der gesamten Konversation, und label:, in: und is: lesen die gesamte Konversation. folder gilt weiterhin, sofern die Anfrage nicht selbst einen Ordner benennt, mit in: oder mit einem is:, das ein Ordner ist, etwa is:sent; in:anywhere durchsucht jeden Ordner, für sich genommen wie auch neben anderen Begriffen. Eine Entwurfsliste ist die Ausnahme und bleibt in den Entwürfen, was die Anfrage auch benennt.
Ein Wert, mit dem die Suche nichts anfangen kann, wird ignoriert statt eingrenzend, ein Tippfehler in einem Wert weitet das Ergebnis also aus, statt es zu leeren: category:, larger:, smaller:, size:, messagesize:, list:, rfc822msgid:, received:, sent:, die Kategoriewörter wie is:promotions, ein has:-Wort, das keine Anhangsart benennt, ein importance: außer high oder low, ein unlesbares Datum und eine Dauer, deren Einheit nicht h, d, w, m oder y ist. Ein Operatorname, den sie nicht kennt, etwa project:, wird als einfacher Text durchsucht. Daten lesen die neueste Aktivität des Threads, in UTC, wobei after: den genannten Tag einschließt und before: ihn ausschließt; schreiben Sie eines als YYYY/MM/DD, YYYY-MM-DD, YYYYMMDD, als bloßes Jahr oder als Epochensekunden oder -millisekunden.
nextPageToken ist opak. Geben Sie genau das zurück, was Sie erhalten haben; konstruieren oder bearbeiten Sie niemals eines. Seine Form ist nicht Teil des Vertrags.
Abrufen
GET /threads/{id} liefert jede Nachricht des Threads, nicht nur die neueste, zusammen mit seinen Labels und der Information, ob darin etwas ungelesen ist.
Nachrichten, die verschlüsselt eingegangen sind
Diese API verschlüsselt und entschlüsselt nicht. Sie kann keine Nachricht öffnen, die jemand anderes verschlüsselt hat, und sie kann keine verschlüsselte versenden. Ein Request, der eine Verschlüsselungsmarkierung trägt, wird mit einem 422 abgelehnt, denn die einzigen Oberflächen, die eine setzen dürfen, sind die, welche die Schlüssel halten, und kein API-Client hält einen Schlüssel. Was sie tut, ist einen versiegelten Umschlag beim Eingang zu ERKENNEN, allein am Content-Type der obersten Ebene, und das dann an der Nachricht zu vermerken.
OpenEmail hält inzwischen selbst Schlüssel, und es lohnt sich, genau zu sagen, welche Hälfte und wo. Eine Postfachinhaberin erzeugt im Browser eine OpenPGP-Identität und veröffentlicht den ÖFFENTLICHEN Schlüssel in einem Verzeichnis, das andere angemeldete OpenEmail-Absender auflösen können. Die private Hälfte entsteht in diesem Browser, wird nie hierher gesendet und ist nie wiederherstellbar; nichts in dieser API kann also irgendetwas entschlüsseln, und keine Supportanfrage, keine Vorladung und kein Backup von uns bringt einen Schlüssel hervor, der das könnte. Die Web-App kann eine PGP/MIME- oder Inline-PGP-Nachricht inzwischen ÖFFNEN, wenn der Schlüssel im Browser der Leserin liegt, aber dieses Entschlüsseln geschieht im Tab, und sein Klartext wird nie zurückgeschrieben: Die gespeicherte Nachricht bleibt Chiffretext, und keine Antwort dieser API führt je den geöffneten Text mit. Die App kann eine neue Nachricht inzwischen im Browser versiegeln und versenden: Der Composer verschlüsselt auf die veröffentlichten Schlüssel der Empfänger, und die Post geht als PGP/MIME hinaus. Diese API kann weiterhin nichts versiegeln; das Feld unten beschreibt also sowohl Post, die jemand anderes verschlüsselt hat, als auch Post, die in einem OpenEmail-Tab versiegelt wurde.
Das ist ein Feld wert, wegen dem, was die Alternative war. Eine versiegelte Nachricht speichert keinen lesbaren Body, decodedBody kommt also als "" zurück, dieselben Bytes wie bei einer Nachricht, die wirklich keinen Inhalt hatte. encryption ist das, womit Sie die beiden auseinanderhalten können, bevor Sie auf eine davon reagieren, und es ist eine Aussage über den Umschlag und keine Verifikation: Zu sehen, dass eine Nachricht versiegelt ist, heißt nicht, sie geöffnet zu haben.
{ "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'- Welcher Umschlag eingegangen ist. Abgelesen am `Content-Type` der obersten Ebene (dessen Parameter `protocol` bei PGP, dessen `smime-type` bei S/MIME) oder, bei `pgp-inline`, an einem Body, der mit dem PGP-Armor-Header beginnt. Ein `pkcs7-mime`-Teil ganz ohne `smime-type` wird als `smime-encrypted` gelesen, denn dazu macht ihn RFC 8551 standardmäßig.
detectedAtstring- ISO 8601, wann die Erkennung lief, also wann die Nachricht hier eingeliefert wurde. Es sagt nichts darüber, wann die Nachricht verschlüsselt wurde oder von wem.
rawRetainedboolean- Ob die ursprünglichen RFC822-Bytes aufbewahrt wurden, sodass die Nachricht vollständig zurückgegeben werden könnte. Heute bei jeder Nachricht false, da hier noch nichts rohe Post aufbewahrt. Es steht jetzt schon in der Antwort, damit der Tag, an dem sich das ändert, nicht auch der Tag ist, an dem jede gespeicherte Nachricht erneut migriert werden muss.
partsobject[]- Die Umschlagteile, die dieses Format verwendet. Vorhanden, wann immer `encryption` es ist, und leer, wenn es keine zu benennen gibt: `pgp-inline` hat überhaupt keinen eigenen Teil, denn sein Armor IST der Body und kommt in `decodedBody` an.
parts[].indexnumber- Welcher MIME-Teil der ursprünglichen Nachricht dies war, gezählt über die Teile, wie sie eingegangen sind, und nicht über `attachments`. Die beiden Listen unterscheiden sich, und genau deshalb wird dies festgehalten.
parts[].attachmentIdstring- Die id, die dieser Teil in `attachments` trägt, sofern er dort überhaupt auftaucht: die Nachrichten-id mit angehängtem Teilindex. Der Teil `ciphertext` wird aufgeführt und lädt sich herunter wie jede andere Datei; `version` und `signature` bleiben aus der Liste heraus, ihre ids stellen also lediglich die Verbindung zwischen den beiden Sichten her. Der Attachments-Endpunkt gibt sie nicht zurück.
parts[].role'version' | 'ciphertext' | 'signature'- `version` ist der PGP/MIME-Steuerteil, `ciphertext` ist die Nachricht, `signature` ist eine abgetrennte Signatur. Nur `ciphertext` lohnt das Abrufen; die anderen beiden sind Protokollmobiliar, das früher als Müllanhänge gerendert wurde und das nicht mehr tut.
| format | Was eingegangen ist | Body |
|---|---|---|
| pgp-mime | Ein PGP/MIME-Umschlag: multipart/encrypted mit protocol=application/pgp-encrypted. | Versiegelt |
| pgp-inline | Armor im Body selbst. Wird ausschließlich am Bodytext abgelesen, damit eine Antwort, die lediglich einen Armor-Block zitiert, nicht dafür gehalten wird. | Versiegelt |
| smime-encrypted | Ein S/MIME-pkcs7-mime-Teil mit smime-type=enveloped-data, oder einer ganz ohne smime-type. | Versiegelt |
| pgp-signed | Eine abgetrennte PGP-Signatur neben der Nachricht: multipart/signed mit protocol=application/pgp-signature. | Lesbar |
| smime-signed | Eine abgetrennte S/MIME-Signatur: ein pkcs7-signature-Protokoll oder smime-type=signed-data. | Lesbar |
Signiert ist nicht versiegelt, und wer auf das Vorhandensein von encryption verzweigt statt auf format, versteht das genau verkehrt herum. Eine Signatur ist eine Aussage darüber, wer die Nachricht geschrieben hat, keine Hülle darum: Der Body einer signierten Nachricht liegt im Klartext vor und liest sich wie jeder andere. Behandeln Sie pgp-mime, pgp-inline und smime-encrypted als unlesbar und die beiden signierten Formate als gewöhnliche Post.
Was sich bei einer versiegelten Nachricht ändert
Nur die drei versiegelten Formate ändern etwas, und die Änderung passiert beim Eingang und nicht in dieser Antwort. Alles, was den Body gelesen hätte, tritt zurück, statt Chiffretext zu lesen und ein Ergebnis zu melden, das es nicht haben kann:
- Die Suche über den Body. Die Nachricht wird mit leerem Body-Snippet indiziert, sie wird also weiterhin über Absender, Betreff, Adresse und Label gefunden und nicht über irgendetwas in ihrem Inneren.
- Der Body-Durchgang der Phishing-Bewertung. Das Urteil kommt weiterhin und sagt, was es nicht tun konnte:
risk.signalsführtbody-encrypted, undrisk.aiCheckedist false. - Die Prüfung auf KI-Autorschaft, die sich zurückhält statt zu raten:
aiWritten.levelistunknownundaiWritten.skippedistencrypted. - Body-Bedingungen in Regeln. Umschlag- und Header-Bedingungen laufen genau wie zuvor; eine Regel, die nach dem Body gefragt hat, wird als nicht ausgewertet vermerkt statt als Nichttreffer gezählt, denn "hat nicht gepasst" und "konnte nicht gelesen werden" sind verschiedene Antworten.
- Der Import von Kalendereinladungen. Die Einladung steckt im Chiffretext, und aus dem Umschlag ein Ereignis zu bauen, würde einen falschen Eintrag in einen echten Kalender setzen.
- Thread-Zusammenfassungen und Embeddings, für den gesamten Thread. Eine versiegelte Antwort genügt. Eine Zusammenfassung ist die Lesart eines Modells vom Klartext, gespeichert als Klartext-Metadaten, und das ist die eine Stelle in dieser Pipeline, an der ein Body in einen Speicher sickern würde, den niemand für einen Body hält.
Alles, was den Body nicht braucht, bleibt unberührt:
- DMARC, DKIM und SPF. Die werden an
Authentication-Resultsabgelesen, was Chiffretext nicht verbirgt; eine verschlüsselte Nachricht erhält also weiterhin ein echtes Authentifizierungsurteil statt gar keines. - Threading, Spam-Ablage und die Sperrliste: alles Umschlag- und Header-Arbeit.
- Anhänge. Der Chiffretext-Teil bleibt in
attachments, heißtencrypted-message.asc, wenn er ohne Namen eingeht, und lädt sich über den Endpunkt unten herunter. Es ist genau das, was der Reader der Web-App selbst abruft und im Browser entschlüsselt; für einen API-Client, der keinen Schlüssel hält, bleibt dieser Download der einzige Weg, die Post zu lesen. Öffnen Sie sie in einem Client, der einen hat. - Eine signierte Nachricht verliert nichts davon. Jede der obigen Prüfungen läuft weiter auf ihr, und es wird nichts zurückgehalten; genau deshalb ist die Liste der versiegelten Formate drei Formate lang und nicht fünf.
Das Fehlen von encryption ist keine Aussage über Klartext. Es bedeutet, dass niemand nachgesehen hat: Die Nachricht ist älter als die Erkennung, oder sie hat das Postfach auf einem Weg erreicht, auf dem der Detektor nicht läuft. Nichts füllt das nachträglich auf; ein Feld, das sagt "wir haben nicht geprüft", darf also nie als "wir haben geprüft und keine gefunden" gelesen werden.
Markieren und mit Labels versehen
PATCH /threads/{id} nimmt read, addLabelIds und removeLabelIds. Der Lesestatus ist auf jedem Backend, das dieses Produkt unterstützt, ein Label; read zu setzen und Labels in einem Aufruf zu verschieben hält die Reihenfolge also deterministisch.
{ "read": true, "addLabelIds": ["USER_INVOICES"] }TRASH und SNOOZED werden hier mit label_not_directly_settable abgelehnt. Keiner der beiden Zustände wird allein von seinem Label getragen (das Verschieben in den Papierkorb löscht außerdem die Ordnerlabels, und ein Schlummern braucht eine daneben gespeicherte Weckzeit); sie von Hand zu setzen hinterlässt einen Thread in einem Zustand, den die App nie erzeugt und aus dem sie ihn nicht befreien kann. Verwenden Sie die Endpunkte unten.
Papierkorb und Schlummern
| Endpunkt | Tut |
|---|---|
| POST /threads/{id}/trash | Verschiebt in den Papierkorb und löscht dabei INBOX, SPAM, SNOOZED und ARCHIVE gemeinsam. |
| POST /threads/{id}/snooze | Body { "wakeAt": "…" }. Blendet ihn aus und plant seine Rückkehr. |
| POST /threads/{id}/unsnooze | Holt ihn sofort zurück und storniert die geplante Rückkehr. |
Schlummern schreibt zwei Dinge: das Label, das den Thread ausblendet, und den Eintrag, der ihn zurückholt. Das eine ohne das andere zu tun ist genau der Grund, warum dies Endpunkte sind und keine Label-Bearbeitungen.
Anhänge
GET /threads/{id}/messages/{messageId}/attachments liefert jeden Anhang mit filename, contentType, size und content als base64. content ist ein leerer String, wo die gespeicherten Bytes nicht gefunden werden konnten; prüfen Sie also die Länge, bevor Sie dekodieren.
Ein verschlüsselter Umschlag ist nicht vollständig hier. Der Chiffretext schon (er ist die Nachricht, und ihn herunterzuladen ist für einen API-Client der einzige Weg, diese Post zu lesen), aber der PGP/MIME-Versionsteil und eine etwaige abgetrennte Signatur bleiben aus der Liste heraus, weil sie als Müllanhänge gerendert wurden und ein Aufrufer nichts mit ihnen anfangen kann. Beide behalten ihre ids in encryption.parts, was die beiden Sichten verbindet; dieser Endpunkt gibt sie nicht zurück.