Ga direct naar de documentatie
API

Threads

Post lezen en ordenen.

GETapi.openemail.uk/threads

Voert elk van de 7 aanroepen op deze pagina uit op je workspace, met je eigen sleutel.

Tonen

GET /threads?folder=inbox. query meegeven doorzoekt dezelfde lokale index. Losse woorden moeten allemaal voorkomen, en elk matcht soepel, ongeacht hoofdletters, accenten en scheidingstekens, dus min vindt "Benjamin". Een aangehaalde zinsnede wordt gematcht zoals geschreven, afgezien van hoofdletters en accenten, dus "ben jamin" vindt "Ben-Jamin" niet. Stopwoorden zoals the of emails worden uit een lijst losse woorden geschrapt zolang er iets anders overblijft om op te zoeken. Operatoren zoals from:, to:, subject:, label:, is:unread, has:pdf, after:2026/01/31 en newer_than:7d versmallen het, en OR, haakjes en een leidend - combineren ze. Ontvangers worden als één lijst zonder rollen opgeslagen en bevatten nooit een Bcc, dus cc: leest hetzelfde veld als to: en bcc: matcht niets van zichzelf. from:me is post die jij verstuurde, en to:me is post die een van je eigen adressen draagt, aliassen inbegrepen, tussen de ontvangers of als het adres waaraan hij is afgeleverd.

Woorden en de operatoren from:, to:, cc:, subject: en body: lezen het nieuwste bericht op elke thread: de afzender, de ontvangers, het onderwerp en de eerste 4.000 tekens van de body. filename: en has: lezen elke bijlage op het hele gesprek, en label:, in: en is: lezen het hele gesprek. folder blijft gelden tenzij de query er zelf een noemt met in:, of met een is: die een map is zoals is:sent, en in:anywhere doorzoekt elke map, zowel op zichzelf als naast andere termen. Een conceptenlijst is de uitzondering en blijft in concepten, wat de query ook noemt.

Een waarde die de zoekopdracht niet kan gebruiken wordt genegeerd in plaats van versmald, dus een typefout in een waarde verbreedt het resultaat in plaats van het leeg te maken: category:, larger:, smaller:, size:, messagesize:, list:, rfc822msgid:, received:, sent:, de categoriewoorden zoals is:promotions, een has:-woord dat geen soort bijlage noemt, een importance: anders dan high of low, een onleesbare datum en een duur waarvan de eenheid niet h, d, w, m of y is. Een operatornaam die hij niet kent, bijvoorbeeld project:, wordt als platte tekst doorzocht. Datums lezen de nieuwste activiteit op de thread, in UTC, waarbij after: de genoemde dag insluit en before: hem uitsluit; schrijf er een als YYYY/MM/DD, YYYY-MM-DD, YYYYMMDD, een kaal jaartal, of epoch-seconden of -milliseconden.

nextPageToken is ondoorzichtig. Geef precies terug wat je kreeg; bouw of bewerk er nooit een. De vorm ervan hoort niet bij het contract.

Ophalen

GET /threads/{id} geeft elk bericht in de thread terug, niet alleen het meest recente, samen met de labels en of er iets in ongelezen is.

Berichten die versleuteld binnenkwamen

Deze API versleutelt noch ontsleutelt. Hij kan een bericht dat iemand anders versleutelde niet openen, en hij kan er geen versleuteld bericht mee versturen. Een verzoek met een versleutelingsmarkering wordt geweigerd met een 422, want de enige oppervlakken die er een mogen zetten zijn die met de sleutels, en geen enkele API-client heeft een sleutel. Wat hij wel doet is een verzegelde envelop bij binnenkomst HERKENNEN, aan het Content-Type op het hoogste niveau en aan niets anders, en dat vervolgens op het bericht vermelden.

OpenEmail houdt nu zelf sleutels, en het is de moeite waard precies te zijn over welke helft en waar. Een mailboxeigenaar genereert een OpenPGP-identiteit in zijn browser en publiceert de PUBLIEKE sleutel naar een directory die andere ingelogde OpenEmail-afzenders kunnen opzoeken. De private helft wordt in die browser gemaakt, komt hier nooit terecht en is nooit te herstellen, dus niets in deze API kan iets ontsleutelen, en geen enkel supportverzoek, dagvaarding of back-up van ons levert een sleutel op die dat wel zou kunnen. De webapp kan nu een PGP/MIME- of inline-PGP-bericht OPENEN wanneer de sleutel in de browser van de lezer zit, maar die ontsleuteling gebeurt in het tabblad en de platte tekst wordt nooit teruggeschreven: het opgeslagen bericht blijft ciphertext, en geen antwoord van deze API draagt ooit de geopende tekst. De app kan nu ook een nieuw bericht in de browser verzegelen en versturen: de composer versleutelt naar de gepubliceerde sleutels van de ontvangers en de post gaat als PGP/MIME de deur uit. Deze API kan nog steeds niets verzegelen, dus het veld hieronder beschrijft zowel post die iemand anders versleutelde als post die in een OpenEmail-tabblad is verzegeld.

Dat is een veld waard vanwege wat het alternatief was. Een verzegeld bericht slaat geen leesbare body op, dus decodedBody komt terug als "", dezelfde bytes als een bericht dat werkelijk geen inhoud had. encryption is wat je die twee uit elkaar laat houden voordat je op een ervan handelt, en het is een uitspraak over de envelop en geen verificatie: zien dat een bericht verzegeld is, is niet hetzelfde als het geopend hebben.

Antwoord
{    "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'
Welke envelop er binnenkwam. Afgelezen aan het `Content-Type` op het hoogste niveau (de `protocol`-parameter voor PGP, de `smime-type` voor S/MIME) of, voor `pgp-inline`, aan een body die met de PGP-armorheader begint. Een `pkcs7-mime`-deel zonder enige `smime-type` wordt gelezen als `smime-encrypted`, wat RFC 8551 er standaard van maakt.
detectedAtstring
ISO 8601, wanneer de detector draaide, wat het moment is waarop het bericht hier werd ingenomen. Het zegt niets over wanneer het bericht werd versleuteld, of door wie.
rawRetainedboolean
Of de oorspronkelijke RFC822-bytes zijn bewaard, zodat het bericht in zijn geheel kon worden teruggegeven. Vandaag op elk bericht false, aangezien hier nog niets ruwe post bewaart. Het zit nu al in het antwoord zodat de dag waarop dat verandert niet ook de dag is waarop elk opgeslagen bericht opnieuw gemigreerd moet worden.
partsobject[]
De envelopdelen die dit formaat gebruikt. Aanwezig zodra `encryption` dat is, en leeg wanneer er niets te noemen valt: `pgp-inline` heeft helemaal geen apart deel, want de armor IS de body en komt in `decodedBody` binnen.
parts[].indexnumber
Welk MIME-deel van het oorspronkelijke bericht dit was, geteld over de delen zoals ze binnenkwamen in plaats van over `attachments`. De twee lijsten verschillen, en dat is precies de reden dat dit wordt vastgelegd.
parts[].attachmentIdstring
De id die dit deel in `attachments` draagt, voor zover het daar überhaupt verschijnt: de bericht-id met de deelindex erachter. Het `ciphertext`-deel staat in de lijst en downloadt als elk ander bestand; `version` en `signature` worden uit de lijst gehouden, zodat hun ids de twee weergaven met elkaar verbinden en verder niets. Het attachments-endpoint geeft ze niet terug.
parts[].role'version' | 'ciphertext' | 'signature'
`version` is het PGP/MIME-controledeel, `ciphertext` is het bericht, `signature` is een losgekoppelde handtekening. Alleen `ciphertext` is de moeite van het ophalen waard; de andere twee zijn protocolmeubilair dat vroeger als rommelbijlagen werd gerenderd en dat nu niet meer doet.
formatWat er binnenkwamBody
pgp-mimeEen PGP/MIME-envelop: multipart/encrypted met protocol=application/pgp-encrypted.Verzegeld
pgp-inlineArmor in de body zelf. Wordt altijd alleen aan de bodytekst afgelezen, zodat een antwoord dat slechts een armorblok citeert niet voor een verzegeld bericht wordt aangezien.Verzegeld
smime-encryptedEen S/MIME pkcs7-mime-deel met smime-type=enveloped-data, of een deel zonder enige smime-type.Verzegeld
pgp-signedEen losgekoppelde PGP-handtekening naast het bericht: multipart/signed met protocol=application/pgp-signature.Leesbaar
smime-signedEen losgekoppelde S/MIME-handtekening: een pkcs7-signature-protocol, of smime-type=signed-data.Leesbaar

Ondertekend is niet verzegeld, en vertakken op de aanwezigheid van encryption in plaats van op format heeft dat precies verkeerd om. Een handtekening is een bewering over wie het bericht schreef, geen omhulsel eromheen: de body van een ondertekend bericht is leesbaar en leest als elk ander. Behandel pgp-mime, pgp-inline en smime-encrypted als onleesbaar, en de twee ondertekende formaten als gewone post.

Wat er verandert bij een verzegeld bericht

Alleen de drie verzegelde formaten veranderen iets, en die verandering gebeurt bij het innemen en niet in dit antwoord. Alles wat de body zou hebben gelezen treedt terug, in plaats van ciphertext te lezen en een uitkomst te melden die het onmogelijk kan hebben:

  • Zoeken over de body. Het bericht wordt geïndexeerd met een lege bodysnippet, dus het is nog steeds te vinden op afzender, onderwerp, adres en label, en niet op iets wat erin staat.
  • De bodycontrole van de phishingscorer. Het oordeel komt nog steeds binnen en vermeldt wat het niet kon doen: risk.signals draagt body-encrypted en risk.aiChecked is false.
  • De controle op AI-auteurschap, die afziet in plaats van te gokken: aiWritten.level is unknown en aiWritten.skipped is encrypted.
  • Bodyvoorwaarden in regels. Envelop- en headervoorwaarden draaien precies als voorheen; een regel die naar de body vroeg wordt vastgelegd als niet-geëvalueerd in plaats van als niet-match geteld, want "matchte niet" en "kon niet worden gelezen" zijn verschillende antwoorden.
  • Het importeren van agenda-uitnodigingen. De uitnodiging zit in de ciphertext, en een afspraak bouwen uit de envelop zou een verkeerde vermelding op een echte agenda zetten.
  • Threadsamenvattingen en embeddings, voor de hele thread. Eén verzegeld antwoord is genoeg. Een samenvatting is de lezing van een model van de platte tekst, opgeslagen als leesbare metadata, en dat is de ene plek in deze pijplijn waar een body zou lekken naar een opslag die niemand als body beschouwt.

Alles wat de body niet nodig heeft blijft ongemoeid:

  • DMARC, DKIM en SPF. Die worden afgelezen aan Authentication-Results, wat ciphertext niet verbergt, dus een versleuteld bericht krijgt nog steeds een echt authenticatieoordeel in plaats van geen.
  • Threading, spamindeling en de blokkeerlijst: allemaal envelop- en headerwerk.
  • Bijlagen. Het ciphertext-deel blijft in attachments staan, encrypted-message.asc genoemd wanneer het naamloos binnenkomt, en downloadt via het endpoint hieronder. Het is precies wat de eigen lezer van de webapp ophaalt en in de browser ontsleutelt; voor een API-client, die geen sleutel heeft, blijft die download de enige manier om de post te lezen. Open hem in een client die er wel een heeft.
  • Een ondertekend bericht verliest hier niets van. Elk van de controles hierboven blijft erop draaien, en er wordt niets achtergehouden, en dat is waarom de verzegelde lijst een lijst van drie formaten is en niet van vijf.

De afwezigheid van encryption is geen bewering van platte tekst. Het betekent dat niemand heeft gekeken: het bericht dateert van voor de detectie, of bereikte de mailbox via een pad waar de detector niet draait. Niets vult het met terugwerkende kracht aan, dus een veld dat zegt "we hebben niet gekeken" mag nooit worden gelezen als "we hebben gekeken en niets gevonden".

Markeren en labelen

PATCH /threads/{id} neemt read, addLabelIds en removeLabelIds. Leesstatus is op elke backend die dit product ondersteunt een label, dus read zetten en labels verplaatsen in één aanroep houdt de volgorde deterministisch.

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

TRASH en SNOOZED worden hier geweigerd met label_not_directly_settable. Geen van beide statussen wordt door het label alleen gedragen (weggooien wist ook de maplabels, en een snooze heeft een wektijd nodig die ernaast wordt opgeslagen), dus ze met de hand zetten laat een thread achter in een staat die de app nooit produceert en waarvan hij niet kan herstellen. Gebruik de endpoints hieronder.

Prullenbak en snooze

EndpointDoet
POST /threads/{id}/trashVerplaatst naar de prullenbak en wist INBOX, SPAM, SNOOZED en ARCHIVE tegelijk.
POST /threads/{id}/snoozeBody { "wakeAt": "…" }. Verbergt hem en plant zijn terugkeer.
POST /threads/{id}/unsnoozeHaalt hem nu terug en annuleert de geplande terugkeer.

Snooze schrijft twee dingen: het label dat de thread verbergt, en de vermelding die hem terugbrengt. Het een zonder het ander doen is precies waarom dit endpoints zijn en geen labelbewerkingen.

Bijlagen

GET /threads/{id}/messages/{messageId}/attachments geeft elke bijlage terug met filename, contentType, size en content als base64. content is een lege string waar de opgeslagen bytes niet konden worden gevonden, dus controleer de lengte voordat je decodeert.

Een versleutelde envelop staat hier niet volledig in. De ciphertext wel (dat is het bericht, en hem downloaden is de enige manier waarop een API-client deze post leest), maar het PGP/MIME-versiedeel en een eventuele losgekoppelde handtekening worden uit de lijst gehouden, omdat ze als rommelbijlagen werden gerenderd en een aanroeper er niets mee kan. Beide behouden hun ids in encryption.parts, wat de twee weergaven verbindt; dit endpoint geeft ze niet terug.