Keep this reference nearby while implementing or debugging the three /api/copilot/* routes.
Route files
Create these route files:
app/api/copilot/run/route.ts
app/api/copilot/stream/route.ts
app/api/copilot/webhook/route.tsRequest body for /api/copilot/run
{
"sessionId": "reference-session-001",
"message": "Summarize the highest-risk files in this workspace."
}Required fields:
sessionIdmessage
Optional field:
eventType
Request body for /api/copilot/stream
The stream route uses the same request body:
{
"sessionId": "reference-session-001",
"message": "Stream progress while you inspect the workspace."
}Only the response changes: the stream route returns incremental events instead of waiting for one terminal result.
Response headers
The route helpers return:
x-kestrel-request-id
x-kestrel-correlation-idThe stream route also returns:
content-type: text/event-stream; charset=utf-8
cache-control: no-cache, no-transform
connection: keep-aliveStream cancellation behavior
When the browser closes the connection:
client disconnect
-> Response stream cancel()
-> stream.cancel()
-> upstream run cancellation
-> terminal result becomes run.cancelledThis leaves the run with an accurate cancelled outcome when a user closes the connection.
Webhook payload mapping
Use the webhook route to translate an external service's payload into the fields Kestrel expects.
Example payload mapping:
mapPayload(payload) {
return {
sessionId: payload.sessionId ?? "reference-session-001",
message: payload.prompt,
};
}Use the webhook helper when:
- another service already owns the payload schema
- the caller is not a browser
- you want the app server to normalize the inbound event before Kestrel sees it
When to choose each route
| Route | Choose it when | Avoid it when |
|---|---|---|
/api/copilot/run | caller wants one terminal response | caller expects live progress |
/api/copilot/stream | browser needs incremental output | caller cannot hold an SSE connection |
/api/copilot/webhook | payload comes from another system | the caller can already use the normal run route |
Debugging checklist
- route file imports the shared
workspaceCopilotAgent - route resolves server-side context, not browser-supplied actor data
-
sessionIdis present in the inbound body or mapped payload - response includes
x-kestrel-request-id - stream route surfaces
run.cancelledwhen the client disconnects