-
Notifications
You must be signed in to change notification settings - Fork 0
Email Sending
All email sending is done through ServerClient. You will need a server API token.
import postmark
client = postmark.ServerClient("your-server-token")Send a single email using either a dict or the Email model.
import asyncio
from postmark import Email
async def main():
# Using a dict
response = await client.outbound.send({
"sender": "you@example.com",
"to": "recipient@example.com",
"subject": "Hello from Postmark",
"text_body": "Hello!",
"html_body": "<p>Hello!</p>",
"message_stream": "outbound",
})
print(f"Message ID: {response.message_id}")
# Using the Email model (recommended)
response = await client.outbound.send(
Email(
sender="you@example.com",
to="recipient@example.com",
subject="Hello via Model",
text_body="Hello!",
metadata={"user_id": "12345"},
)
)
print(f"Message ID: {response.message_id}")
asyncio.run(main())Enable open and link tracking on outgoing messages.
response = await client.outbound.send(
Email(
sender="you@example.com",
to="recipient@example.com",
subject="Track me",
html_body="<p>Click <a href='https://example.com'>here</a>.</p>",
track_opens=True,
track_links="HtmlAndText", # "None", "HtmlAndText", "HtmlOnly", "TextOnly"
)
)Pass arbitrary email headers using the headers field.
from postmark.models.messages import Email, Header
response = await client.outbound.send(
Email(
sender="you@example.com",
to="recipient@example.com",
subject="Custom headers",
text_body="Hello!",
headers=[
Header(name="X-Custom-Header", value="my-value"),
Header(name="Reply-To", value="reply@example.com"),
],
)
)Attachment content must be Base64-encoded. Use Python's standard base64 module.
import asyncio
import base64
from postmark.models.messages import Attachment, Email
async def main():
# Attachment from in-memory content
report = Attachment(
name="report.txt",
content=base64.b64encode(b"Q3 sales are up 12%.").decode("utf-8"),
content_type="text/plain",
)
# Attachment from a file on disk
with open("/path/to/document.pdf", "rb") as f:
pdf = Attachment(
name="document.pdf",
content=base64.b64encode(f.read()).decode("utf-8"),
content_type="application/pdf",
)
response = await client.outbound.send(
Email(
sender="you@example.com",
to="recipient@example.com",
subject="Your files",
text_body="Please find your files attached.",
attachments=[report, pdf],
)
)
print(f"Sent: {response.message_id}")
asyncio.run(main())Use a content_id on an attachment to embed images inline. Reference it in the HTML body as <img src="cid:...">.
import base64
from postmark.models.messages import Attachment, Email
with open("/path/to/logo.png", "rb") as f:
inline_logo = Attachment(
name="logo.png",
content=base64.b64encode(f.read()).decode("utf-8"),
content_type="image/png",
content_id="cid:logo",
)
response = await client.outbound.send(
Email(
sender="you@example.com",
to="recipient@example.com",
subject="Check out our logo",
html_body='<p>Here is our logo:</p><img src="cid:logo">',
attachments=[inline_logo],
)
)Send multiple distinct emails in a single API call.
Important: The API always returns HTTP 200, even when individual messages fail. Check
response.success(orresponse.error_code == 0) on each item — a truthy HTTP status does not mean every message was accepted.
import asyncio
async def main():
responses = await client.outbound.send_batch([
{
"sender": "you@example.com",
"to": "alice@example.com",
"subject": "Hi Alice",
"text_body": "Hello Alice!",
},
{
"sender": "you@example.com",
"to": "bob@example.com",
"subject": "Hi Bob",
"text_body": "Hello Bob!",
},
])
for resp in responses:
if resp.success:
print(f" Sent: {resp.message_id}")
else:
print(f" Failed [error_code={resp.error_code}]: {resp.message}")
asyncio.run(main())Send the same message to many recipients, each with their own template variables. Uses BulkEmail and BulkRecipient.
import asyncio
from postmark.models.messages import BulkEmail, BulkRecipient
async def main():
response = await client.outbound.send_bulk(
BulkEmail(
sender="you@example.com",
subject="Hello {{FirstName}}, your order is ready",
html_body="<p>Hi {{FirstName}}, your order <strong>{{OrderId}}</strong> is ready.</p>",
text_body="Hi {{FirstName}}, your order {{OrderId}} is ready.",
message_stream="broadcast",
messages=[
BulkRecipient(
to="alice@example.com",
template_model={"FirstName": "Alice", "OrderId": "ORD-001"},
),
BulkRecipient(
to="bob@example.com",
template_model={"FirstName": "Bob", "OrderId": "ORD-002"},
),
],
)
)
print(f"Bulk request ID: {response.id} Status: {response.status}")
# Poll for completion
status = await client.outbound.get_bulk_status(response.id)
print(f"{status.percentage_completed:.0f}% of {status.total_messages} messages sent")
asyncio.run(main())- Templates — Send with pre-built Postmark templates
- Error Handling — Handle API errors gracefully
- Postmark API Reference