Skip to content
AI Inbx
Esc
navigateopen⌘Jpreview
On this page

Scheduling

Defer a send up to 30 days, move it, or cancel it — and what stays mutable until the moment it goes out.

Pass scheduled_at on a send or a reply and the message is held until then. The API returns it immediately with status: "scheduled", and it stays yours to move or cancel right up until it leaves.

const email = await aiinbx.emails.send({
  from: "Ada <ada@example.com>",
  to: "grace@example.com",
  subject: "Monday reminder",
  text: "The review is at 10.",
  scheduled_at: "2026-09-14T09:00:00Z",
})

console.log(email.status) // "scheduled"
email = client.emails.send(
    {
        "from_": "Ada <ada@example.com>",
        "to": ["grace@example.com"],
        "subject": "Monday reminder",
        "text": "The review is at 10.",
        "scheduled_at": "2026-09-14T09:00:00Z",
    }
)
curl https://api.aiinbx.com/api/v2/emails \
  -H "Authorization: Bearer $AI_INBX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "Ada <ada@example.com>",
    "to": "grace@example.com",
    "subject": "Monday reminder",
    "text": "The review is at 10.",
    "scheduled_at": "2026-09-14T09:00:00Z"
  }'

scheduled_at is an RFC 3339 timestamp with an offset (2026-09-14T09:00:00Z or 2026-09-14T11:00:00+02:00), at most 30 days out. A naive timestamp is rejected with invalid_request — the offset is required precisely because “9am” without one means six different instants.

Rescheduling

PATCH /emails/{id} moves a scheduled message. The same 30-day bound applies from the moment of the call.

const moved = await aiinbx.emails.reschedule("eml_...", {
  scheduled_at: "2026-09-15T09:00:00Z",
})
moved = client.emails.reschedule("eml_...", scheduled_at="2026-09-15T09:00:00Z")
curl -X PATCH https://api.aiinbx.com/api/v2/emails/eml_... \
  -H "Authorization: Bearer $AI_INBX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "scheduled_at": "2026-09-15T09:00:00Z" }'

Only a message still in scheduled status can move. Once it has begun sending, the call fails with 409 not_scheduled.

Canceling

const canceled = await aiinbx.emails.cancel("eml_...")
console.log(canceled.status) // "canceled"
canceled = client.emails.cancel("eml_...")
curl -X POST https://api.aiinbx.com/api/v2/emails/eml_.../cancel \
  -H "Authorization: Bearer $AI_INBX_API_KEY"

Cancellation is terminal — a canceled message can’t be rescheduled back into flight. Send a new one instead.

Both rescheduling and cancellation need a key with the sending or full scope.

Finding what’s scheduled

status: "scheduled" on the email list is the queue of everything still pending:

for await (const email of aiinbx.emails.list({ status: "scheduled" })) {
  console.log(email.scheduled_at, email.subject, email.to)
}
for email in client.emails.iter(status="scheduled"):
    print(email["scheduled_at"], email["subject"], email["to"])
curl "https://api.aiinbx.com/api/v2/emails?status=scheduled&limit=100" \
  -H "Authorization: Bearer $AI_INBX_API_KEY"

Composed at send time, not at schedule time

A scheduled message is stored as an intent and composed at the instant it goes out. Two consequences worth designing around:

  • Suppressions are applied then, not now. A recipient who unsubscribes between scheduling and sending is dropped, and shows up in the delivery record rather than in the response you already received.
  • Pacing rules are evaluated then, too. A message scheduled for 3am under an hours rule that forbids 3am is held until the window opens; scheduled_at sets the earliest time it may go, not a guarantee of the exact instant.

If you need a message to ignore the pacing rules entirely, set pacing.skip on it:

{
  "scheduled_at": "2026-09-14T09:00:00Z",
  "pacing": { "skip": true }
}

Scheduling versus pacing

They solve different problems and compose fine:

Scheduling Pacing
Set on One message Workspace-wide rules
Answers “Not before this instant” “Not faster than this, and not outside these hours”
Changed by reschedule / cancel Rule edits, or releasing from the queue

Use scheduled_at for “send this Monday morning”. Use pacing for “never more than 200 an hour from this domain”.

Last updated on September 9, 2026

Was this page helpful?