build

Next.js route cookbook

Copy the request bodies, response headers, cancellation behavior, and webhook mapping for a Kestrel-backed Next.js app.

Next.jsintermediate0.7.0 Stable
Verified 2026-03-20View sourceReport a docs issue

Keep this reference nearby while implementing or debugging the three /api/copilot/* routes.

Route files

Create these route files:

Text
app/api/copilot/run/route.ts
app/api/copilot/stream/route.ts
app/api/copilot/webhook/route.ts

Request body for /api/copilot/run

JSON
{
  "sessionId": "reference-session-001",
  "message": "Summarize the highest-risk files in this workspace."
}

Required fields:

  • sessionId
  • message

Optional field:

  • eventType

Request body for /api/copilot/stream

The stream route uses the same request body:

JSON
{
  "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:

Text
x-kestrel-request-id
x-kestrel-correlation-id

The stream route also returns:

Text
content-type: text/event-stream; charset=utf-8
cache-control: no-cache, no-transform
connection: keep-alive

Stream cancellation behavior

When the browser closes the connection:

Text
client disconnect
  -> Response stream cancel()
    -> stream.cancel()
      -> upstream run cancellation
        -> terminal result becomes run.cancelled

This 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:

TypeScript
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

RouteChoose it whenAvoid it when
/api/copilot/runcaller wants one terminal responsecaller expects live progress
/api/copilot/streambrowser needs incremental outputcaller cannot hold an SSE connection
/api/copilot/webhookpayload comes from another systemthe 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
  • sessionId is present in the inbound body or mapped payload
  • response includes x-kestrel-request-id
  • stream route surfaces run.cancelled when the client disconnects

Use these routes in an application