> ## Documentation Index
> Fetch the complete documentation index at: https://repost.sh/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Python Framework Integration

> Own one generated Repost client across Django, Flask, or FastAPI and close it during graceful shutdown.

The Python package is framework-neutral. Create one generated client for each application process, inject or import it where events are published, and close it during shutdown. Do not construct a client for every request—the shared instance owns bounded connection pools and runtime state.

## Django

Keep the client in a small server-only module and import it from views, services, or signal handlers:

```python myapp/repost.py theme={"languages":{"custom":["/languages/repost.json"]}}
import atexit

from repost_sdk import RepostClient

repost = RepostClient()
atexit.register(repost.close)
```

`atexit` is a process fallback. When your WSGI or ASGI server exposes worker lifecycle hooks, create and close the client in those hooks instead. Each worker process owns its own instance.

If `AppConfig.ready()` wires Django signals, guard against the development autoreloader registering them twice. Do not perform sends or other network work from `ready()`.

## Flask

Create the client next to the application, then expose it through `app.extensions` for handlers and services:

```python theme={"languages":{"custom":["/languages/repost.json"]}}
import atexit

from flask import Flask
from repost_sdk import RepostClient


def create_app() -> Flask:
    app = Flask(__name__)
    repost = RepostClient()
    app.extensions["repost"] = repost
    atexit.register(repost.close)
    return app
```

Prefer your serving process's graceful-shutdown hook when it provides one. Flask request teardown callbacks run after every request, so they are the wrong owner for a process-wide client.

## FastAPI

Python sends are synchronous in `0.4`. Own the client with the application lifespan and call generated methods through Starlette's thread pool so an async route does not block the event loop:

```python theme={"languages":{"custom":["/languages/repost.json"]}}
from contextlib import asynccontextmanager

from fastapi import FastAPI
from starlette.concurrency import run_in_threadpool

from repost_sdk import RepostClient, User


@asynccontextmanager
async def lifespan(app: FastAPI):
    repost = RepostClient()
    app.state.repost = repost
    try:
        yield
    finally:
        repost.close()


app = FastAPI(lifespan=lifespan)


@app.post("/users")
async def create_user() -> dict[str, str]:
    result = await run_in_threadpool(
        app.state.repost.webhooks.user.created,
        customer_id="customer_123",
        data=User(id="user_123", email="ada@example.com"),
        idempotency_key="user_123:created",
    )
    return {"message_id": result.id}
```

The SDK does not create an event loop or install an executor. Your framework remains responsible for thread-pool capacity and request cancellation policy.

## Trace parenting

Configure `telemetry()` once when you construct the client. It captures the current OpenTelemetry context for every send, so `repost.send` becomes a child of the active inbound request span. Standard Django, Flask, and FastAPI OpenTelemetry middleware can provide that parent. The SDK does not install middleware or global instrumentation.

## Continue

<Columns cols={3} className="gap-y-4">
  <Card title="Compatibility & security" icon="shield" href="/docs/send/python/compatibility" cta="Baseline" arrow="true">
    Review supported interpreters, dependencies, threading, and credential boundaries.
  </Card>

  <Card title="Configuration" icon="sliders-horizontal" href="/docs/send/python/configuration" cta="Configure" arrow="true">
    Build the options owned by your application lifecycle.
  </Card>

  <Card title="Observability" icon="activity" href="/docs/send/python/observability" cta="Observe" arrow="true">
    Wire metrics and tracing without global instrumentation.
  </Card>
</Columns>
