Skip to main content

outboxstack

Python client for Outbox Stack email. Python 3.9+, no dependencies.

pip install outboxstack

Create a Sending only API key under Developers in the dashboard and set it as OUTBOX_API_KEY. For tests and staging, create a Test key instead: it checks every message but delivers nothing.

Send

from outboxstack import Outbox

outbox = Outbox()  # reads OUTBOX_API_KEY; base URL defaults to https://outboxstack.app

result = outbox.send({
    "from": "billing@your-verified-domain.com",
    "fromName": "Your app",
    "to": "Ada Obi <ada@example.com>",   # or a list
    "replyTo": "support@your-verified-domain.com",
    "subject": "Your invoice",
    "html": "<p>Invoice attached.</p>",
    "attachments": [{"filename": "invoice.pdf", "content": pdf_bytes}],
    "metadata": {"invoice_id": "inv_123"},
}, idempotency_key=f"invoice:{invoice.id}")

result["id"]          # the first recipient's message id
result["recipients"]  # one entry per recipient when there's more than one

Fields use the API's names: to, cc, bcc (a string or a list, 50 recipients in total), fromName, toName, replyTo, subject, html, text, template with data, headers (20), metadata (10 strings) and attachments (10 files, 10 MB). Attachment content can be raw bytes; the client encodes it.

send() returns once the message is queued, not delivered. Each recipient becomes its own message with its own id, status and bounce tracking.

Every send carries an idempotency key, generated when you don't pass one, so an automatic retry never sends twice. Pass your own to stay safe across process restarts too.

Also: send_batch(messages) (1–100 messages, each may carry idempotencyKey) and get_message(id).

Find messages

recent = outbox.list_messages(status="bounced", limit=100)   # newest 100

for m in outbox.iter_messages(status="bounced", to="@example.com"):
    print(m["id"], m["to_email"], m["created_at"])          # every match, a page at a time

To page by hand, call list_messages_page() and pass its next back as after until it's None.

Cancel, suppress

from outboxstack import OutboxApiError

try:
    outbox.cancel_message(message_id)
except OutboxApiError as e:
    if e.code != "not_cancellable":   # it already started sending
        raise

outbox.add_suppression("ada@example.com")       # needs a full-access key
outbox.remove_suppression("ada@example.com")    # manual suppressions only
for s in outbox.iter_suppressions():
    print(s["email"], s["reason"])

Errors

Exception When
OutboxApiError The API refused the request. Branch on e.code (domain_not_verified, rate_limited, quota_exceeded, …); quote e.request_id to support.
OutboxConnectionError No answer after every retry (e.code is timeout or connection_error). The request may have arrived: retry with e.idempotency_key.
OutboxError Bad input caught before any request, such as an invalid message id.

Timeouts, connection errors, 408, 429 and 5xx are retried twice with backoff, honouring Retry-After (max_retries=, timeout= seconds per attempt).

Webhooks

from outboxstack import verify_webhook, WebhookVerificationError

# Flask example: pass the raw body, not re-serialised JSON.
@app.post("/hooks/outbox")
def outbox_hook():
    try:
        event = verify_webhook(request.get_data(), request.headers, os.environ["OUTBOX_WEBHOOK_SECRET"])
    except WebhookVerificationError:
        return "", 400
    if event["type"] == "message.bounced":            # a hard bounce: final
        mark_undeliverable(event["data"]["to"])
    # message.deferred is a soft bounce or delay: not final, so don't give up on the address.
    return "", 200

Events can arrive more than once: deduplicate on the webhook-id header. See the event reference.

Licence

MIT

Metadata

Release files for outboxstack 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for outboxstack 0.1.0
File Size Uploaded Provenance
outboxstack-0.1.0.tar.gz 13.0 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for outboxstack 0.1.0
File Interpreter ABI Platform
outboxstack-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 23.8 kB

Release files / outboxstack-0.1.0.tar.gz

Download URL outboxstack-0.1.0.tar.gz
Size 13.0 kB
Tags Source
SHA-256 checksum
How to use checksums
2a459a6afa47ecfca104de30cdececbe48b9a199471bc4ecaccf37bce5b831cb
BLAKE2b-256 checksum
How to use checksums
f95f02b5ede786422e0358fc49f20a0a4367bdec714a872035d63118e2207619
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 8, 2026.

Transparency log

Release files / outboxstack-0.1.0-py3-none-any.whl

Download URL outboxstack-0.1.0-py3-none-any.whl
Size 10.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e612c3afd37b42c2667745e54687e95b9cd27e8919461a89f11585bec09bb702
BLAKE2b-256 checksum
How to use checksums
761e191eb637111c382209b8a9a8088f83ad9c085973d19e957341310113ce1d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 8, 2026.

Transparency log

Security for outboxstack 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for outboxstack-0.1.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 8, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page