Farcaster Observability
Every call already left a record.
The proxy captures as it forwards — status, mode, timing, sizes, upstream status. This surface is the read layer over data the gateway was writing anyway: list it, inspect one, or aggregate it, without standing up anything else to hold it.
Reading it back
Filter first, then open one.
The list endpoint is tenant-scoped, paginated, and filterable by agent, status, mode, error class, model, settlement, and an RFC 3339 time window. It answers "what went wrong last night" without you fetching everything and grepping.
The failures, last 20
$ curl localhost:8080/v1/invocations\ ?agent_id={id}&status=failed&limit=20 ← bodies are never returned on the list endpoint, at any scope
One record, in full
$ curl localhost:8080/v1/invocations/{id} { "status": "failed", "error_class": "upstream_5xx", "mode": "streaming", "ttft_ms": null, "body_captured": false }
The mechanic
Bodies take two keys, not one.
Request and response bodies are the most sensitive thing the gateway touches, so reading them requires two independent decisions made by two different people — and neither one alone is enough.
| Key | What it is | Held by |
|---|---|---|
| Capture | An agent-level toggle, off by default — if it was off, there is nothing to read | Whoever configures the agent |
| Scope | The invocations:read_body OAuth scope. Metadata reads don't need it |
Whoever issues the token |
body_captured tells you the truth either way. It reflects whether
capture was on for that invocation regardless of whether you can read the
bodies — so an auditor can tell "nothing was recorded" apart from "something was
recorded and you can't see it," which are very different answers. When bodies are
returned they're base64-encoded and capped at 1 MiB.
Three fields
The ones that aren't obvious.
Status, latency and sizes are table stakes. These three carry the decisions.
| Field | What it holds | The catch |
|---|---|---|
ttft_ms |
Time to first byte on the streaming path | null for transactional calls, or when no bytes ever streamed |
error_class |
A coarse taxonomy: timeout, upstream_5xx,
upstream_4xx, ssrf_blocked,
credential_error, cancelled, internal
|
null on success. Coarse on purpose — it's for grouping, not for forensics |
model |
Whatever the caller sent in X-Farcaster-Model |
Caller-supplied only. Farcaster proxies arbitrary upstreams, not a known LLM wire format, so it will not read your bodies to guess. MCP is the one exception, because its envelope is a published shape |
Aggregates
Percentiles, with the guardrails showing.
Group by agent and bucket by hour or day, and get count, error rate, a breakdown by error class, and p50/p95/p99 for both latency and time-to-first-byte. The limits below are deliberate, and the endpoint says so rather than degrading quietly.
$ curl localhost:8080/v1/analytics\
?group_by=agent_id&bucket=day&since=2026-07-01T00:00:00Z
| Rule | What happens | Why |
|---|---|---|
since is required |
An unbounded query is 400 |
There is no default window — a query nobody bounded is a query nobody meant |
| The window caps at 31 days | Wider is 400 |
Percentile aggregation over an unbounded range is how a read endpoint takes down a write path |
| Its own rate limit | 429 here only |
Tighter than the general /v1 limiter, and separate — being throttled here doesn't mean the rest of the API is |
| An empty window | 200 with an empty groups array |
Not 404. "Nothing happened" is a valid answer, not a missing resource |
| In-flight calls | Counted, but excluded from percentiles | A call that hasn't finished has no latency yet, and pretending otherwise would drag every p50 down |
Every query filters by tenant first. An invocation ID belonging to another
tenant returns 404, never 403, and the analytics endpoint
only ever aggregates your own tenant's calls.
Where the line falls
The capture is free. The dashboard is the product.
Raw invocation and payment capture, readable over the API, is Apache-2.0 — the data is yours and it never leaves your database. What we sell is what a finance team wants done with it.
| Capability | Tier |
|---|---|
| Raw invocation and payment capture, readable over the API | OSS |
| Aggregate analytics — counts, error rates, latency and TTFT percentiles | OSS |
| Usage and revenue dashboard, quotas, spend limits, invoicing | Commercial |
One gap, stated plainly: there is no OTLP / OpenTelemetry export yet. Metrics are read from the gateway's own API today, not pushed to Datadog or Grafana. That's a deliberate phase-two deferral rather than a disagreement about whether it matters — if you're already standardised on OTel, ask us where it is before you build on this.
Next
You can see every call. Now charge for one.
The last surface is the one that turns a record of what happened into a reason it was allowed to happen at all.