Custom agents guide
Install, instrument, the event methods, a worked example, and common problems.
Using a framework?
LangChain, CrewAI, LlamaIndex and Pydantic AI instrument themselves with one call.
Install
failproofai-sdk and imported in Python as failproofai_sdk. Framework extras such as failproofai-sdk[langgraph] install the framework itself; the adapters always ship in the base wheel.
Connect the Failproof daemon
- Dashboard
- CLI
-
Go to Admin → Keys and create a key with
events:add. - Connect the Failproof daemon to Cloud on the agent machine.
- Run one instrumented session, then find its exact ID under Observe → Events.
-
Go to Observe → Sessions, select the same environment, and open the reconstructed trace.

Configuration
Set by environment variable instead:
Events are queued in memory and written in the background every
flush_interval seconds, with a final flush at interpreter exit. A process killed outright loses whatever had not been written yet.
Identity
Every event belongs to a session and an agent. The scopes fill both in, so you rarely pass them:session_id or agent_id explicitly still works and wins. With neither bound nor passed, the call raises TypeError rather than emitting an event Cloud would quietly discard.
Identity rides on context variables. It follows
asyncio tasks automatically, but not new threads — wrap a worker in failproofai_sdk.propagate() or its events land unattached.Event catalog
Fifteen methods. Most come in pairs — you call the opener, then the closer, and the SDK times the gap.
Three stand alone:
error, human_pause, human_interrupt.
Every field, per method
Every field, per method
Every method also takes
session_id and agent_id, which the scopes fill in for you. Anything left as None is dropped rather than sent as JSON null, and every method returns None.Pairing and duration
One rule: give the closing event the same id as its opener. That is what pairs them, and what lets the SDK time the gap.
Do not pass
duration_ms yourself. The SDK measures it, and passing it raises ValueError.
The one exception is model_response, where only you know the real provider latency. Pass a whole number of milliseconds — a float raises, because the column is a 32-bit integer and would otherwise land empty.
Edge cases
Edge cases
- Ids only need to be unique per kind, per session. A tool call and a hook can share one; two sessions running at once can reuse the same ids without colliding.
- They are not scoped to an agent. A pair opened under one agent and closed under another still matches — which is the normal case in multi-agent code.
request_idis optional but recommended. Without it, model events pair up in the order they arrive, so two concurrent calls in the same agent can mispair.- A pair split across processes still matches in Cloud, but the SDK cannot time it — nothing in either process saw both halves.
- At most 10,000 openers wait for a closer at once. Past that the oldest is dropped, so a leak cannot grow without bound.
Your own fields
Any extra keyword you pass is stored with the event:Decimal, a set, bytes, a model object — is stored as a string.
These five names are reserved and rejected outright: timestamp, session_id, agent_id, type, environment.
Deliver and verify
- Dashboard
- CLI
In Observe → Events, verify
agent_start exists first and agent_end exists last. Then open Observe → Sessions and confirm model, tool, human, hook, and error events appear in the intended order. Use the session ID as the primary troubleshooting key.$FAILPROOFAI_HOME/custom-agents/events, otherwise ~/.failproofai/custom-agents/events. JSONL files prove SDK emission; a growing spool points to daemon configuration or delivery, while an empty spool points to instrumentation or process lifetime.
Inspect the spool only when the daemon is stopped. While it runs, it collects and deletes each batch within milliseconds, so a directory listing races the collector and shows far fewer events than were emitted.

