Saltar para a documentação
API

Conversas

Ler e organizar correio.

GETapi.openemail.uk/threads

Executa qualquer uma das 7 chamadas desta página contra o seu espaço de trabalho, com a sua própria chave.

Listagem

GET /threads?folder=inbox. Passar query pesquisa no mesmo índice local. As palavras simples têm de aparecer todas, e cada uma corresponde de forma flexível, ignorando maiúsculas e minúsculas, acentos e separadores, pelo que min encontra "Benjamin". Uma frase entre aspas é correspondida tal como está escrita, exceto quanto a maiúsculas e acentos, pelo que "ben jamin" não encontra "Ben-Jamin". Palavras de enchimento como the ou emails são removidas de uma lista de palavras simples quando resta outra coisa para pesquisar. Operadores como from:, to:, subject:, label:, is:unread, has:pdf, after:2026/01/31 e newer_than:7d restringem a pesquisa, e OR, parênteses e um - inicial combinam-nos. Os destinatários são guardados numa única lista, sem funções, e nunca incluem um Bcc, pelo que cc: lê o mesmo campo que to: e bcc: não tem correspondências próprias. from:me é o correio que enviou, e to:me é o correio que tem um dos seus próprios endereços, incluindo aliases, entre os destinatários ou como endereço para onde foi entregue.

As palavras e os operadores from:, to:, cc:, subject: e body: leem a mensagem mais recente de cada conversa: o remetente, os destinatários, o assunto e os primeiros 4000 caracteres do corpo. filename: e has: leem todos os anexos da conversa inteira, e label:, in: e is: leem a conversa inteira. folder continua a aplicar-se, a menos que a consulta indique uma pasta com in:, ou com um is: que seja uma pasta, como is:sent, e in:anywhere pesquisa em todas as pastas, tanto sozinho como ao lado de outros termos. Uma listagem de rascunhos é a exceção e permanece nos rascunhos, seja qual for a pasta indicada na consulta.

Um valor que a pesquisa não consegue usar é ignorado em vez de restringir, pelo que um erro de digitação num valor alarga o resultado em vez de o esvaziar: category:, larger:, smaller:, size:, messagesize:, list:, rfc822msgid:, received:, sent:, as palavras de categoria como is:promotions, um has: com uma palavra que não designa nenhum tipo de anexo, um importance: que não seja high nem low, uma data ilegível e uma duração cuja unidade não seja h, d, w, m ou y. Um nome de operador que não conhece, project: por exemplo, é pesquisado como texto simples. As datas referem-se à atividade mais recente da conversa, em UTC, com after: a incluir o dia indicado e before: a excluí-lo; escreva-as como YYYY/MM/DD, YYYY-MM-DD, YYYYMMDD, um ano isolado, ou segundos ou milissegundos desde a época Unix.

nextPageToken é opaco. Devolva exatamente o que recebeu; nunca construa nem edite um. O seu formato não faz parte do contrato.

Obtenção

GET /threads/{id} devolve todas as mensagens da conversa, não apenas a mais recente, juntamente com as respetivas etiquetas e a indicação de haver algo por ler.

Mensagens que chegaram encriptadas

Esta API não encripta nem desencripta. Não consegue abrir uma mensagem que outra pessoa encriptou, nem consegue enviar uma mensagem encriptada. Um pedido que contenha um marcador de encriptação é recusado com um 422, porque as únicas superfícies que o podem definir são as que detêm as chaves, e nenhum cliente da API detém uma chave. O que faz é RECONHECER um envelope selado à entrada, a partir do Content-Type de nível superior e de mais nada, e depois indicá-lo na mensagem.

O próprio OpenEmail já detém chaves, e convém ser exato sobre qual das metades e onde. O titular de uma caixa de correio gera uma identidade OpenPGP no browser e publica a chave PÚBLICA num diretório que outros remetentes do OpenEmail com sessão iniciada conseguem resolver. A metade privada é criada nesse browser, nunca é enviada para aqui e nunca é recuperável, pelo que nada nesta API consegue desencriptar o que quer que seja, e nenhum pedido de suporte, intimação judicial ou cópia de segurança nossa produz uma chave que o consiga. A aplicação web já consegue ABRIR uma mensagem PGP/MIME ou PGP inline quando a chave está no browser do leitor, mas essa desencriptação acontece no separador e o texto simples nunca é escrito de volta: a mensagem guardada continua a ser texto cifrado, e nenhuma resposta desta API contém alguma vez o texto aberto. A aplicação já consegue selar uma nova mensagem no browser e enviá-la: o editor encripta para as chaves publicadas dos destinatários e o correio sai como PGP/MIME. Esta API continua a não conseguir selar nada, pelo que o campo abaixo descreve tanto o correio que outra pessoa encriptou como o correio selado num separador do OpenEmail.

Isto merece um campo por causa do que era a alternativa. Uma mensagem selada não guarda nenhum corpo legível, pelo que decodedBody é devolvido como "", os mesmos bytes de uma mensagem que genuinamente não tinha conteúdo. encryption é o que lhe permite distinguir as duas antes de agir sobre uma delas, e é uma afirmação sobre o envelope, não uma verificação: ver que uma mensagem está selada não é o mesmo que tê-la aberto.

Resposta
{    "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'
Que envelope chegou. Lido a partir do `Content-Type` de nível superior (o parâmetro `protocol` no caso de PGP, o `smime-type` no caso de S/MIME) ou, para `pgp-inline`, a partir de um corpo que começa com o cabeçalho de armadura PGP. Uma parte `pkcs7-mime` sem qualquer `smime-type` é lida como `smime-encrypted`, que é o que o RFC 8551 lhe atribui por predefinição.
detectedAtstring
ISO 8601, quando o detetor correu, ou seja, quando a mensagem foi ingerida aqui. Não diz nada sobre quando a mensagem foi encriptada, nem por quem.
rawRetainedboolean
Se os bytes RFC822 originais foram conservados, para que a mensagem pudesse ser devolvida inteira. False em todas as mensagens atualmente, uma vez que nada aqui guarda ainda correio em bruto. Já está na resposta para que o dia em que isso mudar não seja também o dia em que todas as mensagens guardadas têm de ser migradas novamente.
partsobject[]
As partes do envelope que este formato usa. Presente sempre que `encryption` estiver presente, e vazio quando não há nenhuma a indicar: `pgp-inline` não tem qualquer parte separada, uma vez que a sua armadura É o corpo e chega em `decodedBody`.
parts[].indexnumber
Que parte MIME da mensagem original era esta, contada sobre as partes tal como chegaram e não sobre `attachments`. As duas listas diferem, e é exatamente por isso que isto é registado.
parts[].attachmentIdstring
O id que esta parte tem em `attachments`, quando lá aparece: o id da mensagem com o índice da parte acrescentado. A parte `ciphertext` é listada e descarrega-se como qualquer outro ficheiro; `version` e `signature` ficam fora da lista, pelo que os seus ids servem apenas para correlacionar as duas vistas. O endpoint de anexos não os devolve.
parts[].role'version' | 'ciphertext' | 'signature'
`version` é a parte de controlo PGP/MIME, `ciphertext` é a mensagem, `signature` é uma assinatura destacada. Só vale a pena obter `ciphertext`; as outras duas são acessórios do protocolo que costumavam aparecer como anexos inúteis e já não aparecem.
formatO que chegouCorpo
pgp-mimeUm envelope PGP/MIME: multipart/encrypted com protocol=application/pgp-encrypted.Selado
pgp-inlineArmadura no próprio corpo. Só é lida a partir do texto do corpo, pelo que uma resposta que apenas cita um bloco com armadura não é confundida com uma mensagem selada.Selado
smime-encryptedUma parte S/MIME pkcs7-mime com smime-type=enveloped-data, ou uma sem qualquer smime-type.Selado
pgp-signedUma assinatura PGP destacada ao lado da mensagem: multipart/signed com protocol=application/pgp-signature.Legível
smime-signedUma assinatura S/MIME destacada: um protocolo pkcs7-signature, ou smime-type=signed-data.Legível

Assinado não é selado, e ramificar com base na presença de encryption em vez de em format inverte exatamente isso. Uma assinatura é uma afirmação sobre quem escreveu a mensagem, não um invólucro à volta dela: o corpo de uma mensagem assinada está em claro e lê-se como qualquer outro. Trate pgp-mime, pgp-inline e smime-encrypted como ilegíveis, e os dois formatos assinados como correio normal.

O que muda numa mensagem selada

Só os três formatos selados mudam alguma coisa, e a mudança acontece na ingestão e não nesta resposta. Tudo o que teria lido o corpo abstém-se, em vez de ler texto cifrado e reportar um resultado que não poderia ter obtido:

  • A pesquisa no corpo. A mensagem é indexada com um excerto de corpo vazio, pelo que continua a ser encontrada por remetente, assunto, endereço e etiqueta, mas não por nada no seu interior.
  • A análise do corpo feita pelo avaliador de phishing. O veredicto continua a chegar e diz o que não conseguiu fazer: risk.signals contém body-encrypted e risk.aiChecked é false.
  • A verificação de autoria por IA, que se abstém em vez de adivinhar: aiWritten.level é unknown e aiWritten.skipped é encrypted.
  • As condições de corpo nas regras. As condições de envelope e de cabeçalho correm exatamente como antes; uma regra que perguntava pelo corpo é registada como não avaliada em vez de contada como não correspondente, porque "não correspondeu" e "não pôde ser lido" são respostas diferentes.
  • A importação de convites de calendário. O convite está dentro do texto cifrado, e construir um evento a partir do envelope poria uma entrada errada num calendário real.
  • Os resumos e embeddings de conversas, para a conversa inteira. Basta uma resposta selada. Um resumo é a leitura que um modelo faz do texto simples, guardada como metadados em claro, que é o único ponto deste pipeline em que um corpo passaria para um armazenamento que ninguém considera um corpo.

Tudo o que não precisa do corpo não é afetado:

  • DMARC, DKIM e SPF. São lidos a partir de Authentication-Results, que o texto cifrado não esconde, pelo que uma mensagem encriptada continua a receber um veredicto de autenticação real em vez de nenhum.
  • Agrupamento em conversas, classificação de spam e a lista de bloqueio: tudo trabalho sobre o envelope e os cabeçalhos.
  • Anexos. A parte cifrada fica em attachments, com o nome encrypted-message.asc quando chega sem nome, e descarrega-se através do endpoint abaixo. É exatamente o que o leitor da própria aplicação web obtém e desencripta no browser; para um cliente da API, que não detém nenhuma chave, essa transferência continua a ser a única forma de ler o correio. Abra-a num cliente que tenha uma.
  • Uma mensagem assinada não perde nada disto. Todas as verificações acima continuam a correr sobre ela, e nada é retido, e é por isso que a lista de formatos selados tem três formatos e não cinco.

A ausência de encryption não é uma afirmação de texto simples. Significa que ninguém verificou: a mensagem é anterior à deteção, ou chegou à caixa de correio por um caminho que não executa o detetor. Nada a preenche retroativamente, pelo que um campo que diz "não verificámos" nunca deve ser lido como "verificámos e não encontrámos nada".

Marcação e etiquetagem

PATCH /threads/{id} aceita read, addLabelIds e removeLabelIds. O estado de leitura é uma etiqueta em todos os backends que este produto suporta, pelo que definir read e mover etiquetas numa só chamada mantém a ordem determinística.

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

TRASH e SNOOZED são recusados aqui com label_not_directly_settable. Nenhum dos dois estados é representado apenas pela sua etiqueta (enviar para o lixo também remove as etiquetas de pasta, e um adiamento precisa de uma hora de despertar guardada ao lado), pelo que defini-los à mão deixa uma conversa num estado que a aplicação nunca produz e do qual não consegue recuperar. Use os endpoints abaixo.

Lixo e adiamento

EndpointFaz
POST /threads/{id}/trashMove para o Lixo, removendo INBOX, SPAM, SNOOZED e ARCHIVE em conjunto.
POST /threads/{id}/snoozeCorpo { "wakeAt": "…" }. Oculta-a e agenda o seu regresso.
POST /threads/{id}/unsnoozeTrá-la de volta agora e cancela o regresso agendado.

O adiamento escreve duas coisas: a etiqueta que oculta a conversa e a entrada que a traz de volta. É precisamente para evitar fazer uma sem a outra que estes são endpoints e não edições de etiquetas.

Anexos

GET /threads/{id}/messages/{messageId}/attachments devolve cada anexo com filename, contentType, size e content em base64. content é uma string vazia quando os bytes guardados não foram encontrados, por isso verifique o comprimento antes de descodificar.

Um envelope encriptado não está todo aqui. O texto cifrado está (é a mensagem, e descarregá-lo é a única forma de um cliente da API ler este correio), mas a parte de versão PGP/MIME e qualquer assinatura destacada ficam fora da lista, porque apareciam como anexos inúteis e não há nada que quem chama possa fazer com elas. Ambas mantêm os seus ids em encryption.parts, que correlaciona as duas vistas; este endpoint não as devolve.