Skip to content

Use with an AI Agent

AgentsForms is designed to be driven by AI agents. An agent detects it needs human input, creates a form session, sends the URL to the user, and resumes when the webhook fires with validated data.

Agent detects missing info
→ Creates a session (POST /v1/forms/:id/sessions)
→ Sends the hosted form URL to the human
→ Human fills the form
→ Validated submission arrives
→ Signed webhook fires
→ Agent resumes with structured data

The integration surface is HTTP + webhooks. Three approaches follow.


The simplest integration. Your agent code makes HTTP calls to the AgentsForms API.

Terminal window
curl -X POST https://api.agentsforms.com/v1/forms/form_abc123/sessions \
-H "Authorization: Bearer $AGENT...EY" \
-H "Content-Type: application/json" \
-d '{
"expires_in": 3600,
"prefill": { "email": "user@example.com" },
"metadata": { "thread_id": "thread_42" }
}'
Terminal window
curl -X GET "https://api.agentsforms.com/v1/forms/form_abc123/submissions?session_id=sess_xyz789" \
-H "Authorization: Bearer $AGENT...-H "Accept: application/json"

Or better: set up a webhook. Polling adds latency and cost. A webhook pushes the submission to your agent the moment the human submits.

When your agent receives a webhook, verify the HMAC signature:

import { createHmac } from 'node:crypto';
function verifyWebhook(body: string, signature: string, secret: string): boolean {
const expected = createHmac('sha256', secret).update(body).digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(`sha256=${expected}`)
);
}

In LangGraph, AgentsForms fits the human-in-the-loop interrupt pattern.

from langgraph.graph import StateGraph, START, END
import httpx
class AgentState(TypedDict):
thread_id: str
session_url: str | None
submission: dict | None
async def detect_missing_info(state: AgentState):
# Your agent logic: determine what information is needed
if needs_human_input(state):
resp = await httpx.Client().post(
"https://api.agentsforms.com/v1/forms/form_abc123/sessions",
headers={"Authorization": f"Bearer {API_KEY}"},
json={"metadata": {"thread_id": state["thread_id"]}}
)
session = resp.json()["session"]
return {"session_url": session["url"]}
return {}
async def resume_on_webhook(state: AgentState):
# Your webhook handler populates state["submission"]
# Resume the graph from the interrupt point
...
graph = StateGraph(AgentState)
graph.add_node("detect", detect_missing_info)
graph.add_node("resume", resume_on_webhook)
graph.add_edge(START, "detect")
graph.add_conditional_edges("detect", lambda s: "resume" if s["session_url"] else END)
graph.add_edge("resume", END)
  1. The LangGraph node creates a session and returns the hosted URL.
  2. Your agent sends that URL to the user via its existing channel (Slack, email, chat).
  3. A webhook handler receives the submission and resumes the graph.

Define AgentsForms as a function that your agent can call:

from agents import Agent, function_tool, Runner
@function_tool
async def request_human_input(form_slug: str, prefill: dict = {}) -> str:
"""Request missing information from a human. Returns a URL to send them."""
# Look up form_id by slug, create a session
resp = await httpx.Client().post(
"https://api.agentsforms.com/v1/forms/form_abc123/sessions",
headers={"Authorization": f"Bearer {API_KEY}"},
json={"prefill": prefill}
)
session = resp.json()["session"]
return session["url"]
agent = Agent(
name="SupportBot",
instructions="Collect user info before opening a ticket.",
tools=[request_human_input],
)
result = await Runner.run(agent, "I need help with billing")

The agent calls request_human_input, receives the URL, presents it to the human in the chat interface, and the webhook resumes the conversation.


During development, use a tunneling service to expose your local webhook endpoint:

Terminal window
# Terminal 1: Start your agent
python agent.py
# Terminal 2: Expose your webhook endpoint
ngrok http 3000

Then register the ngrok URL as a webhook:

Terminal window
agentsforms webhooks create \
--url https://abc123.ngrok.io/webhook \
--events submission.created