Перейти к документации
API

Цепочки

Чтение и упорядочивание почты.

GETapi.openemail.uk/threads

Выполняет любой из 7 запросов на этой странице в вашем рабочем пространстве, с вашим собственным ключом.

Получение списка

GET /threads?folder=inbox. Переданный query ищет по тому же локальному индексу. Обычные слова должны встретиться все, и каждое совпадает нестрого, без учёта регистра, диакритики и разделителей, так что min находит «Benjamin». Фраза в кавычках сопоставляется как написана, если не считать регистра и диакритики, так что "ben jamin" не находит «Ben-Jamin». Служебные слова вроде the или emails отбрасываются из списка обычных слов, когда остаётся что искать помимо них. Операторы вроде from:, to:, subject:, label:, is:unread, has:pdf, after:2026/01/31 и newer_than:7d сужают поиск, а OR, скобки и ведущий - их комбинируют. Получатели хранятся одним списком без ролей и никогда не содержат Bcc, так что cc: читает то же поле, что и to:, а bcc: сам по себе ничего не находит. from:me — это отправленная вами почта, а to:me — почта, несущая один из ваших собственных адресов, включая псевдонимы, среди получателей или как адрес доставки.

Слова и операторы from:, to:, cc:, subject: и body: читают самое новое сообщение каждой цепочки: его отправителя, получателей, тему и первые 4 000 символов тела. filename: и has: читают все вложения всей переписки, а label:, in: и is: читают всю переписку. folder продолжает действовать, если только запрос не называет папку через in: или через is:, обозначающий папку, например is:sent, а in:anywhere ищет по всем папкам — и сам по себе, и рядом с другими условиями. Список черновиков — исключение: он остаётся в черновиках, что бы ни называл запрос.

Значение, которое поиск не может использовать, игнорируется, а не сужает выборку, так что опечатка в значении расширяет результат, а не опустошает его: category:, larger:, smaller:, size:, messagesize:, list:, rfc822msgid:, received:, sent:, слова-категории вроде is:promotions, слово при has:, не называющее вид вложения, importance: со значением, отличным от high или low, нечитаемая дата и длительность, единица которой не h, d, w, m или y. Имя оператора, которое поиску неизвестно, например project:, ищется как обычный текст. Даты читают последнюю активность в цепочке, в UTC, причём after: включает названный день, а before: исключает; записывайте дату как YYYY/MM/DD, YYYY-MM-DD, YYYYMMDD, просто год, либо секунды или миллисекунды эпохи.

nextPageToken непрозрачен. Возвращайте ровно то, что вам выдали; никогда не конструируйте и не правьте его. Его форма не часть контракта.

Получение

GET /threads/{id} возвращает все сообщения цепочки, а не только самое свежее, вместе с её метками и признаком того, есть ли в ней непрочитанное.

Сообщения, пришедшие зашифрованными

Этот API ничего не шифрует и не расшифровывает. Он не может открыть сообщение, зашифрованное кем-то другим, и не может отправить зашифрованное. Запрос, несущий маркер шифрования, отклоняется с 422, потому что устанавливать его вправе только те поверхности, у которых есть ключи, а ни у одного клиента API ключа нет. Что он делает — так это РАСПОЗНАЁТ запечатанный конверт на входе, по верхнеуровневому Content-Type и не более того, и затем сообщает об этом в сообщении.

OpenEmail теперь и сам держит ключи, и стоит точно сказать, какую половину и где. Владелец почтового ящика создаёт OpenPGP-личность в своём браузере и публикует ОТКРЫТЫЙ ключ в каталоге, который могут разрешить другие вошедшие отправители OpenEmail. Закрытая половина создаётся в том браузере, никогда не отправляется сюда и невосстановима, так что ничто в этом API не может ничего расшифровать, и ни обращение в поддержку, ни судебный запрос, ни наша резервная копия не дадут ключа, которым это можно было бы сделать. Веб-приложение теперь умеет ОТКРЫВАТЬ сообщение PGP/MIME или inline-PGP, когда ключ есть в браузере читателя, но расшифровка происходит во вкладке, и открытый текст никогда не записывается обратно: сохранённое сообщение остаётся шифротекстом, и ни один ответ этого API никогда не несёт открытый текст. Приложение теперь умеет запечатать новое сообщение в браузере и отправить его: редактор шифрует на опубликованные ключи получателей, и почта уходит как PGP/MIME. Этот API по-прежнему ничего запечатать не может, так что поле ниже описывает и почту, зашифрованную кем-то другим, и почту, запечатанную во вкладке OpenEmail.

Это заслуживает отдельного поля из-за того, какова была альтернатива. Запечатанное сообщение не хранит читаемого тела, так что decodedBody приходит как "" — те же байты, что и у сообщения, у которого содержимого действительно не было. encryption — это то, что позволяет различить их до того, как вы начнёте действовать, и это утверждение о конверте, а не проверка: видеть, что сообщение запечатано, — не то же самое, что открыть его.

Ответ
{    "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'
Какой конверт пришёл. Считывается с верхнеуровневого `Content-Type` (его параметр `protocol` для PGP, его `smime-type` для S/MIME) либо, для `pgp-inline`, с тела, начинающегося с заголовка PGP-брони. Часть `pkcs7-mime`, вовсе не несущая `smime-type`, читается как `smime-encrypted`, каковой её по умолчанию и делает RFC 8551.
detectedAtstring
ISO 8601, когда отработал детектор, то есть когда сообщение было принято здесь. Это ничего не говорит о том, когда и кем сообщение было зашифровано.
rawRetainedboolean
Сохранены ли исходные байты RFC822, чтобы сообщение можно было вернуть целиком. Сегодня false у каждого сообщения, поскольку здесь пока ничто не хранит сырую почту. Оно есть в ответе уже сейчас, чтобы день, когда это изменится, не стал ещё и днём повторной миграции всех сохранённых сообщений.
partsobject[]
Части конверта, которые использует этот формат. Присутствует всегда, когда присутствует `encryption`, и пусто, когда называть нечего: у `pgp-inline` вообще нет отдельной части, поскольку его броня И ЕСТЬ тело и приходит в `decodedBody`.
parts[].indexnumber
Какой MIME-частью исходного сообщения это было, считая по частям в том виде, в каком они пришли, а не по `attachments`. Эти два списка различаются, и ровно поэтому это записывается.
parts[].attachmentIdstring
Идентификатор, который эта часть несёт в `attachments`, если она там вообще появляется: идентификатор сообщения с добавленным индексом части. Часть `ciphertext` перечисляется и скачивается как любой другой файл; `version` и `signature` из списка исключены, так что их идентификаторы лишь соотносят два представления, и не более того. Эндпоинт вложений их не вернёт.
parts[].role'version' | 'ciphertext' | 'signature'
`version` — управляющая часть PGP/MIME, `ciphertext` — само сообщение, `signature` — открепленная подпись. Забирать стоит только `ciphertext`; две другие — протокольная мебель, которая раньше отображалась как мусорные вложения, а теперь нет.
formatЧто пришлоТело
pgp-mimeКонверт PGP/MIME: multipart/encrypted с protocol=application/pgp-encrypted.Запечатано
pgp-inlineБроня в самом теле. Считывается только с текста тела, так что ответ, просто цитирующий бронированный блок, не будет принят за такое сообщение.Запечатано
smime-encryptedЧасть S/MIME pkcs7-mime с smime-type=enveloped-data либо вовсе без smime-type.Запечатано
pgp-signedОткрепленная PGP-подпись рядом с сообщением: multipart/signed с protocol=application/pgp-signature.Читаемо
smime-signedОткрепленная подпись S/MIME: протокол pkcs7-signature либо smime-type=signed-data.Читаемо

Подписано — не значит запечатано, и ветвление по наличию encryption вместо format переворачивает это ровно наоборот. Подпись — это утверждение о том, кто написал сообщение, а не обёртка вокруг него: тело подписанного сообщения открыто и читается как любое другое. Считайте нечитаемыми pgp-mime, pgp-inline и smime-encrypted, а два подписанных формата — обычной почтой.

Что меняется у запечатанного сообщения

Что-либо меняют только три запечатанных формата, и меняется это при приёме, а не в этом ответе. Всё, что читало бы тело, отходит в сторону вместо того, чтобы прочитать шифротекст и сообщить результат, которого оно не могло получить:

  • Поиск по телу. Сообщение индексируется с пустым фрагментом тела, так что его по-прежнему можно найти по отправителю, теме, адресу и метке — и нельзя по тому, что внутри.
  • Проход фишинг-оценщика по телу. Вердикт всё равно приходит и говорит, чего он не смог: risk.signals несёт body-encrypted, а risk.aiChecked равно false.
  • Проверка на авторство ИИ, которая отказывается отвечать, а не гадает: aiWritten.level равен unknown, а aiWritten.skippedencrypted.
  • Условия по телу в правилах. Условия по конверту и заголовкам отрабатывают в точности как раньше; правило, спрашивавшее про тело, записывается как невычисленное, а не засчитывается как несовпадение, потому что «не совпало» и «не удалось прочитать» — разные ответы.
  • Импорт приглашений календаря. Приглашение находится внутри шифротекста, и построение события из конверта поставило бы неверную запись в настоящий календарь.
  • Сводки цепочки и эмбеддинги — для всей цепочки. Достаточно одного запечатанного ответа. Сводка — это прочтение моделью открытого текста, сохранённое как метаданные в открытом виде, и это единственное место в конвейере, где тело утекло бы в хранилище, которое никто телом не считает.

Всё, чему тело не нужно, остаётся нетронутым:

  • DMARC, DKIM и SPF. Они считываются с Authentication-Results, который шифротекст не скрывает, так что зашифрованное сообщение всё равно получает настоящий вердикт аутентификации, а не никакого.
  • Сшивание в цепочки, раскладка спама и список блокировок: всё это работа с конвертом и заголовками.
  • Вложения. Часть с шифротекстом остаётся в attachments под именем encrypted-message.asc, если она пришла без имени, и скачивается через эндпоинт ниже. Это ровно то, что забирает и расшифровывает в браузере собственный читатель веб-приложения; для клиента API, у которого ключа нет, эта загрузка остаётся единственным способом прочитать письмо. Откройте его в клиенте, у которого ключ есть.
  • Подписанное сообщение ничего из этого не теряет. Все перечисленные выше проверки продолжают на нём работать, и ничего не утаивается, — поэтому список запечатанных содержит три формата, а не пять.

Отсутствие encryption — не утверждение о том, что текст открыт. Оно означает, что никто не смотрел: сообщение старше детектора или попало в почтовый ящик путём, на котором детектор не работает. Ничто это не дозаполняет, так что поле, говорящее «мы не проверяли», никогда нельзя читать как «мы проверили и ничего не нашли».

Пометки и метки

PATCH /threads/{id} принимает read, addLabelIds и removeLabelIds. Состояние прочтения — это метка на любом бэкенде, который поддерживает продукт, так что установка read и перемещение меток в одном вызове делают порядок детерминированным.

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

TRASH и SNOOZED здесь отклоняются с label_not_directly_settable. Ни одно из этих состояний не задаётся одной лишь меткой (удаление в корзину ещё и снимает метки папок, а отложенному нужно время пробуждения, хранимое рядом), так что ручная установка оставит цепочку в состоянии, которого приложение никогда не создаёт и из которого не может выйти. Используйте эндпоинты ниже.

Корзина и откладывание

ЭндпоинтДействие
POST /threads/{id}/trashПеремещает в Bin, снимая разом INBOX, SPAM, SNOOZED и ARCHIVE.
POST /threads/{id}/snoozeТело { "wakeAt": "…" }. Прячет цепочку и планирует её возвращение.
POST /threads/{id}/unsnoozeВозвращает её сейчас же и отменяет запланированное возвращение.

Откладывание записывает две вещи: метку, которая прячет цепочку, и запись, которая её возвращает. Сделать одно без другого — ровно та причина, по которой это эндпоинты, а не правки меток.

Вложения

GET /threads/{id}/messages/{messageId}/attachments возвращает каждое вложение с filename, contentType, size и content в base64. content — пустая строка там, где сохранённые байты не удалось найти, так что проверяйте её длину перед декодированием.

Зашифрованный конверт представлен здесь не целиком. Шифротекст есть (это и есть сообщение, и его загрузка — единственный способ для клиента API прочитать эту почту), но часть version у PGP/MIME и любая открепленная подпись из списка исключены, потому что они отображались как мусорные вложения и вызывающей стороне с ними делать нечего. Обе сохраняют свои идентификаторы в encryption.parts, что соотносит два представления; этот эндпоинт их не возвращает.