Skip to main content

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)

Source distribution for pagesystems 0.4.0
File Size Uploaded Provenance
pagesystems-0.4.0.tar.gz 10.9 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for pagesystems 0.4.0
File Interpreter ABI Platform
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.

Release history Release notifications | RSS feed

This release

0.4.0 This release

2 release files No 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