# Django (/sdks/python/django)

Use Python 3.11 through 3.14. From an empty directory, create and activate a virtual environment:

```bash
python -m venv .venv
. .venv/bin/activate
```

Install the Django extra, then create a project with an `api` application:

```bash
pip install "recuut[django]"
```

```bash
django-admin startproject my_app .
python manage.py startapp api
```

Set the merchant test key in your server environment; the SDK reads `RECUUT_API_KEY` automatically unless you pass a client or `api_key` to the decorator.

```bash
export RECUUT_API_KEY="recuut_test_..."
```

Before choosing a contract setup, complete [step 1 of the Direct HTTP guide](/guides/http/#1-create-the-test-payment-policy) to create and publish the resource, revision, Price, and test key. You do not need to build its raw HTTP server.

## Choose your contract setup




Generate a contract when your application can commit generated files. This keeps the revision ID and reporting fields in checked Python types.

After you publish the revision, add this output location to your project `pyproject.toml`:

```toml title="pyproject.toml"
[tool.recuut]
contracts-output-dir = "my_app/recuut_contracts"
```

Then pull the revision contract. The generated payment type makes the reporting fields part of the Python function signature, so a missing or misspelled field is caught by your type checker.

```bash
recuut pull-contracts -v
```

Add this typed view to `api/views.py`. `TextGenerationV1` and `TextGenerationPayment` stand for the names generated for your published revision.

```python title="api/views.py"
from django.http import HttpRequest
from pydantic import BaseModel
from recuut.django import recuut

from my_app.recuut_contracts import TextGenerationPayment, TextGenerationV1


class GenerateRequest(BaseModel):
    prompt: str


class GenerateResponse(BaseModel):
    text: str


@recuut(TextGenerationV1)
async def generate(
    request: HttpRequest,
    payload: GenerateRequest,
    payment: TextGenerationPayment,
) -> GenerateResponse:
    text = payload.prompt.upper()
    payment.report(
        input_tokens=len(payload.prompt.split()),
        output_tokens=len(text.split()),
    )
    return GenerateResponse(text=text)
```

`payment.report(input_tokens=..., output_tokens=...)` is typed from that revision. The SDK validates the values again when it completes the transaction.




Use a raw revision only when your application cannot generate files. Set its explicit revision ID in addition to `RECUUT_API_KEY`:

```bash
export RECUUT_REVISION_ID="acr_..."
export RECUUT_SETTLEMENT_ASSET="USDC"
```

The reporting values are a dictionary, so your Python type checker cannot catch a missing or misspelled contract field.

```python title="api/views.py"
import os

from django.http import HttpRequest
from pydantic import BaseModel
from recuut import RawPayment, RawRevision
from recuut.django import recuut
from recuut.merchant_api.models import SettlementAsset


class GenerateRequest(BaseModel):
    prompt: str


class GenerateResponse(BaseModel):
    text: str


revision = RawRevision(
    os.environ["RECUUT_REVISION_ID"],
    SettlementAsset(os.environ["RECUUT_SETTLEMENT_ASSET"]),
)


@recuut(revision)
async def generate(
    request: HttpRequest,
    payload: GenerateRequest,
    payment: RawPayment,
) -> GenerateResponse:
    del request
    text = payload.prompt.upper()
    payment.report(
        {
            "input_tokens": len(payload.prompt.split()),
            "output_tokens": len(text.split()),
        }
    )
    return GenerateResponse(text=text)
```




Register the view in `api/urls.py`:

```python title="api/urls.py"
from django.urls import path

from .views import generate

urlpatterns = [path("generate", generate)]
```

Include those application routes from the project URL configuration:

```python title="my_app/urls.py"
from django.urls import include, path

urlpatterns = [path("", include("api.urls"))]
```

Start Django from the directory containing `manage.py`:

```bash
python manage.py runserver
```

Check the unpaid path before you use a wallet:

```bash
curl --include \
  --request POST \
  --url http://127.0.0.1:8000/generate \
  --header 'Content-Type: application/json' \
  --data '{"prompt":"paid testnet response"}'
```

Expect HTTP `402`, a `PAYMENT-REQUIRED` header, and no uppercase result. If the response differs, verify the published revision, active Price, **Require payment** setting, and test key before continuing.

The integration validates normal input before it asks Recuut for access. A successful paid request completes payment before it returns the view result with `PAYMENT-RESPONSE`. HTTP `202` remains private. A returned `4xx` or `5xx` response fails the transaction and reports only its status, duration, media type, and byte size. The response body and headers are not sent to recuut.

Use the [wallet and Circle faucet preflight](/guides/http/#4-create-and-fund-the-base-sepolia-wallet), then run the [paid AWAL retry](/guides/http/#5-run-the-paid-retry) against this same URL. The [receipt check](/guides/http/#6-verify-the-receipt) is identical.
