فهرست نقشها
همهٔ نقشهای فضای کاری، نخست نقشهای داخلی، همراه با تعداد افراد و کلیدهایی که هر کدام را دارند.
فراخوانی واقعی را با کلید خودتان روی فضای کاری شما اجرا میکند.
GET /roles
همهٔ نقشهای فضای کاری، نخست نقشهای داخلی، همراه با تعداد افراد و کلیدهایی که هر کدام را دارند.
دو محور، و یک پرسش نیستند
export OE=https://api.openemail.ukexport AUTH="Authorization: Bearer $OPENEMAIL_API_KEY"یک نقش میگوید کسی در این فضای کاری چه کاری میتواند بکند: خواندن نامه، فرستادنش، ویرایش قالبها، افزودن دامنه. یک دسترسی اعطاشده میگوید آن کار را روی کدام نشانیها میتواند بکند، و همسایهٔ دیوار به دیوار روی /members/{userId}/addresses است، به صورت member (نشانی را میخواند و با آن ارسال میکند) یا viewer (تنها میخواندش). پیش از بیرونرفتن یک پیام هر دو باید موافق باشند: نقشی که emails:send دارد و هیچ دسترسی اعطاشدهای ندارد از هیچجا میتواند ارسال کند، و همهٔ نشانیهای فضای کاری زیر یک دسترسی viewer هم از هیچجا میتواند ارسال کند.
هر فضای کاری با همان شش نقش کاشته میشود. Owner، Admin، Member و Viewer یک نردبان میسازند. هر کدام هر چه را نفر بعدی دارد دارد، پس تنزلدادن کسی دسترسیاش را تنگتر میکند نه اینکه آن را با برشی متفاوت عوض کند. Developer و Billing پلههای آن نردبان نیستند: Developer یکپارچهسازی میسازد (کلیدها، وبهوکها، قالبها، ارسال) و هیچیک از نامههای فضای کاری را نمیخواند، و Billing پلن و فاکتورها را میبیند و نه چیز دیگر. هر دو کاملاً درون Admin جا میگیرند. آنها در نخستین خواندن کاشته میشوند نه هنگام ساخت فضای کاری، پس فضای کاریای که پیش از وجود این قابلیت ساخته شده همان لحظهای که چیزی بخواهدشان آنها را میرویاند. builtin نام میبرد که یک سطر از کدام seed آمده، و همهٔ آنچه نام میبرد همین است: این ششتا نقطهٔ شروعیاند که قرار است فضای کاری شکلشان دهد، و هر کدام جز Owner را میتوان تغییر نام داد، مجوزهایش را عوض کرد و حذفش کرد. به جای نام، بر اساس editable و deletable تصمیم بگیرید: نقشی که کسی نامش را عوض کرده باز هم به این دو درست پاسخ میدهد، و نامش دیگر چیزی به شما نمیگوید.
Owner تنها استثناست، و از هر جهت استثناست: editable: false، deletable: false، و به عنوان مقصد روی PATCH /members/{userId} رد میشود. حسابی را توصیف میکند که فضای کاری بر پایهاش کلید خورده و همهٔ مجوزها را دارد، از جمله مجوزهایی که در نسخهای بعدی افزوده میشوند، و به همین دلیل فهرستش محاسبه میشود نه ذخیره. مالککردن کسی دیگر انتقال فضای کاری است؛ اینجا اندپوینتی نیست که چنین کاری بکند.
پنج نقش دیگر همهچیز را میپذیرند: فهرست مجوز تازه، توضیح تازه، نام تازه، یک DELETE. آنها پیشفرضهای کاشتهشدهاند نه چیزهای ثابت: فضای کاریای که هرگز یکپارچهسازی نمیسازد باید بتواند از شر Developer خلاص شود، و جایی که «Member» معنای تنگتری دارد باید بتواند آن را به زبان خودش بگوید. تنها owner رد میکند، و همه را زیر یک کد رد میکند: role_immutable، یک 409 که param: "roleId" را با خود دارد، چه PATCH نامی داشته باشد چه فهرست مجوزی. دیگر هیچ تغییر نامی بهتنهایی رد نمیشود، پس تغییرناپذیریای با param: "name" برای مدیریت نمانده؛ تنها 409ی که یک نام هنوز میتواند برانگیزد role_name_taken است، وقتی نقشی دیگر روی فضای کاری همان نام را دارد.
فراتر از آن ششتا، هر فضای کاری تا ۲۴ نقش از خودش مینویسد. سقف تنها همانها را میشمارد، پس حذف یک نقش کاشتهشده زیر آن سقف جایی نمیخرد. مجوزها هنگام ورود بسط مییابند نه تحتاللفظی گرفته میشوند (templates:write بهتنهایی به صورت templates:read و templates:write ذخیره میشود)، پس فهرست را از روی پاسخ بخوانید نه اینکه فرض کنید همان است که فرستادهاید.
نقش سقف یک کلید API هم هست. کلیدی که در برابر یک نقش صادر شده key.scopes ∩ role.permissions را میتواند و نه بیشتر، که در مرز و به ازای هر درخواست حل میشود، پس ویرایش یک نقش همان فراخوانی بعدیِ کلیدهایش کارهایی را که میتوانند بکنند تغییر میدهد، و کلیدی که نقش ندارد اصلاً سقفی ندارد. همهٔ این ماجرا در صفحهٔ اسکوپها آمده است.
نمونه
نیازمند roles:read است. بدون cursor. پاکت hasMore و nextCursor را با خود دارد تا کلاینت بتواند آن را به همان کد فهرستی بدهد که برای هر مجموعهٔ دیگری دارد، و هرگز صفحهٔ دومی نیست.
curl "$OE/roles" -H "$AUTH"{ "object": "list", "data": [ { "object": "role", "id": "role_1c94e05d3862c1f0a44b7f3a", "name": "Owner", "description": "The person the workspace belongs to. Holds everything, including additions.", "permissions": ["emails:send", "emails:read", "…", "workspace:manage"], "builtin": "owner", "editable": false, "deletable": false, "members": 0, "apiKeys": 2, "createdAt": "2026-08-01T09:00:00.000Z", "updatedAt": "2026-08-01T09:00:00.000Z" }, { "object": "role", "id": "role_c40a95f21cc65d31c2a89e07", "name": "Viewer", "description": "Reads the mail on the addresses they hold, and changes nothing.", "permissions": [ "emails:read", "drafts:read", "threads:read", "labels:read", "contacts:read", "calendar:read", "templates:read", "rules:read", "connections:read", "settings:read" ], "builtin": "viewer", "editable": true, "deletable": true, "members": 3, "apiKeys": 1, "createdAt": "2026-08-01T09:00:00.000Z", "updatedAt": "2026-08-01T09:00:00.000Z" } ], "hasMore": false, "nextCursor": null}مرتبشده بر اساس رتبهٔ داخلی و سپس نام (owner، admin، member، viewer، developer، billing، و بعد بقیه به ترتیب الفبا) نه جدیدتریناول مثل بقیهٔ API. ماتریس مجوزها مثل یک نردبان خوانده میشود، و مرتبکردنش بر اساس createdAt هر هفته گستردهترین نقش را در سطری دیگر میگذارد.
خواندن همین فهرست است که آن شش نقش را روی فضای کاریای که هرگز نقشی نداشته میکارد. کاشتن روی یک ایندکس یکتا تعارض میگیرد و بار دوم کاری نمیکند، پس فراخوانی idempotent است و تنها اولی مینویسد، و همین است که باعث میشود POST /members همیشه بتواند roleIdای نام ببرد که وجود دارد.
تنها یک بار میکارد. فضای کاری ثبت میکند که کاشته شده، پس این خواندن فضای کاریای را که از خود قابلیت قدیمیتر است پر میکند و دیگر هرگز نمینویسد، و همین است که حذف یک نقش کاشتهشده را دائمی میکند. ساختی پیشین در هر خواندن هر سطر الگویی را که نبود دوباره درج میکرد، پس Billingِ حذفشده در بارگذاری بعدی صفحه با یک id تازه برمیگشت؛ دیگر چنین نیست.
members و apiKeys همان چیزهاییاند که پیش از رفتن نقش باید جابهجا شوند، و همین است که به کلاینت اجازه میدهد پیش از پیشنهاد حذف هشدار بدهد نه پس از 409. سطر owner معمولاً members: 0 میخواند: مالک عضو فضای کاری خودش نیست، او حسابی است که فضای کاری بر پایهاش کلید خورده.
دقیقاً برای اینکه این بتواند یک پاسخ باشد، سقف سختی از ۲۴ نقش سفارشی هست. فضای کاریای با چهل نقش نمیتواند با نگاهکردن پاسخ دهد که «چه کسی میتواند با billing@ ارسال کند»، و این تنها پرسشی است که این قابلیت برای پاسخپذیرکردنش وجود دارد.