پرش به مستندات
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، تنها یک سال، یا ثانیه یا میلی‌ثانیهٔ epoch بنویسید.

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`، از روی بدنه‌ای که با سرآیند armor مربوط به PGP آغاز می‌شود. بخش `pkcs7-mime` ای که اصلاً `smime-type` ندارد به‌عنوان `smime-encrypted` خوانده می‌شود، چون RFC 8551 به‌صورت پیش‌فرض همین را می‌گوید.
detectedAtstring
ISO 8601، زمانی که آشکارساز اجرا شده، یعنی همان زمانی که پیام اینجا دریافت شده است. دربارهٔ اینکه پیام کی یا به دست چه کسی رمزگذاری شده چیزی نمی‌گوید.
rawRetainedboolean
اینکه آیا بایت‌های اصلی RFC822 نگه داشته شده‌اند تا بتوان پیام را دست‌نخورده بازگرداند. امروز روی هر پیامی false است، چون هنوز چیزی اینجا ایمیل خام را نگه نمی‌دارد. از همین حالا در پاسخ هست تا روزی که این وضع تغییر کند، همان روزِ مهاجرت دوبارهٔ همهٔ پیام‌های ذخیره‌شده نباشد.
partsobject[]
بخش‌های پاکتی که این قالب از آن‌ها استفاده می‌کند. هرجا `encryption` باشد این هم هست، و وقتی چیزی برای نام‌بردن نباشد خالی است: `pgp-inline` اصلاً بخش جداگانه‌ای ندارد، چون armor آن خودِ بدنه است و در `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-inlinearmor در خود بدنه. همیشه فقط از روی متن بدنه خوانده می‌شود، پس پاسخی که صرفاً یک بلوک armor را نقل می‌کند با آن اشتباه گرفته نمی‌شود.مهرشده
smime-encryptedیک بخش pkcs7-mime از نوع S/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.skipped برابر encrypted است.
  • شرط‌های مربوط به بدنه در قواعد. شرط‌های پاکت و سرآیند دقیقاً مانند گذشته اجرا می‌شوند؛ قاعده‌ای که دربارهٔ بدنه پرسیده باشد به‌جای شمرده‌شدن به‌عنوان عدم تطابق، ارزیابی‌نشده ثبت می‌شود، چون «تطبیق نیافت» و «خوانده نشد» دو پاسخ متفاوت‌اند.
  • درون‌ریزی دعوت‌نامهٔ تقویم. دعوت‌نامه درون متن رمزی است، و ساختن رویداد از روی پاکت، ورودی نادرستی را روی یک تقویم واقعی می‌نشاند.
  • خلاصه‌ها و بردارهای رشته، برای کل رشته. یک پاسخ مهرشده کافی است. خلاصه، خوانش یک مدل از متن آشکار است که به‌صورت فرادادهٔ رمزنشده ذخیره می‌شود، و این تنها جایی در این خط لوله است که یک بدنه می‌تواند به انباری نشت کند که هیچ‌کس آن را بدنه نمی‌داند.

هر چیزی که به بدنه نیاز ندارد دست‌نخورده می‌ماند:

  • DMARC، DKIM و SPF. این‌ها از روی Authentication-Results خوانده می‌شوند که متن رمزی آن را پنهان نمی‌کند، پس پیام رمزگذاری‌شده هم حکم احراز اصالت واقعی می‌گیرد، نه هیچ.
  • رشته‌بندی، دسته‌بندی هرزنامه و فهرست مسدودی: همه کار روی پاکت و سرآیند است.
  • پیوست‌ها. بخش ciphertext در attachments می‌ماند، و اگر بدون نام برسد encrypted-message.asc نامیده می‌شود، و از راه نقطهٔ پایانی پایین دانلود می‌شود. این دقیقاً همان چیزی است که خوانندهٔ خودِ برنامهٔ وب می‌گیرد و در مرورگر رمزگشایی می‌کند؛ برای کلاینت API که هیچ کلیدی ندارد، آن دانلود تنها راه خواندن این ایمیل است. آن را در کلاینتی باز کنید که کلید دارد.
  • پیام امضاشده هیچ‌کدام از این‌ها را از دست نمی‌دهد. تک‌تک بررسی‌های بالا روی آن اجرا می‌شوند و چیزی دریغ نمی‌شود، و به همین دلیل فهرست مهرشده‌ها فهرستی از سه قالب است، نه پنج.

نبودِ encryption ادعای متن آشکار نیست. یعنی کسی نگاه نکرده است: پیام پیش از وجود آشکارساز رسیده، یا از مسیری به صندوق رسیده که آشکارساز را اجرا نمی‌کند. چیزی این مقدار را به‌گذشته پر نمی‌کند، پس فیلدی که می‌گوید «بررسی نکردیم» هرگز نباید «بررسی کردیم و چیزی نبود» خوانده شود.

علامت‌گذاری و برچسب‌زدن

PATCH /threads/{id} مقادیر read، addLabelIds و removeLabelIds را می‌گیرد. وضعیت خوانده‌شدن روی هر backend ای که این محصول پشتیبانی می‌کند یک برچسب است، پس تنظیم 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 نگه می‌دارند که این دو نما را به هم مرتبط می‌کند؛ این نقطهٔ پایانی آن‌ها را برنمی‌گرداند.