REST API
ارسال ایمیل با استفاده از REST API سرویس Mail.Land به همراه مرجع کامل درخواست و پاسخ
ایمیلهای تراکنشی را از طریق POST /v1/transactional/send ارسال کنید. این روش اصلی ارسال ایمیل از طریق MailLand است.
احراز هویت
کلید API خود را در هدر X-API-Key قرار دهید:
X-API-Key: ml_pet_Ab3Kx9mN2pQ7rT4wL8yD...اعتبارنامه باید کانال Api فعال داشته باشد و از نوع اعتبارنامه محصول Sending باشد.
ارسال یک ایمیل
POST https://api.mail.land/v1/transactional/sendcurl -X POST https://api.mail.land/v1/transactional/send \
-H "X-API-Key: ml_pet_Ab3Kx9mN2pQ7rT4wL8yD..." \
-H "Content-Type: application/json" \
-d '{
"from": {"email": "[email protected]"},
"to": [{"email": "[email protected]"}],
"subject": "Hello from MailLand",
"htmlContent": "<p>Your email content here</p>"
}'بدنه درخواست
{
"from": {
"email": "[email protected]",
"name": "Your App"
},
"to": [
{
"email": "[email protected]",
"name": "Jane Doe",
"variables": {
"firstName": "Jane",
"orderNumber": "ORD-12345"
}
}
],
"subject": "Your order #{{orderNumber}} has shipped",
"htmlContent": "<h1>Hi {{firstName}},</h1><p>Your order #{{orderNumber}} is on its way.</p>",
"textContent": "Hi {{firstName}}, Your order #{{orderNumber}} is on its way.",
"replyTo": "[email protected]",
"tags": ["order-shipped", "transactional"],
"customHeaders": {
"X-Entity-Ref-ID": "order-12345"
},
"tracking": {
"opens": true,
"clicks": true
},
"priority": "High",
"idempotencyKey": "order-12345-shipped-v1"
}مرجع فیلدها
| فیلد | نوع | الزامی | توضیحات |
|---|---|---|---|
from | object | بله | آدرس فرستنده |
from.email | string | بله | ایمیل فرستنده (باید روی دامنه تایید شده باشد) |
from.name | string | خیر | نام نمایشی |
to | array | بله | لیست گیرندگان |
to[].email | string | بله | آدرس ایمیل گیرنده |
to[].name | string | خیر | نام نمایشی گیرنده |
to[].variables | object | خیر | متغیرهای قالب اختصاصی هر گیرنده |
subject | string | مشروط | الزامی است مگر اینکه از قالب استفاده شود |
templateId | string | خیر | استفاده از قالب ذخیره شده به جای محتوای درونخطی |
htmlContent | string | مشروط | بدنه HTML (اگر templateId نباشد، htmlContent یا textContent الزامی است) |
textContent | string | خیر | متن ساده جایگزین |
variables | object | خیر | متغیرهای سراسری قالب. چه templateId تنظیم شده باشد چه نشده اعمال میشوند؛ در صورت همپوشانی کلید، مقدار to[].variables خودِ گیرنده برنده میشود |
replyTo | string | خیر | آدرس ایمیل پاسخدهی |
tags | array | خیر | برچسبها برای فیلترکردن در آنالیتیکس و وبهوکها |
customHeaders | object | خیر | هدرهای سفارشی ایمیل (جفتهای کلید-مقدار) |
tracking | object | خیر | تنظیمات ردیابی را بازنویسی کنید |
tracking.opens | boolean | خیر | درخواست ردیابی باز شدن برای همین پیام (پیشفرض: در هر دو جریان خاموش). فقط تا وقتی به کار میرود که دامنهٔ فرستنده انتخابی نکرده باشد؛ انتخاب خود دامنه برای هر جریان، در صفحهٔ دامنهها، مقدم است. false همیشه آن را خاموش میکند |
tracking.clicks | boolean | خیر | ردیابی کلیک را فعال کنید (پیشفرض: برای جریان تراکنشی false مگر اینکه حساب شما آن را فعال کند، برای بازاریابی true) |
ipPoolId | string | خیر | برای استخرهای IP اختصاصی رزرو شده و هنوز در دسترس نیست؛ ارسال آن خطای 400 IP_POOL_NOT_FOUND برمیگرداند. آن را حذف کنید. |
scheduledAt | datetime | خیر | زمانبندی برای تحویل آینده (UTC)، حداکثر تا ۷ روز بعد |
idempotencyKey | string | خیر | جلوگیری از ارسال تکراری برای یک کلید یکسان. ترجیحاً از هدر X-Idempotency-Key استفاده کنید |
attachments | array | خیر | پیوستهای فایل (حداکثر ۱۰ عدد و حداکثر ۱۰ مگابایت مجموع اندازهٔ رمزگشاییشده) |
priority | enum | خیر | Low، Normal (پیشفرض)، High یا Critical |
پیوستها
هر پیوست به سه فیلد نیاز دارد:
{
"attachments": [
{
"fileName": "invoice.pdf",
"contentType": "application/pdf",
"content": "JVBERi0xLjQKJ...",
"inline": false
},
{
"fileName": "logo.png",
"contentType": "image/png",
"content": "iVBORw0KGgoAAAA...",
"contentId": "company-logo",
"inline": true
}
]
}| فیلد | نوع | الزامی | توضیحات |
|---|---|---|---|
fileName | string | بله | نام فایل با پسوند |
contentType | string | بله | نوع MIME (مثلاً application/pdf) |
content | string | بله | محتوای فایل با Base64 استاندارد رمزگذاریشده: دارای padding و بدون فاصله یا شکست خط |
contentId | string | خیر | شناسه محتوا برای پیوستهای درونخطی (در HTML با cid:company-logo ارجاع دهید) |
inline | boolean | خیر | آیا این یک تصویر درونخطی است (پیشفرض: false) |
حداکثر ۱۰ پیوست در هر ایمیل و حداکثر ۱۰ مگابایت مجموع اندازهٔ رمزگشاییشده (پس از Base64). نبودِ contentType، Base64 غیراستاندارد، یا عبور از تعداد یا حجم مجاز، خطای 400 VALIDATION_FAILED برمیگرداند. مسیر POST /v1/transactional/send-batch فیلد attachments ندارد.
پاسخ
وضعیت: 202 Accepted
{
"success": true,
"jobId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"messageIds": ["t_a1b2c3d4e5f6..."],
"acceptedRecipients": ["[email protected]"],
"rejectedRecipients": [],
"queuedAt": "2026-02-17T10:30:00Z",
"isSandboxed": false
}| فیلد | نوع | توضیحات |
|---|---|---|
success | boolean | آیا درخواست پذیرفته شد |
jobId | string | شناسه یکتای کار برای ردیابی |
messageIds | array | شناسههای پیام برای هر گیرنده (پیشوند t_ برای تراکنشی، m_ برای بازاریابی) |
acceptedRecipients | array | آدرسهای ایمیل پذیرفتهشده برای تحویل |
rejectedRecipients | array | گیرندگانی که رد شدند |
queuedAt | datetime | زمانی که ایمیل در صف قرار گرفت |
isSandboxed | boolean | اگر درخواست با اعتبارنامهٔ sandbox ارسال شده باشد true است (حالت Sandbox را ببینید) |
sandboxReason | string | credential_sandbox_mode (ثبت شده، تحویل نشده) یا all_recipients_not_in_sandbox_allowlist (هیچ گیرندهای در فهرست مجاز نبود)؛ در غیر این صورت وجود ندارد |
گیرندگان رد شده
گیرندگان میتوانند به چند دلیل رد شوند:
{
"success": true,
"jobId": "...",
"acceptedRecipients": ["[email protected]"],
"rejectedRecipients": [
{
"email": "[email protected]",
"reason": "Suppressed"
},
{
"email": "[email protected]",
"reason": "domain_typo",
"suggestion": "[email protected]"
}
]
}دلایل رایج رد:
Suppressed: آدرس در لیست سرکوب است (بانس یا شکایتهای قبلی)disposable_domain: ارائهدهنده ایمیل یکبارمصرف شناسایی شدdomain_typo: احتمالاً اشتباه تایپی در دامنه (شامل فیلدsuggestion)
بررسی پیش از پرواز
توانایی ارسال خود را قبل از درخواست واقعی تأیید کنید:
GET https://api.mail.land/v1/transactional/send/check?recipientCount=1curl https://api.mail.land/v1/transactional/send/check?recipientCount=1 \
-H "X-API-Key: ml_pet_Ab3Kx9mN2pQ7rT4wL8yD..."{
"canSend": true,
"rateLimitStatus": {
"currentSecond": 2,
"limitPerSecond": 50,
"currentHour": 450,
"limitPerHour": 5000,
"currentDay": 8200,
"limitPerDay": 50000
},
"monthlyUsed": 125000,
"monthlyLimit": 500000
}اگر canSend برابر false باشد، فیلد reason دلیل آن را توضیح میدهد (محدودیت نرخ، سهمیه، وضعیت حساب و غیره).
ایدمپوتنسی (Idempotency)
تلاش دوباره برای ارسال پس از پایان مهلت شبکه نباید ایمیل را دو بار تحویل دهد. با دادن کلید ایدمپوتنسی، تلاش دوباره بهجای ارسال مجدد، پاسخ اصلی را بازپخش میکند.
کلید را بهصورت هدر بفرستید (توصیهشده):
X-Idempotency-Key: order-12345-shipped-v1یا از فیلد idempotencyKey در بدنه استفاده کنید. هر دو پذیرفته میشوند و ارسال همزمانِ هر دو تنها در صورت یکسان بودن مقادیر مجاز است.
قواعد
- دامنه.
X-Idempotency-Keyتنها رویPOST /v1/transactional/sendوPOST /v1/transactional/send-batchپذیرفته میشود و هیچ نقطهٔ پایانی دیگری آن را نمیخواند. - قالب. ۱ تا ۲۵۵ نویسه: حروف، ارقام،
_و-. کلیدها دقیقاً مقایسه میشوند؛ بدون حذف فاصله و بدون یکسانسازی بزرگی و کوچکی حروف. - بازه. هر کلید تا ۲۴ ساعت قابل بازپخش است. پس از آن همان کلید یک درخواست تازه به شمار میرود.
- بازپخش. تلاش دوباره در این بازه، پاسخ اصلی را عیناً برمیگرداند، همان شناسهها و همان وضعیت، و چیزی ارسال نمیکند.
- یکتایی. کلیدها به ازای هر حساب یکتا هستند. استفادهٔ دوباره از یک کلید برای نقطهٔ پایانی دیگر با
409رد میشود؛ برای هر عملیات کلید جداگانه به کار ببرید.
شناسه همبستگی
درخواستها را در سیستمها با افزودن یک شناسه همبستگی ردیابی کنید:
X-Correlation-ID: my-app-order-12345یا
X-Request-ID: my-app-order-12345شناسهٔ همبستگی در لاگها و جریان فعالیت ظاهر میشود.
هدرهای محدودیت نرخ
پاسخهای موفق شامل اطلاعات محدودیت نرخ بهازای هر IP هستند:
X-RateLimit-Limit: 300
X-RateLimit-Remaining: 295هنگامی که موتور سیاست ارسال محدودیت نرخ را اعمال میکند (429)، هدر Retry-After ثانیههای انتظار را نشان میدهد:
Retry-After: 30یک درخواست تراکنشی میتواند تا ۵ گیرنده داشته باشد و هر گیرنده در محدودیتهای نرخ شما شمرده میشود. اگر این تعداد از سهمیهٔ در ثانیهٔ شما بیشتر باشد (برای مثال ۱ در ثانیه در پلنی با ۶۰ در دقیقه)، درخواست وقتی پذیرفته میشود که در همان ثانیه درخواست دیگری پذیرفته نشده باشد؛ در غیر این صورت پاسخ 429 با Retry-After: 1 میگیرد و با تلاش دوباره در ثانیهٔ بعد، در حد محدودیت دقیقهای شما، موفق میشود. به محدودیتهای نرخ مراجعه کنید.
یک 429 در سطح IP همچنین شامل X-RateLimit-Reset بهصورت زمان UTC با قالب ISO-8601 است.
پاسخهای خطا
تمام خطاها یک بدنهٔ JSON برمیگردانند:
{
"code": "ERROR_CODE",
"message": "Human-readable description",
"timestamp": "2026-02-17T10:30:00Z",
"traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
}کدهای خطا
| وضعیت | کد | توضیحات |
|---|---|---|
400 | INVALID_REQUEST | بدنهٔ درخواست نادرست، یا scheduledAt بیش از ۷ روز جلوتر («امکان زمانبندی ایمیل بیش از ۷ روز جلوتر وجود ندارد») |
400 | VALIDATION_FAILED | بدون htmlContent، textContent یا templateId؛ یا یک پیوست اعتبارسنجی را نمیگذراند (به محدودیتهای پیوستها در بالا مراجعه کنید). پاسخ شامل آرایهٔ errors[] با فیلد و قانون ناموفق است. (قبلاً: MISSING_CONTENT: از ۲۰۲۶-۰۴ منسوخ شده.) |
413 | CONTENT_TOO_LARGE | "The message is too large to send: it would exceed the platform's message-size limit. Reduce the HTML, text, attachments or recipients.": حجم پیام از حد مجاز پلتفرم فراتر میرود. POST /v1/transactional/send و send-batch آن را با 413 برمیگردانند؛ چیزی در صف قرار نمیگیرد. پیام را کوچکتر کنید. در درونریزی CSV کد 400 است |
400 | transactional_recipient_limit_exceeded | بیش از ۵ گیرنده روی یک اعتبارنامهٔ تراکنشی (۱۰۰۰ در بازاریابی) |
400 | TEMPLATE_NOT_FOUND | templateId به هیچ قالب فعالی از این حساب اشاره ندارد (برای قالب حذفشده نیز برگردانده میشود) |
400 | IP_POOL_NOT_FOUND | ipPoolId به هیچ استخری که به این حساب اختصاص یافته باشد اشاره ندارد (اکنون همیشه، چون هنوز استخری قابل اختصاص نیست: به مرجع فیلدها در بالا مراجعه کنید) |
422 | TEMPLATE_VARIABLE_MISSING | محتوا یک {{placeholder}} بدون مقدار دارد؛ details.missingVariables نامها را فهرست میکند. چیزی در صف قرار نمیگیرد |
400 | IDEMPOTENCY_KEY_INVALID | کلید خالی، نامعتبر یا بیش از ۲۵۵ نویسه است، یا هدر بیش از یکبار ارسال شده است |
400 | IDEMPOTENCY_KEY_CONFLICT | هدر و فیلد idempotencyKey هر دو ارسال شدهاند و مقادیرشان متفاوت است |
400 | SEND_FAILED | تمام گیرندگان سرکوب شدهاند (پیام: «All recipients are suppressed») |
409 | IDEMPOTENCY_KEY_REUSED | این کلید هماکنون برای نقطهٔ پایانی دیگری در این حساب استفاده میشود |
401 | UNAUTHORIZED | X-API-Key وجود ندارد، نامعتبر است، منقضی شده یا لغو شده است |
403 | ACCOUNT_SUSPENDED | حساب تعلیق شده است |
403 | ACCOUNT_DISABLED | حساب غیرفعال شده است |
403 | ACCOUNT_NOT_ACTIVE | حساب فعال نیست |
403 | NO_ACTIVE_SUBSCRIPTION | حساب اشتراک فعالی ندارد |
403 | SUBSCRIPTION_EXPIRED | اشتراک حساب منقضی شده است |
403 | IP_NOT_ALLOWED | IP درخواست در فهرست مجاز اعتبارنامه نیست |
403 | SENDING_BLOCKED | ارسال برای این حساب مسدود شده است |
403 | ACTION_REQUIRED | پیش از ارسال، اقدامی روی حساب لازم است |
403 | TENANT_APPROVAL_REQUIRED | حساب پیش از ارسال در انتظار بررسی است. تلاش مجدد کمکی نمیکند؛ با پشتیبانی تماس بگیرید |
422 | DOMAIN_NOT_VERIFIED | دامنهٔ فرستنده تأیید نشده است |
422 | DOMAIN_NOT_ALLOWED_FOR_CREDENTIAL | دامنه در محدودهٔ اعتبارنامه نیست |
422 | DOMAIN_NOT_FOUND | دامنهٔ فرستنده یافت نشد |
429 | RATE_LIMIT_EXCEEDED | از محدودیت نرخ تجاوز شده |
429 | QUOTA_EXCEEDED | سهمیهٔ ماهانه تمام شده |
429 | WARMUP_LIMIT_EXCEEDED | سقف روزانهٔ دورهٔ راهاندازی ارسال حساب شما پر شده است؛ در ۰۰:۰۰ UTC بازنشانی میشود |
429 | DAILY_LIMIT_EXCEEDED | به محدودیت روزانهٔ ارسال رسیده |
429 | WARMUP_POOL_BUDGET_EXCEEDED | "Sending is temporarily paused for today. It resumes automatically at 00:00 UTC; no action is needed." / "Sending is briefly delayed. Please retry in a few minutes." |
429 | WARMUP_POOL_EMPTY | "Sending is temporarily paused. It resumes automatically; no action is needed." |
429 | PLATFORM_DAILY_CAPACITY_REACHED | "Platform sending capacity for today is fully booked. This is a temporary platform-wide limit, not your account quota, and it resets at midnight UTC." |
429 | PLATFORM_CAPACITY_PROTECTION | "New sending is briefly paused to protect platform capacity. Mail already accepted is not affected. This is transient and clears on its own; retry shortly." |
429 | PROBATION_CAP_REACHED | "New accounts can send to N recipients per rolling 24 hours until our team has reviewed the account. Capacity returns as earlier sends age out of the 24-hour window; contact support to request a review." |
429 | PAYMENT_DAILY_LIMIT_EXCEEDED | محدودیت روزانه برای حساب دارای پرداخت معوق اعمال شده |
429 | MARKETING_PAUSED | ارسال بازاریابی برای این حساب موقتاً متوقف شده |
429 | SENDING_BLOCKED | سطح تأیید حساب اجازهٔ این ارسال را نمیدهد (ردِّ مربوط به sandbox/سطح تأیید؛ مسدودسازی قطعی ارسال بهجای آن 403 برمیگرداند، به بالا مراجعه کنید) |
503 | SERVICE_UNAVAILABLE | سرویس ایمیل موقتاً در دسترس نیست |
محدودیت گیرندگان تراکنشی
اعتبارنامههای تراکنشی حداکثر ۵ گیرنده در هر درخواست ارسال را اجرا میکنند. این امر از سوءاستفاده از جریانهای تراکنشی برای ارسال انبوه جلوگیری میکند. اگر به گیرندگان بیشتری نیاز دارید، از اعتبارنامه بازاریابی با ارسال دستهای استفاده کنید.
استفاده از قالبها
به جای HTML درونخطی، به یک قالب ذخیرهشده ارجاع دهید:
{
"from": { "email": "[email protected]" },
"to": [
{
"email": "[email protected]",
"variables": {
"firstName": "Jane",
"resetLink": "https://app.example.com/reset?token=abc123"
}
}
],
"templateId": "t8x2m4q9zk7wpr",
"variables": {
"companyName": "Acme Inc"
}
}subject از قالب میآید. در صورت همپوشانی کلید، مقدار to[].variables خودِ گیرنده، variables سراسری را بازنویسی میکند، همان قاعدهٔ محتوای درونخطی. برای ایجاد و مدیریت قالبها به قالبها مراجعه کنید.
مراحل بعدی
- ارسال دستهای: ارسال به چندین گیرنده به یکباره
- رله SMTP: ارسال از طریق SMTP به جای REST
- قالبها: ایجاد قالبهای ایمیل قابل استفاده مجدد
- ایمیلهای زمانبندی شده: زمانبندی ایمیلها برای تحویل آینده