MailLandDocs

Scheduled Emails

Schedule emails for future delivery with timezone support

Schedule an Email

POST https://api.mail.land/v1/transactional/scheduled
Schedule an email
curl -X POST https://api.mail.land/v1/transactional/scheduled \
  -H "X-API-Key: ml_pet_Ab3Kx9mN2pQ7rT4wL8yD..." \
  -H "Content-Type: application/json" \
  -d '{
    "from": {
      "email": "[email protected]",
      "name": "Your App"
    },
    "to": {
      "email": "[email protected]",
      "name": "Jane Doe"
    },
    "subject": "Your trial expires tomorrow",
    "html": "<h1>Hi Jane,</h1><p>Your free trial expires tomorrow. Upgrade now to keep access.</p>",
    "text": "Hi Jane, Your free trial expires tomorrow. Upgrade now to keep access.",
    "scheduledAt": "2026-02-18T09:00:00Z",
    "timeZone": "America/New_York",
    "tags": ["trial-expiry", "reminder"]
  }'

Request Fields

FieldTypeRequiredDescription
fromobjectYesSender address (email and optional name)
toobjectYesSingle recipient (email and optional name)
subjectstringYesEmail subject line
htmlstringConditionalHTML content (one of html or text required)
textstringConditionalPlain text content
replyTostringNoReply-to address
ccarrayNoCC recipients (email and optional name)
bccarrayNoBCC recipients (email and optional name)
attachmentsarrayNoFile attachments (same format as single send)
headersobjectNoCustom email headers
tagsarrayNoTags for analytics
scheduledAtdatetimeYesWhen to send. Always interpreted as UTC: send it with a Z or an explicit offset
timeZonestringNoIANA timezone (e.g., America/New_York). Stored and echoed back for display only: it does not affect when the email is sent

scheduledAt must be in the future and no more than 7 days ahead. Setting timeZone does not convert scheduledAt into that zone: the timestamp is always read as UTC. A past time is rejected with 400 VALIDATION_FAILED; more than 7 days ahead is rejected with 400 INVALID_REQUEST ("Cannot schedule email more than 7 days in advance"). The same 7-day window applies whether the schedule is created here, via the send endpoint, or via reschedule.

Note the field name differences from the send endpoint: scheduled emails use html/text (not htmlContent/textContent) and to is a single object (not an array).

Response

Status: 201 Created

Schedule response
{
  "scheduleId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "status": "pending",
  "scheduledAt": "2026-02-18T09:00:00Z",
  "timeZone": "America/New_York",
  "to": "[email protected]",
  "recipientCount": 1,
  "subject": "Your trial expires tomorrow",
  "createdAt": "2026-02-17T10:00:00Z",
  "processedAt": null,
  "messageIds": [],
  "rejectedRecipients": [],
  "reasonCode": null,
  "error": null
}

Scheduled Email Statuses

StatusDescription
pendingQueued for future delivery
processingBeing processed for sending
sentSuccessfully sent
failedSending failed (check error field)
cancelledCancelled before sending

List Scheduled Emails

GET https://api.mail.land/v1/transactional/scheduled?limit=20
List scheduled emails
curl https://api.mail.land/v1/transactional/scheduled?limit=20 \
  -H "X-API-Key: ml_pet_Ab3Kx9mN2pQ7rT4wL8yD..."

Filter by status:

GET https://api.mail.land/v1/transactional/scheduled?status=pending
List scheduled emails by status
curl "https://api.mail.land/v1/transactional/scheduled?status=pending" \
  -H "X-API-Key: ml_pet_Ab3Kx9mN2pQ7rT4wL8yD..."
List response
{
  "items": [
    {
      "scheduleId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
      "status": "pending",
      "scheduledAt": "2026-02-18T09:00:00Z",
      "timeZone": "America/New_York",
      "to": "[email protected]",
      "recipientCount": 1,
      "subject": "Your trial expires tomorrow",
      "createdAt": "2026-02-17T10:00:00Z",
      "processedAt": null,
      "messageIds": [],
      "rejectedRecipients": [],
      "reasonCode": null,
      "error": null
    }
  ],
  "nextCursor": "eyJ2IjoxLCJzIjoiY3JlYXRlZGF0OmRlc2MiLCJrIjoi...",
  "hasMore": true
}

Query parameters:

ParameterTypeDefaultDescription
cursorstring-Opaque cursor from the previous page's nextCursor. Omit for the first page.
limitint20Items per page (max 100)
statusstring-Filter by status: pending, processing, sent, failed, cancelled

Get a Scheduled Email

GET https://api.mail.land/v1/transactional/scheduled/{scheduleId}
Get a scheduled email
curl https://api.mail.land/v1/transactional/scheduled/f47ac10b-58cc-4372-a567-0e02b2c3d479 \
  -H "X-API-Key: ml_pet_Ab3Kx9mN2pQ7rT4wL8yD..."
Detail response
{
  "scheduleId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "status": "sent",
  "scheduledAt": "2026-02-18T09:00:00Z",
  "timeZone": "America/New_York",
  "to": "[email protected]",
  "recipientCount": 1,
  "subject": "Your trial expires tomorrow",
  "createdAt": "2026-02-17T10:00:00Z",
  "processedAt": "2026-02-18T09:00:02Z",
  "messageId": "t_a1b2c3d4e5f6...",
  "messageIds": ["t_a1b2c3d4e5f6..."],
  "rejectedRecipients": [],
  "reasonCode": null,
  "error": null
}

After sending, the response includes processedAt (when it was sent), messageId (legacy single-message field), messageIds and rejectedRecipients (the full record of what was delivered or dropped), and reasonCode/error when the schedule failed instead.

Cancel a Scheduled Email

DELETE https://api.mail.land/v1/transactional/scheduled/{scheduleId}
Cancel a scheduled email
curl -X DELETE https://api.mail.land/v1/transactional/scheduled/f47ac10b-58cc-4372-a567-0e02b2c3d479 \
  -H "X-API-Key: ml_pet_Ab3Kx9mN2pQ7rT4wL8yD..."

Returns 204 No Content on success. You can only cancel emails with pending status. Attempting to cancel a processing, sent, or failed email returns 400 Bad Request.

Reschedule an Email

Change the delivery time of a pending email:

PATCH https://api.mail.land/v1/transactional/scheduled/{scheduleId}
Reschedule an email
curl -X PATCH https://api.mail.land/v1/transactional/scheduled/f47ac10b-58cc-4372-a567-0e02b2c3d479 \
  -H "X-API-Key: ml_pet_Ab3Kx9mN2pQ7rT4wL8yD..." \
  -H "Content-Type: application/json" \
  -d '{
    "scheduledAt": "2026-02-19T14:00:00Z",
    "timeZone": "Europe/London"
  }'
Reschedule response
{
  "scheduleId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "status": "pending",
  "scheduledAt": "2026-02-19T14:00:00Z",
  "timeZone": "Europe/London",
  "to": "[email protected]",
  "recipientCount": 1,
  "subject": "Your trial expires tomorrow",
  "createdAt": "2026-02-17T10:00:00Z",
  "processedAt": null,
  "messageIds": [],
  "rejectedRecipients": [],
  "reasonCode": null,
  "error": null
}

The same scheduling constraints apply: the new time must be in the future and no more than 7 days from now, or the request is rejected with 400 INVALID_REQUEST.

Timezone Handling

scheduledAt is always interpreted as UTC, whether or not you provide a timeZone: send it with a Z or an explicit offset. timeZone is stored and echoed back in responses for display purposes only; MailLand does not convert scheduledAt into that zone. Use IANA timezone identifiers if you want it recorded for display:

  • America/New_York
  • Europe/London
  • Asia/Tokyo
  • Australia/Sydney

Scheduling via the Send Endpoint

You can also schedule emails directly through the regular send endpoint by including the scheduledAt field:

Schedule via send endpoint
{
  "from": { "email": "[email protected]" },
  "to": [{ "email": "[email protected]" }],
  "subject": "Scheduled email",
  "htmlContent": "<p>This will be sent later.</p>",
  "scheduledAt": "2026-02-18T09:00:00Z"
}

The 202 response from the send endpoint includes scheduleId and scheduledAt. That schedule is the same kind of row this page's endpoints operate on: it can be listed, fetched, cancelled and rescheduled with the endpoints described above, exactly like a schedule created directly through /v1/transactional/scheduled. The 7-day window (see the callout above) applies here too: scheduledAt more than 7 days ahead is rejected with 400 INVALID_REQUEST.

Next Steps

On this page