تخطَّ إلى المستندات
CLI

القوالب والقواعد وخطافات الويب

كل أمر `templates` و`rules` و`webhooks`: أجسام محفوظة ترسلها بالمعرّف النصي، وقواعد ترتّب البريد الوارد، وأحداث موقّعة لخادمك الخاص.

ثلاث مساحات أسماء

تتيح مساحات الأسماء الثلاث هذه لصندوق البريد أن يعمل دون أن يراقبه أحد. يخزّن templates الأجسام التي ترسلها مرات كثيرة، ويرتّب rules البريد لحظة وصوله، ويخبر webhooks خادمك الخاص بما حدث. وكل أمر طريقة SDK باسمها بصيغة kebab-case، فيصبح webhooks.rotateSecret هو openemail webhooks rotate-secret، ويقرأ الوسائط والخيارات كأي أمر موارد آخر.

مساحة الأسماءأيضًاالقراءة تحتاج إلىالتغيير يحتاج إلى
templatestemplatetemplates:readtemplates:write، وemails:send أيضًا لـ send
rulesrulerules:read، بما في ذلك testrules:write
webhookswebhookwebhooks:readwebhooks:write، بما في ذلك test وreplay-delivery

تسرد هذه الصفحة كل أمر وما يجدر معرفته قبل كتابة سكربت له. ولكل وسيط وخيار، بنوعه والنطاقات التي يحتاجها ونقطة النهاية وما يعيده، شغّل openemail <namespace> <verb> --help. أضف --json لتحصل على الصفحة نفسها بصيغة JSON.

المساعدة
openemail templates --helpopenemail rules create --helpopenemail webhooks replay-delivery --help --json

القوالب

أجسام تُخزَّن مرة وتُرسل مرات كثيرة، مع إصدارات ومعاينات وخصائص ذات أنواع. كل أمر يأخذ <id-or-slug> يقبل معرّف tpl_ أو المعرّف النصي. والمعرّف النصي لا يتغير أبدًا عند إعادة تسمية القالب، لذا ثبّته في السكربتات.

الأمرما تفعله
openemail templates listاسرد القوالب، الأحدث تحديثًا أولًا. يُبقي --status المسودات أو النشطة أو المؤرشفة، ويطابق --search الأسماء والمعرّفات النصية والمواضيع، ويختار --sort الترتيب
openemail templates get <id-or-slug>اقرأ قالبًا مع إصداره الرئيسي كاملًا، بما فيه الجسم
openemail templates create --name <value>أنشئ قالبًا وإصداره الأول. يبقى مسودة ما لم تمرّر --publish، ويبذره --starter من تصميم مبدئي
openemail templates update <id-or-slug>عدّل الاسم أو المعرّف النصي أو الوصف أو الحالة، أو جسم المسودة. وتبقى عمليات الإرسال على الإصدار المنشور حتى تنشر
openemail templates duplicate <id-or-slug>انسخ الإصدار الرئيسي إلى قالب جديد، يبدأ مسودة
openemail templates replace-content <id-or-slug>استبدل الجسم بجسم تصميم مبدئي (--starter) أو قالب آخر (--from-template-id). يطلب منك التأكيد
openemail templates delete <id-or-slug>احذف قالبًا وكل إصداراته. يطلب منك التأكيد
openemail templates list-versions <id-or-slug>اسرد الإصدارات، الأحدث أولًا، دون أجسامها
openemail templates get-version <id-or-slug> <version>اقرأ إصدارًا واحدًا مع جسمه، دون المساس بالمسودة
openemail templates publish <id-or-slug>انشر المسودة كي تُحال إليها عمليات الإرسال. ونشر إصدار رئيسي منشور بالفعل لا يغيّر شيئًا
openemail templates restore-version <id-or-slug> <version>أعِد جسم إصدار أقدم ليصبح المسودة. يطلب منك التأكيد
openemail templates delete-version <id-or-slug> <version>احذف إصدارًا واحدًا. يُرفض الإصدار المنشور والإصدار الرئيسي والإصدار الوحيد. يطلب منك التأكيد
openemail templates list-startersاسرد التصاميم المبدئية المدمجة
openemail templates get-starter <slug>اقرأ تصميمًا مبدئيًا واحدًا كاملًا، مع شجرة كتله ومعاينة معروضة
openemail templates list-fontsاسرد خطوط الويب التي قد يحمّلها القالب
openemail templates renderاعرض جسمًا غير مخزّن في أي مكان، من --html أو --document
openemail templates preview <id-or-slug>اعرض قالبًا مخزّنًا مع --props و--slots، بما فيه المسودات، دون إرساله
openemail templates get-analytics <id-or-slug>عمليات الإرسال والفتح والنقر خلال فترة، حسب اليوم وحسب المصدر وحسب الإصدار
openemail templates list-sends <id-or-slug>الرسائل الفردية التي أرسلها القالب، الأحدث أولًا، صفحةً صفحة
openemail templates send <id-or-slug> --from <value> --to <a,b>أرسل بريدًا معروضًا من الإصدار المنشور، أو من الإصدار الذي يثبّته --template-version

للقالب إصدار رئيسي، وهو مسودة ما دامت فيه تعديلات غير منشورة، وإصدار منشور، وهو ما يستخدمه الإرسال دون --template-version. وcreate دون --publish، وتعديل الجسم بـ update، وreplace-content وrestore-version كلها تكتب المسودة، فلا يرى المستلمون شيئًا جديدًا حتى publish.

  • القالب المؤرشف يرفض الإرسال بـ template_archived. ويعيده publish نشطًا.
  • تضم مساحة العمل 200 قالب على الأكثر، بما فيها المؤرشفة، لذا فالحذف هو الطريقة الوحيدة لإفساح المجال.
  • يُرفض delete بـ template_in_use ما دام بث مجدول أو في الطابور يسمّي القالب.

القواعد

شروط وإجراءات تُقيَّم على البريد الوارد، بالترتيب الذي يعرضه rules list. ولا تعمل القاعدة إلا على البريد الذي يصل وهي مفعّلة. ولا يوجد أمر يطبّق قاعدة على بريد موجود بالفعل في صندوق البريد، وrules test هو طريقتك لترى ما ستلتقطه. تبدأ معرّفات القواعد بـ rul_.

الأمرما تفعله
openemail rules listاسرد القواعد بالترتيب الذي تعمل به. ويُبقي --enabled أو --no-enabled نوعًا واحدًا
openemail rules get <id>اقرأ قاعدة واحدة، مع matchCount وlastMatchedAt
openemail rules create --name <value> --conditions <json|@file|-> --actions <json|@file|->أنشئ قاعدة في آخر الترتيب. تكون مفعّلة ما لم تمرّر --no-enabled
openemail rules update <id>غيّر قاعدة. يستبدل --conditions و--actions القائمة كلها، وينقل --position هذه القاعدة وحدها
openemail rules delete <id>احذف قاعدة. يبقى ما فعلته بالفعل في list-runs. يطلب منك التأكيد
openemail rules reorder <rule-ids...>اضبط ترتيب كل القواعد دفعة واحدة، مع ذكر كل قاعدة مرة واحدة بالضبط
openemail rules test <id>شغّل قاعدة تشغيلًا تجريبيًا على بريد موجود بالفعل في صندوق البريد. لا تغيّر شيئًا، وتعمل على قاعدة معطّلة
openemail rules list-runsما فعلته القواعد فعلًا بالبريد الوارد، الأحدث أولًا. ويضيّقه --rule-id و--thread-id

--conditions قائمة من كائنات { field, op, value }، يربطها --match all أو --match any، حيث value سلسلة نصية دائمًا وnegate: true يعكس شرطًا واحدًا. و--actions قائمة من كائنات { type, value } تُطبَّق بالترتيب. تأخذ القاعدة من 1 إلى 20 شرطًا ومن 1 إلى 10 إجراءات، ويضم صندوق البريد 100 قاعدة على الأكثر.

  • حقول الشروط: from وfrom_domain وenvelope_from وto وcc وbcc وrecipient وreply_to وdelivered_to وsubject وbody وheader وlist_id وattachment_name وattachment_type وhas_attachment وattachment_size وmessage_size وspam وhour وweekday.
  • العوامل: matches وcontains وequals وstarts_with وends_with وgt وlt. ولا يعمل gt وlt إلا على الحقول الرقمية، ولا يأخذ has_attachment وspam إلا equals مع true أو false.
  • أنواع الإجراءات: label وremove_label وarchive وmark_read وstar وspam وtrash وforward وreply وblock_sender وreject. ويأخذ label وremove_label معرّف تسمية مثل USER_RECEIPTS، ويأخذ forward عنوانًا، ويأخذ reply معرّف قالب أو معرّفه النصي.
  • يطابق from_domain النطاقات الفرعية أيضًا، ويُقرأ hour وweekday بتوقيت UTC، مع 0 ليوم الأحد.
  • القاعدة التي فيها إجراء reject يجب أن تختبر envelope_from أيضًا، وإلا رُفضت بـ reject_needs_envelope.

خطافات الويب

نقاط نهاية على خادمك الخاص تستقبل أحداث صندوق البريد الموقّعة، مع أسرار توقيعها وسجل تسليمها وسجل تدقيق لكل تغيير. تبدأ معرّفات نقاط النهاية بـ whe_ ومعرّفات التسليم بـ whd_.

الأمرما تفعله
openemail webhooks listاسرد نقاط النهاية في مساحة العمل، الأحدث أولًا، مع حالتها الصحية
openemail webhooks get <id>اقرأ نقطة نهاية واحدة. سر التوقيع ليس جزءًا من أي قراءة أبدًا
openemail webhooks create --url <value>سجّل نقطة نهاية HTTPS. يطبع سر التوقيع، وهي المرة الوحيدة التي ترى فيها ذلك السر
openemail webhooks update <id>غيّر عنوان URL، أو الأحداث، أو قوائم السماح، أو حالة التفعيل. كل قائمة تستبدل المخزنة
openemail webhooks delete <id>احذف نقطة نهاية وسجل تسليمها. يطلب منك التأكيد
openemail webhooks rotate-secret <id>أصدر سر توقيع جديدًا. يتوقف القديم عن العمل فورًا. يطلب منك التأكيد
openemail webhooks test <id>أرسل حدث email.sent اصطناعيًا موقّعًا وأبلغ عن كيفية سير التسليم
openemail webhooks list-deliveries <id>محاولات التسليم لنقطة نهاية واحدة، الأحدث أولًا. ويضيّقها --status و--since و--until
openemail webhooks get-delivery <id> <delivery-id>محاولة واحدة كاملة: الجسم المرسل، وجواب خادمك، وكل محاولة للحدث، وما إذا كانت إعادة الإرسال ستُقبل
openemail webhooks replay-delivery <id> <delivery-id>أرسل حدثًا مخزنًا واحدًا إلى نقطة النهاية من جديد، الآن
openemail webhooks list-workspace-deliveriesمحاولات التسليم عبر كل نقاط النهاية، أو تلك التي يسمّيها --endpoint-ids
openemail webhooks list-activity <id>سجل التدقيق لنقطة نهاية واحدة: من أنشأها أو غيّرها أو اختبرها أو أعاد إرسالها أو أزالها
openemail webhooks list-workspace-activityسجل التدقيق لكل نقطة نهاية، بما فيها المزالة

أغفِل --event-types تستقبل نقطة النهاية المجموعة الافتراضية، وهي أحداث email.* عدا email.replied. أما email.replied وأحداث domain.* وأحداث suppression.* فلا تصلها إلا حين تسمّيها. ويضيّق --address-allowlist و--domain-allowlist نقطة النهاية على بعض العناوين أو النطاقات، كما يضيّقان مفتاح API.

  • تضم مساحة العمل 10 نقاط نهاية ما لم يرفع الدعم حدّها.
  • نقطة النهاية التي تفشل في 100 تسليم متتالٍ يوقفها الخادم، ويعيدها webhooks update <id> --enabled.
  • مع تسجيل الدخول عبر المتصفح، لا يستطيع قراءة تسليم بـ get-delivery إلا مالك مساحة العمل. وأي شخص آخر يحصل على owner_only ورمز الخروج 4.

افحص قالبًا، ثم انشره

يعرض templates preview بالضبط ما سينتجه إرسال بالقيم نفسها، بما في ذلك المسودات، ولا يحتاج إلا إلى templates:read، فيستطيع حتى مفتاح القراءة وحدها تشغيله. ويبلّغ عن الخاصية المطلوبة الناقصة كتحذير حيث كان send سيرفضها، لذا أفشل البناء عند أي تحذير. وpublish آمن في كل نشر، لأن نشر إصدار رئيسي منشور بالفعل لا يغيّر شيئًا.

CI
draft=$(openemail templates get order-shipped --json | jq .latestVersion)openemail templates preview order-shipped --template-version "$draft" \  --props '{"orderId":"AC-4192","customer":"Ada"}' --json | jq -e '.warnings == []'openemail templates publish order-shipped

الإرسال من قالب

ثبّت الإصدار، كي لا تغيّر إعادة كتابة تُنشر غدًا ما يرسله هذا الكود، ومرّر مفتاح لاتكرارية مأخوذًا مما تسبب في الإرسال، كي تعيد المحاولةُ بعد ضياع الجواب تشغيلَ الرسالة الأولى بدلًا من إرسال ثانية. يطبع --dry-run الطريقة وعنوان URL والترويسات مع حجب بيانات اعتمادك والجسم، ولا يرسل شيئًا ويخرج بالرمز 0. شغّله مرة أخرى دون --dry-run لترسل.

الطرفية
openemail templates send order-shipped \  --from 'Acme <[email protected]>' \  --to [email protected] \  --template-version 5 \  --props '{"orderId":"AC-4192","customer":"Ada"}' \  --idempotency-key order-shipped:AC-4192 \  --dry-run

اختبر قاعدة قبل أن تعمل

أنشئ القاعدة معطّلة، وشغّلها تشغيلًا تجريبيًا على البريد الحديث، ثم فعّلها حين تلتقط ما قصدته. مع تسجيل الدخول عبر المتصفح، يطلب rules create وrules update رمز تحقق لا يستطيع السكربت كتابته، لذا شغّل openemail verify أولًا. وخلال الدقائق الـ 60 التالية يشغّلهما ذلك الملف الشخصي دون سؤال.

conditions.json
[  { "field": "from_domain", "op": "equals", "value": "stripe.com" },  { "field": "has_attachment", "op": "equals", "value": "true" }]
actions.json
[  { "type": "label", "value": "USER_RECEIPTS" },  { "type": "archive" }]
الطرفية
openemail verifyrule=$(openemail rules create --name 'Stripe receipts' \  --conditions @conditions.json --actions @actions.json --no-enabled --json | jq -r .id)openemail rules test "$rule" --days 30 --limit 100openemail rules update "$rule" --enabled

اقرأ تحذيرات rules test قبل تطابقاته. يعني field_unevaluable أن شرطًا يقرأ شيئًا لم يعد البريد المخزن يحمله، فلم يستطع الاختبار الحكم عليه، ويعني forward_unverified أن هدف إعادة التوجيه غير مستضاف هنا. وتسرد wouldApply ما تعلنه القاعدة: فإعادة التوجيه إلى عنوان لم يؤكد تظل تفشل حين يصل بريد حقيقي.

ضع قاعدة أولًا، واعرف لماذا انتقلت رسالة

يأخذ rules reorder كل قاعدة في صندوق البريد مرة واحدة بالضبط. والقاعدة المُغفلة أو المذكورة مرتين تُرفض ولا يتحرك شيء. ويعيد rules list المعرّفات بالترتيب الذي تعمل به، لذا ضع القاعدة التي تريدها أولًا أمام البقية.

الطرفية
first=rul_4f1c9a2b7d3e8f6a0b5c1d2eopenemail rules reorder "$first" $(openemail rules list --all --ndjson \  | jq -r --arg first "$first" 'select(.id != $first) | .id')openemail rules list-runs --thread-id CAHk7pQ2x9LmZ4 --json | jq '.items[] | {ruleName, actions, failures}'

list-runs هو سجل ما حدث فعلًا. كل صف قاعدة واحدة طابقت رسالة واحدة، مع الإجراءات التي نفذت، وفي failures تلك التي رفضها صندوق البريد، مثل الرد على مرسل سبق الرد عليه ذلك اليوم. ويحتفظ كل صف بالاسم الذي كان للقاعدة حينها، لذا يعمل --rule-id مع قاعدة حذفتها منذ ذلك الحين.

سجّل خطاف ويب وأثبت أنه يعمل

يعرض webhooks create سر التوقيع مرة واحدة، ولا يعرضه أي أمر لاحق مرة أخرى. ومع --json يكون في JSON على stdout، بينما يذهب التذكير بحفظه إلى stderr، فيظل الناتج قابلًا للتحليل. ويرسل webhooks test حدث email.sent اصطناعيًا موقّعًا أيًّا كان ما تشترك فيه نقطة النهاية، ولا يُرسَل أي بريد.

الطرفية
openemail verifyopenemail webhooks create --url https://hooks.acme.com/openemail \  --event-types email.received,email.bounced,email.complained \  --description 'Support desk sync' --json > endpoint.jsonjq -r .secret endpoint.jsonopenemail webhooks test "$(jq -r .id endpoint.json)" --json | jq .deliveryrm endpoint.json

ضع السر في مخزن أسرارك قبل حذف الملف. يخرج test بالرمز 0 حتى حين يفشل خادمك، لذا اقرأ delivery.status: delivered لجواب 2xx وfailed لأي شيء آخر، بما فيه إعادة التوجيه، لأن إعادات التوجيه لا تُتبع أبدًا. وresponseCode بقيمة null يعني أنه لم يصل أي جواب على الإطلاق.

اعثر على التسليمات الفاشلة وأعِد إرسال أحدها

بعد انقطاع من جهتك، اسرد ما فشل عبر كل نقاط النهاية، وتحقّق من أن إعادة الإرسال ستُقبل، وأرسل الحدث من جديد. وتحمل إعادة الإرسال معرّف الحدث نفسه، فالمستقبِل الذي يُسقط المعرّفات التي عالجها بالفعل يعاملها كالحدث الذي يعرفه.

الطرفية
openemail webhooks list-workspace-deliveries --status failed --since 2026-09-26T00:00:00Z --all --ndjson \  | jq -r '[.endpointId, .id, .eventType, (.responseCode // "no answer")] | @tsv'openemail webhooks get-delivery whe_3f9c2a7b1e4d8f60a5c7b92d whd_8c1e4a7f2b9d3e6a0c5f1b28 --json | jq .replayRefusalopenemail webhooks replay-delivery whe_3f9c2a7b1e4d8f60a5c7b92d whd_8c1e4a7f2b9d3e6a0c5f1b28
  • يأخذ --since و--until لحظة بصيغة ISO 8601.
  • الصف الفاشل الذي تحمل nextAttemptAt فيه وقتًا لا تزال أمامه إعادة محاولة تلقائية.
  • تكون replayRefusal بقيمة null حين ستخرج إعادة الإرسال، وإلا فإنها تسمّي سبب رفضها، مثل webhook_disabled ما دامت نقطة النهاية معطّلة.
  • تجري إعادات الإرسال حدثًا واحدًا في كل مرة. ولا يوجد أمر يعيد إرسال كل تسليم فاشل.

رموز التحقق

مع تسجيل الدخول عبر المتصفح، تطلب أربعة من هذه الأوامر رمز تحقق قبل أن تغيّر أي شيء، كما يفعل تطبيق الويب: rules create وrules update وwebhooks create وwebhooks update. ولا يُطلب من مفتاح API أبدًا. وكل أمر آخر في هذه الصفحة يعمل دون رمز، بما فيها عمليات الحذف وwebhooks rotate-secret.

  • في الطرفية، ترسل إليك CLI بالبريد رمزًا من ست خانات، أو تطلب رمزًا من تطبيق المصادقة حين يكون تسجيل الدخول بخطوتين مفعّلًا، ثم تشغّل الأمر مرة واحدة.
  • من دون إشراف، مع --json أو --no-input، أو في CI، أو دون طرفية، لا أحد يستطيع كتابة الرمز، لذا يتوقف الأمر برمز الخروج 4 ولا يغيّر شيئًا. شغّل openemail verify أولًا، فلا يحتاج الملف الشخصي إلى رمز مدة 60 دقيقة.
  • --yes يؤكد الحذف، لكنه لا يتخطى الرمز أبدًا.

التأكيدات والتشغيل التجريبي

سبعة أوامر هنا تزيل شيئًا أو تكتب فوقه، لذا تطلب منك التأكيد أولًا: templates delete وtemplates delete-version وtemplates replace-content وtemplates restore-version وrules delete وwebhooks delete وwebhooks rotate-secret. ومن دون إشراف يتوقف كل منها برمز الخروج 2 ما لم تمرّر --yes.

الطرفية
$ openemail webhooks delete whe_3f9c2a7b1e4d8f60a5c7b92d --no-input✗ Refusing to run unattended. Pass --yes to confirm.$ openemail webhooks delete whe_3f9c2a7b1e4d8f60a5c7b92d --yes

يطبع --dry-run أول طلب كان سيغيّر شيئًا ويخرج بالرمز 0، دون إرساله أو طلب التأكيد منك. ومع --json يطبع مستند { dryRun, request } واحدًا. أما rules test وtemplates render وtemplates preview فلا تغيّر شيئًا، لكنها طلبات POST، لذا يطبعها التشغيل التجريبي بدلًا من تشغيلها.

التصفح

  • تقرأ templates list وtemplates list-versions وrules list وrules list-runs وكل أمر webhooks list… صفحة واحدة في كل مرة، 25 صفًا ما لم يطلب --limit حتى 100. وتعرض الطرفية قيمة --cursor التي تمرّرها للصفحة التالية.
  • يقرأ --all كل صفحة، ويتوقف --max <n> بعد هذا العدد من الصفوف، ويطبع --ndjson كائن JSON واحدًا في كل سطر. ومع --json تطبع القائمة مستند { items, hasMore, nextCursor } واحدًا، ومع --all كذلك.
  • أعِد المؤشر مع المرشّحات والترتيب نفسها التي جاء بها. وأي شيء آخر يُرفض بوصفه invalid_cursor، برمز الخروج 7.
  • أما templates list-sends فيتصفح بالأرقام بدلًا من ذلك، مع --page و--page-size، ويبلّغ عن total، ولا يملك --all. وأرقام الصفحات تنزاح أثناء خروج البريد، لذا ضيّق الفترة بـ --days أو --minutes بدلًا من التعمق في الصفحات.
  • يعيد templates list-starters وtemplates list-fonts الفهرس كله دفعة واحدة، ويعيد rules reorder كل قاعدة في قائمة عادية بترتيبها الجديد.
  • يضم صندوق البريد 100 قاعدة على الأكثر، لذا يعيد rules list --limit 100 كل قاعدة في صفحة واحدة دائمًا.

خيارات تستحق نظرة ثانية

  • --template-version هو حقل الجسم version، أُعيدت تسميته لأن --version يطبع إصدار CLI. والوسيط <version> في get-version وrestore-version وdelete-version رقم إصدار، لا معرّف tplv_.
  • تأخذ --conditions و--actions و--document و--slots و--props وبقية خيارات JSON قيمة JSON مضمّنة، أو من ملف عبر @path، أو من stdin عبر -. ويأخذ --data الجسم كله بالطريقة نفسها، وأي خيار تمرّره إلى جانبه يتجاوز مفتاحه.
  • يأخذ --html الترميز نفسه، لا ملفًا، لذا يرسل --html @page.html النص @page.html. مرّر --html "$(cat page.html)"، أو ضع html في الملف الذي تعطيه لـ --data.
  • يستبدل rules update --conditions و--actions القائمة كلها، وكذلك webhooks update --event-types و--address-allowlist و--domain-allowlist. اقرأ القيمة الحالية، وغيّرها، وأرسلها كلها.
  • القيمة الفارغة لـ --event-types خطأ استخدام. ولإعادة نقطة نهاية إلى المجموعة الافتراضية، أرسل --data '{"eventTypes":[]}'، ولإيقاف تسليماتها مرّر --no-enabled.
  • يأخذ --expected-version في templates update وreplace-content وrestore-version الإصدار الرئيسي الذي قرأته. وحين يكون شخص آخر قد حرّك الإصدار الرئيسي منذ ذلك الحين، يتوقف الأمر برمز الخروج 6 وversion_conflict، ولا يكتب شيئًا.
  • يوقف rules update <id> --no-enabled قاعدة ويحتفظ بمكانها في الترتيب، وهذه طريقة إيقاف قاعدة مؤقتًا دون حذفها.

صندوق بريدك،
بشروطك أنت.

بنية بريد إلكتروني للشركات والذكاء الاصطناعي والوكلاء والبريد الشخصي. مبنية للتوسّع والخصوصية والتحكّم. كل ما كان ينبغي للبريد الإلكتروني أن يملكه منذ اليوم الأول.

OpenEmail

بنية بريد إلكتروني للشركات والذكاء الاصطناعي والوكلاء والبريد الشخصي. مبنية للتوسّع والخصوصية والتحكّم. كل ما كان ينبغي للبريد الإلكتروني أن يملكه منذ اليوم الأول.

© 2026 OpenEmail. جميع الحقوق محفوظة.