Farcaster Routing & Proxy
In the path, not in the way.
A caller invokes the agent through Farcaster instead of calling the upstream directly. Farcaster resolves the upstream, injects the agent's credential, forwards the body untouched, and records what happened. The upstream sees its own protocol — not ours.
Two modes
Hold the connection, or don't.
The same agent is reachable two ways, and the caller picks by use case rather than
by configuration. Both return an inv_ ID, so a call is addressable in
observability no matter which one you used.
Transactional
$ curl localhost:8080/v1/proxy/{id} \ -H "Authorization: Bearer $TOKEN" -d '{}' 202 Accepted X-Farcaster-Invocation-ID: inv_019842… { "invocation_id": "inv_019842…" }
Returns immediately, runs the upstream call server-side, and you poll
/v1/proxy/{id}/invocations/{inv_id} for the result. Suits long
thinking flows where holding a connection open buys you nothing.
Streaming
$ curl -N localhost:8080/v1/proxy/{id}/stream \ -H "Authorization: Bearer $TOKEN" -d '{}' ← request and response stream through verbatim, with no buffering
A long-lived connection for token-by-token responses or hefty payloads. The invocation ID is set on the response before any upstream byte is written, whatever the outcome — so a call is traceable even when it fails mid-stream.
The mechanic
There is no envelope to unwrap.
Farcaster forwards the caller's request body unmodified. No wrapper, no re-encoding, no schema of ours in the middle. An upstream built before Farcaster existed needs no change to sit behind it, and moving off Farcaster does not mean unpicking a format.
| Header | What it does | Set by |
|---|---|---|
X-Request-ID |
Correlates the call end to end. Yours is honoured; if absent, one is injected | Caller or gateway |
X-Farcaster-Model |
Optional label recorded on the invocation, capped at 256 bytes | Caller |
Authorization |
Always stripped. The upstream never sees the caller's token | Gateway |
Headers forward through a block-list, not a pass-all. That is the deliberate direction: the caller's OAuth token is removed on the way out, and the agent's own credential is injected in its place — so the upstream is authenticated as your integration, never as whoever happened to call you.
The transactional endpoint caps request bodies at 32 MiB. The streaming endpoint has no cap, because it never buffers.
Policy
A call you refuse is never asked to pay.
Policy is evaluated inline on the proxy path, after the call is parsed and
before the payment gate. The ordering is the point: a request your policy
denies gets a 403, not a 402 followed by a
403 once they've signed for it.
| Action | What happens | Recorded |
|---|---|---|
allow |
Forwarded unchanged — also the behaviour when nothing matches and the policy's default is allow | Yes |
warn |
Forwarded, plus an X-Farcaster-Policy-Warning header on the transactional response |
Yes |
deny |
Rejected with 403 and a single coarse reason. Never forwarded |
Yes |
The no-match and on-error actions are read from your policy, not hardcoded. You decide whether an unevaluable call fails open or closed — we don't decide it for you. And a tenant with no policy configured sees zero change: calls proxy exactly as they did before this surface existed.
When you get it wrong
Refused early, and without a map of your network.
The checks that can be made before an upstream call is attempted are made before an upstream call is attempted. What comes back says what happened without describing where anything lives.
| What you did | What you get | Why |
|---|---|---|
Invoked a pending, suspended, or soft-deleted agent |
422 |
Decided from the catalog record, before any upstream call is made |
| Pointed at an upstream that won't answer | 502 |
Transient failures are retried and a circuit breaker opens on an upstream that keeps failing, so one bad host doesn't become your latency |
| Pointed at an upstream that answers too slowly | 504 |
|
| Sent a call your policy denies | 403 |
One coarse reason — a precise one would let a caller map your rules by probing |
| Read any error body above | No internals | Error bodies never carry upstream addresses or internal detail |
One limit worth knowing before you rely on it. If the policy store is
unreachable — a database timeout, say — your configured on-error posture can't be
read, so it can't be honoured. The gateway fails closed and returns 500
for the duration. That's the safe direction, and it's a documented interim posture
rather than a surprise waiting for you in production.
Where the line falls
The whole traffic path is free.
Routing is not the part we sell. Everything that carries a request from your caller to your upstream and back is Apache-2.0.
| Capability | Tier |
|---|---|
| Transactional and streaming proxy, verbatim body forwarding | OSS |
| Credential injection, SSRF guarding, retries and the circuit breaker | OSS |
| Policy evaluation on the proxy path | OSS |
| Governance and a control plane across tenants | Commercial |
Next
The proxy forwards bytes. MCP lets it read them.
Declaring an agent's protocol as MCP keeps everything on this page and adds what a protocol-agnostic pipe structurally cannot have: method- and tool-level visibility, and a retry rule that knows which calls are safe to repeat.