Send and verify email events in FastAPI.
The notix-sdk client is synchronous, so declare the routes that use it with plain def: FastAPI runs those in its thread pool and the event loop stays free. The webhook route reads the raw request body so the signature check sees exactly what Notix signed.
Send an email from FastAPI.
import os
from fastapi import FastAPI
from notix import Notix
app = FastAPI()
notix = Notix(os.environ["NOTIX_API_KEY"])
@app.post("/orders/{order_id}/receipt")
def send_receipt(order_id: int, to: str):
resp, _ = notix.emails.send(
payload={
"from": "Acme <receipts@acme.com>",
"to": to,
"subject": "Your receipt",
"html": "<p>Thanks for your order.</p>",
},
options={"idempotency_key": f"order-{order_id}-receipt"},
)
return {"id": resp["emailId"]}
The from address must be on a domain you have verified in Notix. Step by step: the Python quickstart and the longer guide to sending email from FastAPI.
One API key, kept on the server.
Create an API key on the API keys page of the Notix console and store it as NOTIX_API_KEY in the process environment. Read it only in code that runs on the server, never in anything shipped to a browser.
A Send only key is enough for code that only sends: if it leaks, it cannot read your contacts or delete a domain.
NOTIX_API_KEY=notix_xxxxxxxxxxxx
NOTIX_WEBHOOK_SECRET=whsec_xxxxxxxxxxxx
Verify delivery events over the raw body.
Notix signs every webhook call with the webhook's signing secret: an HMAC-SHA256 of the X-Notix-Timestamp header, a dot and the raw body, sent in X-Notix-Signature. The SDKs check the signature and refuse a call more than 5 minutes old.
Pass the request body as bytes, before any JSON parsing: the signature covers the exact bytes Notix sent. A bad call raises WebhookVerificationError; answer 400.
from fastapi import HTTPException, Request
from notix import WebhookVerificationError
webhooks = notix.webhooks(os.environ["NOTIX_WEBHOOK_SECRET"])
@app.post("/webhooks/notix")
async def notix_webhook(request: Request):
try:
event = webhooks.construct_event(await request.body(), headers=request.headers)
except WebhookVerificationError:
raise HTTPException(status_code=400, detail="invalid signature")
return {"received": event["type"]}
Questions, answered.
Is the Python SDK async?
What if the same request runs twice?
Why is my send refused with a from-address error?
Keep reading.
Send email from FastAPI
An async route that sends, dependency injection for the client, a background task, and a webhook endpoint with a verified signature.
DocsPython
Send an email from Python with the notix-sdk package.
IntegrationsIntegrations
Notix inside the frameworks, platforms and AI coding tools you already use, from Next.js and Laravel to Supabase, Vercel and Cursor.
ProductEmail API
One JSON API for transactional and marketing email: send, batch, schedule, webhooks, typed SDKs.
NotixPricing
A free plan with no card, and Pro at $15 a month for 50,000 emails. Only sent volume is metered.
Send your first email in five minutes.
Verify a domain, copy an API key, make one call. The free plan does not ask for a card.