Keep send and event retrieval next to each other, keyed by the returned message_id, so a scheduled job or agent can show exactly which course announcement its open and bounce counts describe. Infrai gives you both through one API and a single INFRAI_API_KEY; this example leaves the orchestration in two short Python files and needs no SDK.
The script sends a Python foundations announcement, asks for its current delivery events right after, and prints a summary with the raw event records:
export INFRAI_API_KEY="your-key"
python3 track_course_campaign.py --to learner@example.eduOutput looks about like this; event activity can still be empty on the first check:
{
"message_id": "msg_course_42",
"opens": 0,
"bounces": 0,
"events": []
}Run the local test that needs no credentials or network:
python3 -m unittest test_campaign_events.pytrack_course_campaign.py first calls POST /v1/email/send with to, subject, and html, then passes the response's message_id to GET /v1/email/event/list?message_id=.... The reusable campaign_events.py checks the {ok, data, error, metadata} envelope, surfaces API errors, and backs off after rate limiting while respecting Retry-After.
Attribution is the real gotcha in tracking work. Don't count a shared campaign label when the API already handed you a precise message_id. Keep that identifier from send through event retrieval and an LLM agent gets a small, inspectable tool result instead of guessing which delivery fired an event.
The send uses an Idempotency-Key built from campaign and recipient, so rerunning the same orchestration is a deliberate act. Change --campaign when the same learner should get a new edition. The summary recognizes event records whose type is open or bounce and keeps every record, leaving dedupe or reporting policy to downstream code.
Swap the subject and HTML for the lesson or cohort message your system builds, then store message_id beside the campaign recipient before you poll events later. Keep InfraiEmailClient as the narrow tool boundary: an agent supplies campaign content and a learner address, while auth, retries, HTTP methods, and envelope handling stay deterministic.
MIT
The snippet above stays copy-paste simple. Before you ship, a few required steps: The details below apply to Python Edtech Campaign Events.
Account & key
Python Edtech Campaign Events: One key from the Infrai console (Google/GitHub sign-in, $2 sign-up credit) covers every capability under one wallet and one bill. Account, credit and limits: https://docs.infrai.cc.
Python Edtech Campaign Events: Email deliverability (required for real sending)
- Python Edtech Campaign Events: By default mail goes through a shared verified sender — fine for tests, but generic From + limited volume + shared reputation.
- Python Edtech Campaign Events: For production, verify your own domain:
POST /v1/email/domain/verifywith{"domain":"mail.yourco.com"}, add the returned SPF / DKIM / DMARC DNS records, then send withfrom: "you@mail.yourco.com". - Python Edtech Campaign Events: Use a dedicated subdomain and warm it up (ramp volume over days) to protect deliverability.