Integration

Send emails from FastAPI with Postkit

Send transactional emails from FastAPI endpoints with async httpx. Full async support from request to delivery.

1. Set your API key

pip install httpx
# .env
POSTKIT_API_KEY=pk_live_...

2. Send an email

python
# main.py
import os
import httpx
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class SendRequest(BaseModel):
    to: str
    name: str

@app.post("/send")
async def send_welcome(req: SendRequest):
    async with httpx.AsyncClient() as client:
        response = await client.post(
            "https://api.postkit.eu/v1/emails",
            headers={
                "Authorization": f"Bearer {os.environ['POSTKIT_API_KEY']}",
                "Content-Type": "application/json",
            },
            json={
                "from": "hello@yourapp.eu",
                "to": req.to,
                "subject": "Welcome aboard!",
                "html": f"<h1>Welcome, {req.name}!</h1>",
            },
        )
    return response.json()

3. Handle webhooks

Postkit sends delivery events (sent, delivered, bounced, opened, clicked) via HMAC-SHA256 signed webhooks following the Standard Webhooks specification.

python
# webhooks.py
import os, hashlib, hmac, base64, json
from fastapi import FastAPI, Request, HTTPException

app = FastAPI()

@app.post("/webhooks/postkit")
async def postkit_webhook(request: Request):
    body = await request.body()
    msg_id = request.headers.get("webhook-id", "")
    timestamp = request.headers.get("webhook-timestamp", "")
    signature = request.headers.get("webhook-signature", "")

    secret_b64 = os.environ["POSTKIT_WEBHOOK_SECRET"].replace("whsec_", "")
    secret = base64.b64decode(secret_b64)
    content = f"{msg_id}.{timestamp}.{body.decode()}"
    expected = base64.b64encode(
        hmac.new(secret, content.encode(), hashlib.sha256).digest()
    ).decode()

    valid = any(
        hmac.compare_digest(s.replace("v1,", ""), expected)
        for s in signature.split(" ")
    )

    if not valid:
        raise HTTPException(status_code=401, detail="Invalid signature")

    event = json.loads(body)
    print(f"Postkit event: {event['type']}")
    return {"received": True}

Start sending in under 5 minutes

Free plan. No credit card required.

Get Started Free