Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ repos:
hooks:
- id: mypy
args: [--ignore-missing-imports]
files: ^(postmark|tests)/
additional_dependencies:
- pydantic
- httpx
Expand Down
4 changes: 2 additions & 2 deletions example.env
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export POSTMARK_SERVER_TOKEN=111-YOUR-SERVER-TOKEN-0000-000000
export POSTMARK_ACCOUNT_TOKEN=222-YOUR-ACCOUNT-TOKEN-0000-000000
export POSTMARK_SERVER_TOKEN=xxx-YOUR-SERVER-TOKEN-xxxx-xxxxxxx
export POSTMARK_ACCOUNT_TOKEN=xxx-YOUR-ACCOUNT-TOKEN-xxxx-xxxxxxx
export POSTMARK_SENDER_EMAIL=test@example.com
export POSTMARK_TEST_MODE=false
export POSTMARK_TRACK_OPENS=true
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
9 changes: 9 additions & 0 deletions examples/sync/bounces/activate_bounce.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import postmark

# Bounce ID's that can be activated show "can_activate" -> True.
bounce_id = 692560173

with postmark.sync.ServerClient("xxx-YOUR-SERVER-TOKEN-xxxx-xxxxxxx") as client:
result = client.bounces.activate(bounce_id)
print(f"Response: {result.message}")
print(f"Inactive after activation: {result.bounce.inactive}")
16 changes: 16 additions & 0 deletions examples/sync/bounces/get_bounce.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import postmark

bounce_id = 692560173

with postmark.sync.ServerClient("xxx-YOUR-SERVER-TOKEN-xxxx-xxxxxxx") as client:
bounce = client.bounces.get(bounce_id)

print(f"ID: {bounce.id}")
print(f"Type: {bounce.type}")
print(f"Email: {bounce.email}")
print(f"Subject: {bounce.subject}")
print(f"Bounced at: {bounce.bounced_at:%Y-%m-%d %H:%M:%S}")
print(f"Description: {bounce.description}")
print(f"Inactive: {bounce.inactive}")
print(f"Can activate: {bounce.can_activate}")
print(f"Dump available: {bounce.dump_available}")
12 changes: 12 additions & 0 deletions examples/sync/bounces/get_bounce_dump.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import postmark

# Bounce ID must have "dump_available" -> True.
bounce_id = 692560173
# Postmark retains raw SMTP dumps for ~30 days after the bounce.

with postmark.sync.ServerClient("xxx-YOUR-SERVER-TOKEN-xxxx-xxxxxxx") as client:
dump = client.bounces.get_dump(bounce_id)
if dump.body:
print(dump.body)
else:
print("Dump not available (may have expired after 30 days).")
9 changes: 9 additions & 0 deletions examples/sync/bounces/get_delivery_stats.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import postmark

with postmark.sync.ServerClient("xxx-YOUR-SERVER-TOKEN-xxxx-xxxxxxx") as client:
stats = client.bounces.get_delivery_stats()

print(f"Inactive addresses: {stats.inactive_mails}")

for entry in stats.bounces:
print(f" {entry.name}: {entry.count}")
11 changes: 11 additions & 0 deletions examples/sync/bounces/list_bounces.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import postmark

with postmark.sync.ServerClient("xxx-YOUR-SERVER-TOKEN-xxxx-xxxxxxx") as client:
# Filter to a specific bounce type; omit `type` to list all.
result = client.bounces.list()
print(f"{result.total} hard bounce(s) on server, showing {len(result.items)}")
for b in result.items:
print(
f" [{b.id}] {b.email} bounced={b.bounced_at:%Y-%m-%d}"
f" inactive={b.inactive}"
)
16 changes: 16 additions & 0 deletions examples/sync/bounces/list_bounces_filtered.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from datetime import datetime

import postmark

with postmark.sync.ServerClient("xxx-YOUR-SERVER-TOKEN-xxxx-xxxxxxx") as client:
# Narrow results to inactive addresses within a date range on a specific stream.
result = client.bounces.list(
count=25,
inactive=True,
from_date=datetime(2024, 1, 1),
to_date=datetime(2024, 12, 31),
message_stream="outbound",
)
print(f"{result.total} matching bounce(s), showing {len(result.items)}")
for b in result.items:
print(f" [{b.id}] {b.email} type={b.type} bounced={b.bounced_at:%Y-%m-%d}")
9 changes: 9 additions & 0 deletions examples/sync/bounces/stream_bounces.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import postmark

with postmark.sync.ServerClient("xxx-YOUR-SERVER-TOKEN-xxxx-xxxxxxx") as client:
# stream() paginates automatically; adjust max_bounces as needed (max 10,000).
count = 0
for b in client.bounces.stream(max_bounces=200):
print(f"[{b.id}] {b.email} type={b.type}")
count += 1
print(f"Streamed {count} bounce(s)")
11 changes: 11 additions & 0 deletions examples/sync/data_removals/create_data_removal.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import postmark

with postmark.sync.AccountClient("xxx-YOUR-ACCOUNT-TOKEN-xxxx-xxxxxxx") as account:
result = account.data_removals.create(
requested_by="admin@example.com",
requested_for="user@example.com",
notify_when_completed=True,
)

print(f"ID: {result.id}")
print(f"Status: {result.status}")
7 changes: 7 additions & 0 deletions examples/sync/data_removals/get_data_removal.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import postmark

with postmark.sync.AccountClient("xxx-YOUR-ACCOUNT-TOKEN-xxxx-xxxxxxx") as account:
result = account.data_removals.get(42)

print(f"ID: {result.id}")
print(f"Status: {result.status}")
15 changes: 15 additions & 0 deletions examples/sync/domains/create_domain.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import postmark

with postmark.sync.AccountClient("xxx-YOUR-ACCOUNT-TOKEN-xxxx-xxxxxxx") as account:
domain = account.domain.create(
name="example.com",
return_path_domain="pm-bounces.example.com",
)

print("Created domain:")
print(f" ID: {domain.id}")
print(f" Name: {domain.name}")
print(f" DKIM host: {domain.dkim_host}")
print(f" DKIM text value: {domain.dkim_text_value}")
print(f" Return-Path domain: {domain.return_path_domain}")
print(f" Return-Path CNAME: {domain.return_path_domain_cname_value}")
7 changes: 7 additions & 0 deletions examples/sync/domains/delete_domain.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import postmark

domain_id = 0 # Replace with the ID of the domain to delete

with postmark.sync.AccountClient("xxx-YOUR-ACCOUNT-TOKEN-xxxx-xxxxxxx") as account:
result = account.domain.delete(domain_id)
print(result.message)
15 changes: 15 additions & 0 deletions examples/sync/domains/edit_domain.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import postmark

domain_id = 0 # Replace with the ID of the domain to update

with postmark.sync.AccountClient("xxx-YOUR-ACCOUNT-TOKEN-xxxx-xxxxxxx") as account:
domain = account.domain.edit(
domain_id,
return_path_domain="pm-bounces.example.com",
)

print("Updated domain:")
print(f" ID: {domain.id}")
print(f" Name: {domain.name}")
print(f" Return-Path domain: {domain.return_path_domain}")
print(f" Return-Path verified: {domain.return_path_domain_verified}")
14 changes: 14 additions & 0 deletions examples/sync/domains/get_domain.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import postmark

domain_id = 0 # Replace with the ID of the domain to retrieve

with postmark.sync.AccountClient("xxx-YOUR-ACCOUNT-TOKEN-xxxx-xxxxxxx") as account:
domain = account.domain.get(domain_id)

print(f"Domain: {domain.name}")
print(f" ID: {domain.id}")
print(f" DKIM verified: {domain.dkim_verified}")
print(f" DKIM host: {domain.dkim_host}")
print(f" Return-Path domain: {domain.return_path_domain}")
print(f" Return-Path verified: {domain.return_path_domain_verified}")
print(f" DKIM update status: {domain.dkim_update_status}")
13 changes: 13 additions & 0 deletions examples/sync/domains/list_domains.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import postmark

with postmark.sync.AccountClient("xxx-YOUR-ACCOUNT-TOKEN-xxxx-xxxxxxx") as account:
result = account.domain.list()

print(f"Total domains: {result.total}")
print()

for domain in result.items:
print(f" [{domain.id}] {domain.name}")
print(f" DKIM verified: {domain.dkim_verified}")
print(f" Return-Path verified: {domain.return_path_domain_verified}")
print("----------------------------------------")
12 changes: 12 additions & 0 deletions examples/sync/domains/rotate_dkim.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import postmark

domain_id = 0 # Replace with the ID of the domain to rotate DKIM keys for

with postmark.sync.AccountClient("xxx-YOUR-ACCOUNT-TOKEN-xxxx-xxxxxxx") as account:
domain = account.domain.rotate_dkim(domain_id)

print(f"Domain: {domain.name}")
print(f" DKIM update status: {domain.dkim_update_status}")
print(f" Current DKIM host: {domain.dkim_host}")
print(f" Pending DKIM host: {domain.dkim_pending_host}")
print(f" Pending DKIM text value: {domain.dkim_pending_text_value}")
11 changes: 11 additions & 0 deletions examples/sync/domains/verify_dkim.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import postmark

domain_id = 0 # Replace with the ID of the domain to verify

with postmark.sync.AccountClient("xxx-YOUR-ACCOUNT-TOKEN-xxxx-xxxxxxx") as account:
domain = account.domain.verify_dkim(domain_id)

print(f"Domain: {domain.name}")
print(f" DKIM verified: {domain.dkim_verified}")
print(f" DKIM update status: {domain.dkim_update_status}")
print(f" Weak DKIM: {domain.weak_dkim}")
11 changes: 11 additions & 0 deletions examples/sync/domains/verify_return_path.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import postmark

domain_id = 0 # Replace with the ID of the domain to verify

with postmark.sync.AccountClient("xxx-YOUR-ACCOUNT-TOKEN-xxxx-xxxxxxx") as account:
domain = account.domain.verify_return_path(domain_id)

print(f"Domain: {domain.name}")
print(f" Return-Path domain: {domain.return_path_domain}")
print(f" Return-Path verified: {domain.return_path_domain_verified}")
print(f" Return-Path CNAME: {domain.return_path_domain_cname_value}")
9 changes: 9 additions & 0 deletions examples/sync/inbound_messages/bypass_inbound.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import postmark

MESSAGE_ID = "your-blocked-message-id-here"

with postmark.sync.ServerClient("xxx-YOUR-SERVER-TOKEN-xxxx-xxxxxxx") as client:
result = client.inbound.bypass(MESSAGE_ID)

print(f"Error code: {result.error_code}")
print(f"Message: {result.message}")
18 changes: 18 additions & 0 deletions examples/sync/inbound_messages/get_inbound_by_id.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import postmark

MESSAGE_ID = "your-message-id-here"

with postmark.sync.ServerClient("xxx-YOUR-SERVER-TOKEN-xxxx-xxxxxxx") as client:
msg = client.inbound.get(MESSAGE_ID)

print(f"ID: {msg.message_id}")
print(f"From: {msg.from_email} ({msg.from_name})")
print(f"To: {msg.to}")
print(f"Subject: {msg.subject}")
print(f"Status: {msg.status}")
print(f"Date: {msg.date}")
if msg.text_body:
print(f"Body: {msg.text_body[:100]}")
if msg.blocked_reason:
print(f"Blocked: {msg.blocked_reason}")
print(f"Headers: {len(msg.headers)}")
14 changes: 14 additions & 0 deletions examples/sync/inbound_messages/list_inbound.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import postmark

with postmark.sync.ServerClient("xxx-YOUR-SERVER-TOKEN-xxxx-xxxxxxx") as client:
result = client.inbound.list(count=10)

print(f"Total inbound messages: {result.total}")
print()

for msg in result.items:
print(f" [{msg.message_id}] {msg.subject}")
print(f" From: {msg.from_email}")
print(f" Status: {msg.status}")
print(f" Date: {msg.date}")
print("----------------------------------------")
9 changes: 9 additions & 0 deletions examples/sync/inbound_messages/retry_inbound.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import postmark

MESSAGE_ID = "id-of-a-failed-message"

with postmark.sync.ServerClient("xxx-YOUR-SERVER-TOKEN-xxxx-xxxxxxx") as client:
result = client.inbound.retry(MESSAGE_ID)

print(f"Error code: {result.error_code}")
print(f"Message: {result.message}")
8 changes: 8 additions & 0 deletions examples/sync/inbound_rules/create_inbound_rule.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import postmark

with postmark.sync.ServerClient("xxx-YOUR-SERVER-TOKEN-xxxx-xxxxxxx") as server:
rule = server.inbound_rules.create("spam@example.com")

print("Created inbound rule:")
print(f" ID: {rule.id}")
print(f" Rule: {rule.rule}")
7 changes: 7 additions & 0 deletions examples/sync/inbound_rules/delete_inbound_rule.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import postmark

trigger_id = 0 # Replace with the ID of the inbound rule to delete

with postmark.sync.ServerClient("xxx-YOUR-SERVER-TOKEN-xxxx-xxxxxxx") as server:
result = server.inbound_rules.delete(trigger_id)
print(result.message)
9 changes: 9 additions & 0 deletions examples/sync/inbound_rules/list_inbound_rules.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import postmark

with postmark.sync.ServerClient("xxx-YOUR-SERVER-TOKEN-xxxx-xxxxxxx") as server:
result = server.inbound_rules.list()

print(f"Total inbound rules: {result.total}\n")

for rule in result.items:
print(f" [{rule.id}] {rule.rule}")
9 changes: 9 additions & 0 deletions examples/sync/message_streams/archive_stream.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import postmark

STREAM_ID = "my-broadcasts"

with postmark.sync.ServerClient("xxx-YOUR-SERVER-TOKEN-xxxx-xxxxxxx") as client:
result = client.stream.archive(STREAM_ID)

print(f"Archived stream: {result.id}")
print(f"Expected purge date: {result.expected_purge_date}")
20 changes: 20 additions & 0 deletions examples/sync/message_streams/create_stream.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import postmark
from postmark.models.streams import MessageStreamType, UnsubscribeHandlingType

with postmark.sync.ServerClient("xxx-YOUR-SERVER-TOKEN-xxxx-xxxxxxx") as client:
stream = client.stream.create(
id="my-broadcasts",
name="My Broadcast Stream",
message_stream_type=MessageStreamType.BROADCASTS,
description="Used for newsletters and announcements",
unsubscribe_handling_type=UnsubscribeHandlingType.POSTMARK,
)

print("Created stream:")
print(f" ID: {stream.id}")
print(f" Name: {stream.name}")
print(f" Type: {stream.message_stream_type.value}")
print(f" Description: {stream.description}")
print(
f" Unsubscribe: {stream.subscription_management_configuration.unsubscribe_handling_type.value}"
)
20 changes: 20 additions & 0 deletions examples/sync/message_streams/edit_stream.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import postmark
from postmark.models.streams import UnsubscribeHandlingType

STREAM_ID = "my-broadcasts"

with postmark.sync.ServerClient("xxx-YOUR-SERVER-TOKEN-xxxx-xxxxxxx") as client:
stream = client.stream.edit(
STREAM_ID,
name="Updated Broadcast Stream",
description="Newsletters and product updates",
unsubscribe_handling_type=UnsubscribeHandlingType.POSTMARK,
)

print("Updated stream:")
print(f" ID: {stream.id}")
print(f" Name: {stream.name}")
print(f" Description: {stream.description}")
print(
f" Unsubscribe: {stream.subscription_management_configuration.unsubscribe_handling_type.value}"
)
16 changes: 16 additions & 0 deletions examples/sync/message_streams/get_stream.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import postmark

STREAM_ID = "outbound"

with postmark.sync.ServerClient("xxx-YOUR-SERVER-TOKEN-xxxx-xxxxxxx") as client:
stream = client.stream.get(STREAM_ID)

print(f"ID: {stream.id}")
print(f"Name: {stream.name}")
print(f"Type: {stream.message_stream_type.value}")
print(f"Description: {stream.description}")
print(f"Created at: {stream.created_at}")
print(f"Updated at: {stream.updated_at}")
print(
f"Unsubscribe: {stream.subscription_management_configuration.unsubscribe_handling_type.value}"
)
14 changes: 14 additions & 0 deletions examples/sync/message_streams/list_streams.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import postmark

with postmark.sync.ServerClient("xxx-YOUR-SERVER-TOKEN-xxxx-xxxxxxx") as client:
result = client.stream.list()

print(f"Total streams: {result.total}")
print()

for stream in result.items:
print(f" [{stream.id}] {stream.name}")
print(f" Type: {stream.message_stream_type.value}")
print(f" Created at: {stream.created_at}")
print(f" Archived: {stream.archived_at is not None}")
print("----------------------------------------")
Loading