pagesystems
Page Systems gives your Python agent hosted web pages (pickers, forms, tables and more). The agent sends the person a link, the person answers on the page, and the answer arrives in your agent's conversation as a new turn. The agent does not poll, and the person does not have to say "done".
The package is a thin client. It bundles no page types, schemas or rendering: your agent reads them from the Page Systems API at runtime, so new page types work without an update. The core uses only the standard library.
1. Install
pip install pagesystems # client and callback handler
pip install "pagesystems[openai-agents]" fastapi uvicorn # or langchain, crewai, pydantic-ai, google-adk, claude-agent-sdk
export PAGE_SYSTEMS_API_KEY=ap_live_... # create a key in the Page Systems dashboard
Python 3.10 or newer. PAGE_SYSTEMS_BASE_URL defaults to https://pagesystems.ai.
2. Add the tools and a callback route
Give your agent the five tools (list_page_types, get_page_type_schema, create_page, get_page_result, cancel_page). Pass the public HTTPS URL of your callback route and an agent_ref that identifies the conversation, so each page calls back with its own conversation id.
from fastapi import BackgroundTasks, FastAPI, Request
from fastapi.responses import JSONResponse
from agents import Agent, Runner, SQLiteSession
from pagesystems import CallbackError, handle_callback_async
from pagesystems.openai_agents import page_systems_tools
app = FastAPI()
CALLBACK_URL = "https://your-app.example.com/page-systems"
def make_agent(conversation_id: str) -> Agent:
return Agent(
name="Assistant",
instructions="Use Page Systems pages when the person must choose or fill in something.",
tools=page_systems_tools(
callback_url=CALLBACK_URL,
agent_ref={"framework": "python", "sessionId": conversation_id},
),
)
async def take_turn(conversation_id: str, message: str):
# The session stores the conversation's history, so the same id continues it.
session = SQLiteSession(conversation_id, "conversations.db")
result = await Runner.run(make_agent(conversation_id), message, session=session)
await send_to_user(conversation_id, result.final_output) # your code: however your app delivers a reply
@app.post("/page-systems")
async def page_systems_callback(request: Request, background: BackgroundTasks):
# Page Systems waits 10 seconds for this response, so answer now and run the turn after.
def resume(event):
if not event.agent_ref: # a page this app did not create (for example over MCP): decline it, the answer stays readable with get_page_result
raise CallbackError(410, {"error": "no_conversation"})
# event.message is "[Page Systems] The person submitted ...: {answer}. Continue the conversation."
background.add_task(take_turn, event.agent_ref["sessionId"], event.message)
status, body = await handle_callback_async(await request.body(), on_submitted=resume, on_new_page_requested=resume)
return JSONResponse(body, status_code=status)
Page Systems gives up on a request after 10 seconds and retries it with backoff (first after 30 seconds, then doubling, at most an hour apart). A slow handler can therefore run the same turn twice. So the handler only schedules the turn and returns; FastAPI's BackgroundTasks runs it after the response. send_to_user is your own code. Use the same resume for on_new_page_requested, or Page Systems gets a 410 no_handler for that event.
Every adapter has the same shape:
| Framework | Import | Returns |
|---|---|---|
| OpenAI Agents SDK | pagesystems.openai_agents.page_systems_tools(...) |
function_tools |
| LangChain | pagesystems.langchain.page_systems_tools(...) |
StructuredTools |
| CrewAI | pagesystems.crewai.page_systems_tools(...) |
CrewAI tools |
| Pydantic AI | pagesystems.pydantic_ai.page_systems_tools(...) |
Tools |
| Google ADK | pagesystems.google_adk.page_systems_tools(...) |
FunctionTools |
| Claude Agent SDK | pagesystems.claude_agent_sdk.page_systems_server(...) |
in-process MCP server, for mcp_servers={"pages": server} |
Each takes callback_url=None, agent_ref=None, client=None.
3. The answer arrives as a new turn
When the person submits, Page Systems calls your route with {"type": "page.submitted", "pageId": "..."}. handle_callback reads the answer with your API key, calls your handler with an event (page_id, title, result, agent_ref, message), and then claims the answer. Your handler starts the next turn of that conversation, and should return within 10 seconds (start the turn in the background; see above).
Status codes returned to Page Systems: 200 done or already handled; 503 reading failed or your handler raised (Page Systems retries, and the answer is not claimed until the handler succeeds); 410 cannot be delivered (also when you pass no handler for the event type); 400 bad body. Raise pagesystems.CallbackError(410 or 503, body) in a handler to choose the response. A retry after a lost response can deliver the same answer twice, so make the handler safe to repeat.
on_new_page_requested runs when the person presses "Request a new page" on an expired, unanswered page. Nothing was created; your agent decides whether to send another page.
Use handle_callback(body, ...) (body: a dict, or the raw request bytes or string) with synchronous handlers (Flask, Django). Use handle_callback_async when handlers are async def.
Client
from pagesystems import PageSystems
ps = PageSystems() # api_key and base_url default to the environment, read at call time
ps.list_page_types(); ps.get_page_type("form"); ps.get_result(page_id)
ps.create_page({"title": "...", "page": {"type": "form", ...}}, callback_url=..., agent_ref=..., idempotency_key=...)
ps.claim(page_id); ps.cancel(page_id); ps.get_skill()
create_page adds version: 1. An invalid spec or paused creation (payment_required, spend_cap_reached, burst_limit) returns a dict with error and message instead of raising. Other API failures raise PageSystemsError.
Pages collect an answer; they never book, buy or pay. Do not use them for card numbers, passwords or identity documents.
Metadata
Release files for pagesystems 0.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| pagesystems-0.4.0.tar.gz | 10.9 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pagesystems-0.4.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 26.3 kB
Release files / pagesystems-0.4.0.tar.gz
| Download URL | pagesystems-0.4.0.tar.gz |
|---|---|
| Size | 10.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9c916d6be76c3130e343838fe57eb862063ccd76a353496b9dbe6189bb00ee7c
|
|
BLAKE2b-256 checksum How to use checksums |
a121ca4310f4bd294a9fd8a904da00add6f656439e09dcfb66e4623a37cb774b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.14 {"installer":{"name":"uv","version":"0.11.14","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / pagesystems-0.4.0-py3-none-any.whl
| Download URL | pagesystems-0.4.0-py3-none-any.whl |
|---|---|
| Size | 15.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
6362d102110b6312b981d211dcb18d82e8cfdf949bb9a43efdf6e1697e00584e
|
|
BLAKE2b-256 checksum How to use checksums |
159c80da0f62edc72c0a1405c79020cf3d8bf636759c3355670f277e97da2159
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.14 {"installer":{"name":"uv","version":"0.11.14","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Security for pagesystems 0.4.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.
No provenance found
None of the files in this release were uploaded with provenance metadata.
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.