HTTP routes
defineHttpRoutes, authentication, HttpContext.startWorkflow, input and idempotency semantics.
HTTP routes are trusted project code outside durable workflow execution. Use them
to validate requests and accept a workflow run. Import all types and
defineHttpRoutes from @laufwerk/sdk/http.
defineHttpRoutes(routes)
Accepts readonly HttpRoute[] and returns it unchanged. Default-export the result
from laufwerk/http.ts. Validation happens when the server loads the routes.
HttpRoute
| Property | Type | Required | Meaning |
|---|---|---|---|
method | string | Yes | Uppercase HTTP method, e.g. "POST" |
path | string | Yes | Exact absolute pathname, e.g. "/triggers/review" |
auth | "owner" | "public" or authentication function | Yes | Access policy |
handler | (request: Request, context: HttpContext) => Response | Promise<Response> | Yes | Standard Web Request/Response handler |
Paths are exact matches, not parameter patterns. A path must start with one /,
contain no query, fragment, percent escape or backslash, and not normalize to a
different pathname. Reserved prefixes are /api, /auth, /healthz, /brand,
/assets. Reserved exact paths are /, /index.html, /client.js, /styles.css,
/client.css, /favicon.ico. Duplicate method/path pairs are rejected.
Authentication
| Value | Behavior |
|---|---|
"owner" | Requires the server's owner bearer token; a Studio cookie alone does not authenticate custom owner routes |
"public" | No route authentication |
| Function | (request) => void | Response | Promise<void | Response>; return void to allow or a Response to reject |
If custom authentication reads the body, read request.clone() so the handler
can still consume the original. Return a rejection response, not false.
context.startWorkflow(input)
HttpContext has one method, startWorkflow. Its argument is HttpWorkflowStart:
| Property | Type | Required | Meaning |
|---|---|---|---|
workflowName | string | Yes | Registered workflow name |
input | unknown | Yes | JSON-compatible input matching the workflow's payload schema |
idempotencyKey | string | Yes | Nonblank submission key scoped to this HTTP method/path |
admission | { groupId: string } | No | Register in the admission queue for later dispatcher handling |
Returns Promise<{ runId: string; duplicate: boolean }> after acceptance commits.
It does not wait for the workflow result. Each call commits independently; a
later handler error does not roll back a run already accepted.
The HTTP submission key and workflow's idempotencyKey(input) are distinct
identities. Retrying the same submission with the same data reuses acceptance;
conflicting reuse is rejected. Input must contain only JSON values; functions,
undefined, nonfinite numbers and nonplain objects are not valid submission data.
Example: authenticated entry point
import { defineHttpRoutes } from "@laufwerk/sdk/http";
export default defineHttpRoutes([{
method: "POST",
path: "/triggers/review",
auth: "owner",
async handler(request, context) {
const idempotencyKey = request.headers.get("idempotency-key");
if (!idempotencyKey?.trim()) {
return new Response("Missing Idempotency-Key", { status: 400 });
}
let input: unknown;
try { input = await request.json(); }
catch { return new Response("Expected JSON", { status: 400 }); }
const accepted = await context.startWorkflow({
workflowName: "review", input, idempotencyKey,
});
return Response.json(accepted, { status: 202 });
},
}]);Define a matching review workflow separately. Serve routes with laufwerk serve;
--studio also serves Studio. Owner routes require a configured owner token.
An HTTP 202 response means accepted, not completed or successful. Inspect the
returned run ID through CLI/Studio.