Skip to main content

recuut Python SDK

The base package is a framework-neutral client for recuut's Merchant API.

pip install "recuut>=0.11.0"
export RECUUT_API_KEY="recuut_test_..."
from recuut import Recuut
from recuut.merchant_api import TransactionFilter, TransactionStatus

with Recuut() as client:
    for transaction in client.transactions.list(
        max=100,
        page_size=50,
        filter=TransactionFilter(status=TransactionStatus.SUCCEEDED),
    ):
        print(transaction.id, transaction.status)

    for resource in client.resources.list(max=100, page_size=25):
        print(resource.id, resource.name)

The same namespaces are available asynchronously:

import asyncio

from recuut import AsyncRecuut

async def main() -> None:
    async with AsyncRecuut() as client:
        page = await client.payers.list(max=100, page_size=25)
        async for payer in page:
            print(payer.identifier, payer.created_at)

asyncio.run(main())

Client methods include: client.payers.list(), client.transactions.list(), client.transactions.get(), client.resources.list(), client.contracts.list(), and client.payments.access(). A list call returns the first page immediately. Its items, count, resolved as_of, and nullable next describe that HTTP page. Iterate over the page to fetch later pages lazily; max bounds the total items yielded by that iterator. Generated filter objects contain backend-owned selection fields. page_size and order are sent to the backend, while max only limits SDK iteration.

Every operation accepts request_options with per-request headers and timeout. Use with_raw_response when successful HTTP metadata matters:

with Recuut() as client:
    response = client.resources.with_raw_response.list(
        page_size=25,
        request_options={"headers": {"X-Trace-ID": "trace-123"}},
    )
    print(response.status_code, response.headers, response.data.items)

Receipts

ReceiptDocument exposes fields directly, such as receipt.id and receipt.payment. The SDK validates the receipt against its transaction.

Test and live isolation

The API key selects one environment. A recuut_test_* key sees only test resources, contracts, payers, and transactions, while a recuut_live_* key sees only live data. Equivalent resources may share an immutable resource key and public rvk_… revision key. Their res_… Resource IDs and internal revision rows remain separate. Client methods intentionally accept no organization or environment parameter.

Pull typed contracts

Configure the only directory the generator may manage:

[tool.recuut]
contracts-output-dir = "recuut_contracts"
recuut pull-contracts -v

The directory must be named recuut_contracts. recuut creates one <resource_key>__generated.py module per resource and re-exports its public symbols from a generated __init__.py. Existing generated packages keep their module and symbol names when upgraded. Generated packages expose RESOURCE_KEY and GENERATED_RESOURCES. They bind revisions by the public rvk_… ID without embedding an environment-specific Resource ID.

Before creating, updating, or deleting managed files, the command prints a plan and asks for confirmation. --yes applies it non-interactively. --check never writes or prompts and exits unsuccessfully when generated source has drifted. Authentication, connection, or schema errors also fail the command. Files without recuut's generated banner are never overwritten or deleted. The check covers every published revision visible to the key. The schema digest describes immutable revision value shapes and does not include environment-specific Resource IDs or mutable pricing policy.

RECUUT_API_URL defaults to https://api.recuut.com; set it, pass --api-url, or configure api-url under [tool.recuut] for local or staging use.

Input and output variable values must be non-negative integers, including zero. Use Python integers or canonical digit strings with at most 18 digits. Decimal values, floats, booleans, signs and exponents are rejected. Prices retain their decimal precision.

FastAPI

pip install "recuut[fastapi]>=0.11.0"
from typing import Self

from fastapi import FastAPI
from pydantic import BaseModel, Field, model_validator
from recuut.fastapi import recuut
from recuut_contracts import (
    TextGenerationV1,
    TextGenerationV1Payment,
)

app = FastAPI()

class GenerateRequest(BaseModel):
    prompt: str
    input_tokens: int = Field(
        default=0,
        exclude=True,
        ge=0,
        json_schema_extra={"readOnly": True},
    )

    @model_validator(mode="after")
    def derive_input_tokens(self) -> Self:
        self.input_tokens = len(self.prompt.split())
        return self

class GenerateResponse(BaseModel):
    text: str

@app.post("/generate")
@recuut(TextGenerationV1)
def generate(
    payload: GenerateRequest,
    payment: TextGenerationV1Payment,
) -> GenerateResponse:
    text = payload.prompt.upper()
    return payment.complete(
        GenerateResponse(text=text),
        output_tokens=len(text.split()),
    )

Generated Payment complete(...) and acomplete(...) methods require every handler-reported value, so mypy and Pyright reject an omitted or misspelled keyword. Values marked as request input are captured from an exact handler argument, one nested Pydantic field, or an explicit input_map. Every input variable must be captured before access; invalid or missing captured values return 422 before any payment request. For revisions without input keys, the access dependency can relay a 402 challenge before FastAPI validates a required body. The decorator leaves validated application inputs unchanged. complete prepares and validates the response before settlement; the decorator releases that prepared response and attaches PAYMENT-RESPONSE. Sync handlers run through FastAPI's thread pool. A returned 4xx or 5xx fails the transaction and reports only status, duration, media type, and response size when available. Response bodies and headers are never sent to recuut. Streaming responses are not supported.

Django REST Framework

pip install "recuut[django]>=0.11.0" djangorestframework

Use your normal serializer and view. Validate before payment access; save only after access succeeds.

from dataclasses import dataclass

from recuut.django import recuut
from rest_framework import serializers
from rest_framework.decorators import api_view
from rest_framework.response import Response

from recuut_contracts import TextGenerationV1, TextGenerationV1Payment

@dataclass
class GenerateResult:
    text: str
    output_tokens: int

class GenerateSerializer(serializers.Serializer):
    prompt = serializers.CharField(max_length=10_000, write_only=True)
    text = serializers.CharField(read_only=True)

    def create(self, validated_data) -> GenerateResult:
        # Replace this sample with your logic and measured usage.
        text = validated_data["prompt"].upper()
        return GenerateResult(text=text, output_tokens=len(text.split()))

@api_view(["POST"])
@recuut(TextGenerationV1)
def generate(request, payment: TextGenerationV1Payment) -> Response:
    serializer = GenerateSerializer(data=request.data)
    serializer.is_valid(raise_exception=True)

    refusal = payment.access(input_tokens=len(serializer.validated_data["prompt"].split()))
    if refusal is not None:
        return refusal

    result = serializer.save()
    return payment.complete(Response(serializer.data), output_tokens=result.output_tokens)

@recuut binds payment to the request. After access succeeds, it reports failure if the view raises or returns 4xx/5xx. A refusal is a native HTTP response; return it immediately. complete() finalizes and renders the response before settlement, attaches the receipt, and returns the native response. Put the same decorator directly on APIView.post() for a class-based view. Preserve your existing authentication, permissions, and CSRF policy.

Plain Django views are also supported. They retain their application's request parsing and validation. Sync views use access() / complete(); async Django views use await aaccess() / await acomplete(). DRF examples use sync views. Streaming responses are unsupported.

Shared payment lifecycle

Django and FastAPI use the same authorization, completion and failure policy. Framework adapters own request headers and final response preparation. FastAPI captures validated inputs automatically; Django supplies them explicitly after its normal validation. Completion happens once, after response validation and rendering. Success is not released until recuut returns a successful receipt.

Pulled contracts expose typed access, aaccess, complete, and acomplete methods. Both completion methods return the supplied native Django response or typed FastAPI value while the FastAPI decorator releases the prepared response. Every reported field remains a required completion keyword. Pull contracts again when upgrading. Previously generated RecuutResult completion helpers remain supported by their existing decorated handlers.

For a revision that has not been pulled, use the explicit mapping fallback:

from recuut import RawPayment, RawRevision
from recuut.fastapi import recuut
from fastapi import FastAPI

app = FastAPI()
revision = RawRevision(revision_key="rvk_00000000000070008000000000000000")

@app.post("/raw")
@recuut(revision)
def raw_endpoint(payment: RawPayment) -> dict[str, bool]:
    return payment.complete({"ok": True}, values={"items": 1})

Errors

Invalid contract construction raises RecuutConfigurationError. Detectable decorator configuration problems warn by default or raise in strict mode. Network failures raise RecuutTransportError. Documented non-successful HTTP responses raise a generated RecuutResponseError; future statuses, undocumented error codes, and malformed payloads raise RecuutUnexpectedResponseError. Response errors retain the exact status, body, and headers so integrations can preserve protocol challenges.

Runnable examples

Each example is an independently locked consumer project kept beside the SDK:

Metadata

Release files for recuut 0.11.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 recuut 0.11.0
File Size Uploaded Provenance Actions
recuut-0.11.0.tar.gz 96.3 kB With provenance Details for recuut-0.11.0.tar.gz

Built distribution (wheel)

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

Total release size: 327.8 kB

Release files / recuut-0.11.0.tar.gz

Download URL recuut-0.11.0.tar.gz
Size 96.3 kB
Tags Source
SHA-256 checksum
How to use checksums
6af034a54db113aa14147244aa24a95eb0084dc157676d277e557280832ee312
BLAKE2b-256 checksum
How to use checksums
bb402ca25bbb9fa7a8ecef8f561c436ecb4df430c4e357695de707541089eb0f
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 6, 2026.

Transparency log

Release files / recuut-0.11.0-py3-none-any.whl

Download URL recuut-0.11.0-py3-none-any.whl
Size 231.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
cd3d427398eab51e330f94b4b589c1ceb72e22651352ab37d13a7ddaecfab955
BLAKE2b-256 checksum
How to use checksums
4f2b0f32d6fc8e65265582898302c29e4cb793c2ac1ffc3122ffbdd49224a732
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 6, 2026.

Transparency log

Security for recuut 0.11.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 recuut-0.11.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 6, 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.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.11.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