Pass options to routeAgentRequest() as the third argument. Their type is AgentOptions, exported from agents/routing:
| Option | Type | Description |
|---|---|---|
prefix |
string |
URL segments before the binding and name. Defaults to agents. |
cors |
boolean | HeadersInit |
Adds CORS headers to matched responses and answers matched OPTIONS preflight requests. |
jurisdiction |
DurableObjectJurisdiction |
Creates objects in a jurisdiction, such as "eu". |
locationHint |
DurableObjectLocationHint |
Hints where to create objects, such as "enam". |
props |
object |
JSON-serializable values passed to onStart(props) when the request starts the object. |
routingRetry |
false | RoutingRetryOptions |
Retries transient Durable Object errors. Enabled by default. |
onBeforeConnect |
(request, route) => Response | Request | void |
Runs in the Worker before a WebSocket upgrade is forwarded. |
onBeforeRequest |
(request, route) => Response | Request | void |
Runs in the Worker before an HTTP request is forwarded. |
onBeforeConnect and onBeforeRequest are covered in Authentication.
prefix replaces the agents segment. It can contain several segments:
import { routeAgentRequest } from "agents/routing";
export default {
async fetch(request, env) {
// Matches /api/v1/my-agent/alice
return (
(await routeAgentRequest(request, env, { prefix: "api/v1" })) ??
new Response("Not found", { status: 404 })
);
},
};import { routeAgentRequest } from "agents/routing";
export default {
async fetch(request: Request, env: Env): Promise<Response> {
// Matches /api/v1/my-agent/alice
return (
(await routeAgentRequest(request, env, { prefix: "api/v1" })) ??
new Response("Not found", { status: 404 })
);
},
} satisfies ExportedHandler<Env>;useAgent and AgentClient build /agents/... paths. With another prefix, pass the full path as their basePath option, such as basePath: "api/v1/my-agent/alice".
Set cors: true when your frontend runs on another origin. It adds these headers to every matched HTTP response:
Access-Control-Allow-Origin: *Access-Control-Allow-Methods: GET, POST, HEAD, OPTIONSAccess-Control-Allow-Headers: *Access-Control-Max-Age: 86400
For credentialed requests, pass your own headers with a specific origin:
import { routeAgentRequest } from "agents/routing";
export default {
async fetch(request, env) {
return (
(await routeAgentRequest(request, env, {
cors: {
"Access-Control-Allow-Origin": "https://app.example.com",
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type, Authorization",
"Access-Control-Allow-Credentials": "true",
},
})) ?? new Response("Not found", { status: 404 })
);
},
};import { routeAgentRequest } from "agents/routing";
export default {
async fetch(request: Request, env: Env): Promise<Response> {
return (
(await routeAgentRequest(request, env, {
cors: {
"Access-Control-Allow-Origin": "https://app.example.com",
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type, Authorization",
"Access-Control-Allow-Credentials": "true",
},
})) ?? new Response("Not found", { status: 404 })
);
},
} satisfies ExportedHandler<Env>;Matched OPTIONS requests get the CORS headers without reaching the object. Responses that onBeforeRequest returns also get them. WebSocket upgrade responses never do.
jurisdiction and locationHint apply to every object the call routes to:
import { routeAgentRequest } from "agents/routing";
export default {
async fetch(request, env) {
return (
(await routeAgentRequest(request, env, {
jurisdiction: "eu",
locationHint: "weur",
})) ?? new Response("Not found", { status: 404 })
);
},
};import { routeAgentRequest } from "agents/routing";
export default {
async fetch(request: Request, env: Env): Promise<Response> {
return (
(await routeAgentRequest(request, env, {
jurisdiction: "eu",
locationHint: "weur",
})) ?? new Response("Not found", { status: 404 })
);
},
} satisfies ExportedHandler<Env>;An object in a jurisdiction has a different ID from an object with the same name outside it. Use the same jurisdiction every time you route to an object. The location hint only applies when the object is first created. For the available values, refer to Data location.
props passes values from the Worker to the object's startup. Each capability's onStart() hook and the host's onStart(props) receive them. Type them with the second type argument of Lifecycle.install():
import { DurableObject } from "cloudflare:workers";
import { Lifecycle } from "agents/lifecycle";
import { routeAgentRequest } from "agents/routing";
export class MyAgent extends DurableObject {
lifecycle = Lifecycle.install(this);
#plan = "free";
onStart(props) {
this.#plan = props?.plan ?? "free";
}
onRequest() {
return Response.json({ plan: this.#plan });
}
}
export default {
async fetch(request, env) {
const userId = request.headers.get("x-user-id") ?? "anonymous";
return (
(await routeAgentRequest(request, env, {
props: { userId, plan: "pro" },
})) ?? new Response("Not found", { status: 404 })
);
},
};import { DurableObject } from "cloudflare:workers";
import { Lifecycle } from "agents/lifecycle";
import { routeAgentRequest } from "agents/routing";
type Props = { userId: string; plan: "free" | "pro" };
export class MyAgent extends DurableObject<Env> {
readonly lifecycle = Lifecycle.install<Env, Props>(this);
#plan: Props["plan"] = "free";
onStart(props?: Props): void {
this.#plan = props?.plan ?? "free";
}
onRequest(): Response {
return Response.json({ plan: this.#plan });
}
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const userId = request.headers.get("x-user-id") ?? "anonymous";
return (
(await routeAgentRequest(request, env, {
props: { userId, plan: "pro" },
})) ?? new Response("Not found", { status: 404 })
);
},
} satisfies ExportedHandler<Env>;Props only reach a cold object. A warm object has already run startup and ignores them. Do not keep anything in props that you need on every request. Store it, or send it with each request.
The same props go to whichever object the URL matches. To start an object over RPC with props, call lifecycle.start(props). For more information, refer to Call an object over RPC.
routingRetry retries Durable Object errors that the platform marks as retryable and not caused by overload. It backs off exponentially, with jitter. Its type is RoutingRetryOptions:
| Field | Default | Description |
|---|---|---|
maxAttempts |
3 |
Attempts, including the first |
baseDelayMs |
100 |
Base delay for the backoff, in milliseconds |
maxDelayMs |
800 |
Maximum delay between attempts, in milliseconds |
onRetry |
None | Called before each retry with the error, attempt number, and delay |
import { routeAgentRequest } from "agents/routing";
export default {
async fetch(request, env) {
return (
(await routeAgentRequest(request, env, {
routingRetry: {
maxAttempts: 5,
onRetry: ({ attempt, delayMs, name }) => {
console.warn(
`Retrying ${name}, attempt ${attempt} in ${delayMs} ms`,
);
},
},
})) ?? new Response("Not found", { status: 404 })
);
},
};import { routeAgentRequest } from "agents/routing";
export default {
async fetch(request: Request, env: Env): Promise<Response> {
return (
(await routeAgentRequest(request, env, {
routingRetry: {
maxAttempts: 5,
onRetry: ({ attempt, delayMs, name }) => {
console.warn(
`Retrying ${name}, attempt ${attempt} in ${delayMs} ms`,
);
},
},
})) ?? new Response("Not found", { status: 404 })
);
},
} satisfies ExportedHandler<Env>;Pass routingRetry: false to turn retries off.