Python SDK
The agentforms package provides sync and async Python clients for the AgentForms API, plus field builder helpers.
Installation
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
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()
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
| Param | Type | Default | Description |
|---|---|---|---|
title | str | — | Form title (required) |
fields | list[dict] | — | Field definitions (required) |
description | str | None | None | Form description |
callback_url | str | None | None | Webhook URL |
callback_metadata | dict | None | None | Custom metadata for webhook |
settings | dict | None | None | Additional settings |
expires_in | str | "48h" | Expiry duration |
get_form()
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()
forms = client.list_forms(status="active", limit=10) # → list[Form]
cancel_form()
form = client.cancel_form(form_id="01JN...") # → Form (status="cancelled")
get_responses()
responses = client.get_responses(form_id="01JN...") # → list[Response]
get_response()
resp = client.get_response( form_id="01JN...", response_id="01JN...", ) # → Response
wait_for_response()
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
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
| Attribute | Type | Description |
|---|---|---|
id | str | ULID identifier |
url | str | Public form URL |
title | str | Form title |
description | str | None | Form description |
status | str | active, completed, cancelled, or expired |
fields_count | int | Number of fields |
expires_at | datetime | None | Expiry timestamp |
created_at | datetime | Creation timestamp |
FormDetail
Extends Form with additional attributes:
| Attribute | Type | Description |
|---|---|---|
fields | list[dict] | Field definitions |
callback_url | str | None | Webhook URL |
settings | dict | Form settings |
Response
| Attribute | Type | Description |
|---|---|---|
id | str | ULID identifier |
form_id | str | Parent form ID |
data | dict[str, Any] | Field key to submitted value mapping |
submitted_at | datetime | Submission timestamp |
metadata | dict | IP 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.
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
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
AgentFormsError(Exception)
.message: str
.status_code: int | None
├── AuthenticationError # HTTP 401
├── FormNotFoundError # HTTP 404
└── FormExpiredError # HTTP 410Any HTTP status >= 400 that doesn't match a specific subclass raises AgentFormsError directly.
Full Example
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")