Python SDK

The agentforms package provides sync and async Python clients for the AgentForms API, plus field builder helpers.

Installation

bash
pip install agentforms

Note: The package is not yet published on PyPI. For now, install from source: pip install -e packages/sdk

Requirements: Python 3.12+, httpx >= 0.27, pydantic >= 2.0.

Sync Client

Python
from agentforms import AgentForms

client = AgentForms(
    api_key="af_live_...",
    base_url="http://localhost:8000",  # default
)

# Or use as a context manager
with AgentForms(api_key="af_live_...") as client:
    form = client.create_form(...)

Methods

create_form()

Python
form = client.create_form(
    title="Feedback Form",
    fields=[
        fields.Text(key="name", label="Name", required=True),
        fields.Email(key="email", label="Email"),
    ],
    description="Optional description",
    callback_url="https://example.com/hook",
    callback_metadata={"task_id": "123"},
    settings={},
    expires_in="48h",  # default
) # → Form
ParamTypeDefaultDescription
titlestrForm title (required)
fieldslist[dict]Field definitions (required)
descriptionstr | NoneNoneForm description
callback_urlstr | NoneNoneWebhook URL
callback_metadatadict | NoneNoneCustom metadata for webhook
settingsdict | NoneNoneAdditional settings
expires_instr"48h"Expiry duration

get_form()

Python
detail = client.get_form(form_id="01JN...")  # → FormDetail
print(detail.fields)       # list of field dicts
print(detail.callback_url)  # webhook URL or None

list_forms()

Python
forms = client.list_forms(status="active", limit=10)  # → list[Form]

cancel_form()

Python
form = client.cancel_form(form_id="01JN...")  # → Form (status="cancelled")

get_responses()

Python
responses = client.get_responses(form_id="01JN...")  # → list[Response]

get_response()

Python
resp = client.get_response(
    form_id="01JN...",
    response_id="01JN...",
)  # → Response

wait_for_response()

Python
response = client.wait_for_response(
    form_id="01JN...",
    timeout=3600,      # seconds (default: 1 hour)
    poll_interval=5,  # seconds between polls (default: 5)
)  # → Response | None

Blocks until a response is submitted or the timeout is reached. Returns None if no response was received before timeout.

close()

Close the underlying HTTP client. Called automatically when using the context manager.

Async Client

Python
import asyncio
from agentforms import AsyncAgentForms, fields

async def main():
    async with AsyncAgentForms(api_key="af_live_...") as client:
        form = await client.create_form(
            title="Quick Poll",
            fields=[
                fields.Select(
                    key="vote",
                    label="Your choice",
                    options=["Option A", "Option B"],
                    required=True,
                ),
            ],
        )
        print(f"Form URL: {form.url}")

        response = await client.wait_for_response(form.id)
        print(f"Vote: {response.data['vote']}")

asyncio.run(main())

The async client has the same methods as the sync client (all async def), except get_response() which is only available on the sync client.

Return Models

Form

AttributeTypeDescription
idstrULID identifier
urlstrPublic form URL
titlestrForm title
descriptionstr | NoneForm description
statusstractive, completed, cancelled, or expired
fields_countintNumber of fields
expires_atdatetime | NoneExpiry timestamp
created_atdatetimeCreation timestamp

FormDetail

Extends Form with additional attributes:

AttributeTypeDescription
fieldslist[dict]Field definitions
callback_urlstr | NoneWebhook URL
settingsdictForm settings

Response

AttributeTypeDescription
idstrULID identifier
form_idstrParent form ID
datadict[str, Any]Field key to submitted value mapping
submitted_atdatetimeSubmission timestamp
metadatadictIP address and user agent

Field Builders

The fields module provides helper functions that return field dicts. See the Field Types reference for details on each type's config options.

Python
from agentforms import fields

fields.Text(key, label, required=False, description=None, placeholder=None, max_length=None, pattern=None)
fields.Textarea(key, label, required=False, description=None, placeholder=None, max_length=None, rows=None)
fields.Number(key, label, required=False, description=None, min=None, max=None, step=None)
fields.Email(key, label, required=False, description=None, placeholder=None)
fields.Select(key, label, options, required=False, description=None)
fields.MultiSelect(key, label, options, required=False, description=None)
fields.Date(key, label, required=False, description=None, min_date=None, max_date=None)
fields.Checkbox(key, label, required=False, description=None)

Config keys with None values are omitted from the output dict. Any extra keyword arguments are passed through as additional config entries.

Error Handling

Python
from agentforms import (
    AgentFormsError,
    AuthenticationError,
    FormNotFoundError,
    FormExpiredError,
)

try:
    form = client.get_form("invalid_id")
except FormNotFoundError as e:
    print(f"Not found: {e.message}")  # e.status_code == 404
except AuthenticationError:
    print("Bad API key")  # status_code == 401
except FormExpiredError:
    print("Form expired")  # status_code == 410
except AgentFormsError as e:
    print(f"Error {e.status_code}: {e.message}")

Exception Hierarchy

text
AgentFormsError(Exception)
    .message: str
    .status_code: int | None
    ├── AuthenticationError    # HTTP 401
    ├── FormNotFoundError      # HTTP 404
    └── FormExpiredError       # HTTP 410

Any HTTP status >= 400 that doesn't match a specific subclass raises AgentFormsError directly.

Full Example

Python
from agentforms import AgentForms, fields, FormExpiredError

with AgentForms(api_key="af_live_...") as client:
    # Create a form
    form = client.create_form(
        title="Employee Onboarding",
        description="Please fill out your information",
        fields=[
            fields.Text(key="name", label="Full Name", required=True),
            fields.Email(key="email", label="Work Email", required=True),
            fields.Select(
                key="department",
                label="Department",
                options=["Engineering", "Design", "Marketing"],
                required=True,
            ),
            fields.Date(key="start_date", label="Start Date"),
            fields.Checkbox(key="agree", label="I accept the terms", required=True),
        ],
        expires_in="7d",
    )

    print(f"Please fill out: {form.url}")

    # Wait for submission
    try:
        response = client.wait_for_response(form.id, timeout=600)
        if response:
            print(f"Welcome, {response.data['name']}!")
            print(f"Department: {response.data['department']}")
        else:
            print("No response received (timeout)")
            client.cancel_form(form.id)
    except FormExpiredError:
        print("Form expired before submission")