DEV Community

Sonam
Sonam

Posted on

Reschedule a Scheduled Email Without Recreating It in Python

Scheduling an email is straightforward until the event behind it changes.

An appointment moves. A webinar starts later. A delivery window slips. The email that was correct when you created it is now scheduled for the wrong time.

A common workaround is to cancel the original message and create another one. That means a new message ID, another pass through your send logic, and an awkward failure window where you could leave both messages scheduled or neither one.

The email-schedule-rescheduler Python example uses the Telnyx Email API to update the existing scheduled message instead.

The complete flow is:

Create a scheduled email
        ↓
Reschedule the same message
        ↓
Verify its new delivery time
        ↓
Reject an invalid past timestamp
        ↓
Cancel the schedule for cleanup
Enter fullscreen mode Exit fullscreen mode

Schedule the original email

Create the message with POST /v2/email_messages and provide a future ISO 8601 timestamp in scheduled_at:

import os
from datetime import datetime, timedelta, timezone

import requests

API_BASE = "https://api.telnyx.com/v2"
HEADERS = {
    "Authorization": f"Bearer {os.environ['TELNYX_API_KEY']}",
    "Content-Type": "application/json",
}

def iso_utc(value: datetime) -> str:
    return value.isoformat().replace("+00:00", "Z")

scheduled_at = datetime.now(timezone.utc) + timedelta(hours=2)

response = requests.post(
    f"{API_BASE}/email_messages",
    headers=HEADERS,
    json={
        "from": os.environ["TELNYX_EMAIL_FROM"],
        "to": [os.environ["TELNYX_EMAIL_TO"]],
        "subject": "Appointment reminder",
        "text_body": "Your appointment is coming up.",
        "scheduled_at": iso_utc(scheduled_at),
    },
)
response.raise_for_status()
message_id = response.json()["data"]["id"]
Enter fullscreen mode Exit fullscreen mode

The API returns 202 Accepted with the message in the scheduled state. The message has an ID, but delivery waits until scheduled_at.

Move the delivery time with one PATCH

When the underlying appointment changes, update the message's schedule:

new_time = datetime.now(timezone.utc) + timedelta(hours=4)

response = requests.patch(
    f"{API_BASE}/email_messages/{message_id}/schedule",
    headers=HEADERS,
    json={"scheduled_at": iso_utc(new_time)},
)
response.raise_for_status()
Enter fullscreen mode Exit fullscreen mode

The Email API returns 200 when the reschedule succeeds. Only the delivery time changes. The message ID, content, recipients, tags, and metadata remain attached to the same resource.

That makes the operation easier to reason about than cancel-and-recreate, especially if your system associates audit records or delivery processing with the original message ID.

Do not silently convert mistakes into immediate sends

The example intentionally tries to move the message into the past:

past_time = datetime.now(timezone.utc) - timedelta(hours=1)

response = requests.patch(
    f"{API_BASE}/email_messages/{message_id}/schedule",
    headers=HEADERS,
    json={"scheduled_at": iso_utc(past_time)},
)

assert response.status_code == 422
Enter fullscreen mode Exit fullscreen mode

The API rejects missing, invalid, or non-future scheduled_at values with 422. It does not reinterpret the request as "send immediately."

That distinction gives your application control over what happens next. It can ask for a corrected time, notify an operator, or cancel the message instead of unexpectedly delivering stale information.

Verify the stored schedule

The reschedule response contains the updated message representation. The sample also retrieves the message to verify the stored value:

response = requests.get(
    f"{API_BASE}/email_messages/{message_id}",
    headers=HEADERS,
)
response.raise_for_status()

stored_time = response.json()["data"]["scheduled_at"]
print(stored_time)
Enter fullscreen mode Exit fullscreen mode

There is no separate rescheduled event. The updated scheduled_at field in the response is the confirmation, so checking the resource is a useful way to make the state visible in your workflow.

Clean up the demo

Cancel the schedule before the email is sent:

response = requests.delete(
    f"{API_BASE}/email_messages/{message_id}/schedule",
    headers=HEADERS,
)
response.raise_for_status()
Enter fullscreen mode Exit fullscreen mode

A successful cancellation returns 200 with the message in the cancelled state.

Run it safely in demo mode

The repository example defaults to DEMO_MODE=true. It prints the request flow without calling the live API, which lets you inspect the behavior before supplying credentials or sending an email.

git clone https://github.com/team-telnyx/telnyx-code-examples.git
cd telnyx-code-examples/email-schedule-rescheduler

cp .env.example .env
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
python app.py
Enter fullscreen mode Exit fullscreen mode

For a live run, configure:

TELNYX_API_KEY=<your API key>
TELNYX_EMAIL_FROM=<verified sender>
TELNYX_EMAIL_TO=<recipient>
DEMO_MODE=false
Enter fullscreen mode Exit fullscreen mode

The sample uses the Telnyx Python SDK where the operation is available and direct HTTP for the schedule PATCH. That also makes the exact request shape easy to adapt in another language.

Where this pattern helps

Rescheduling the same message is useful for:

  • appointment and patient reminders
  • webinar and event updates
  • payment reminders
  • delivery-window notifications
  • reservation changes
  • time-sensitive internal alerts

The reusable idea is simple: keep one message resource, mutate its future delivery time, verify the new state, and fail explicitly when the requested schedule is invalid.

Resources

Top comments (0)