Skip to main content
A workflow is one file in an app’s src/api/ directory that default-exports createEndpoint from zitejs/backend: server-side logic with typed inputs and outputs.
Each workflow must be a default export, and the filename is the workflow id, a single segment matching ^[a-zA-Z0-9_-]+$ (no subfolders under src/api/).

Config

Context

execute receives a context: id and email are always present when a user is signed in. name and image are never sent to a workflow. If the app uses user sync, context.user is the synced row rather than the identity above: id is that record’s id, and the row’s own columns are what you read off it.
context.user can be null even where the type says it cannot. The type only widens to allow null on workflows that declare a schedule or webhook. At runtime it is also null for an anonymous caller on a workflow without authenticated: true, and when user sync is on but the caller has no synced row yet. Reading context.user.email there compiles cleanly and throws. Set authenticated: true when you need a user, or null-check regardless of the type.

Roles

When roles are on, the runtime also sets context.user.roles to an array of { id, name }, covering the caller’s custom roles plus the built-ins they match. It is [] when roles are off, and absent only when user is null.
roles is not in the generated User type, so context.user.roles does not typecheck on most apps. Cast it until the type catches up: (context.user as { roles?: { id: string; name: string }[] }).roles.
process.env.ZITE_APP_URL is set to your app’s public URL. Use it to build links instead of hardcoding.

Errors

Throw ZiteError to return a controlled error. The platform maps its code to an HTTP status:
A positional form, new ZiteError('Not found', 'NOT_FOUND'), also works and defaults the code to 'INTERNAL_ERROR', but the object form above is preferred. Anything that is not a ZiteError, including an error carrying only a numeric statusCode, returns a generic 500.

Calling workflows from the frontend

Workflows are called through a generated, typed client (like tRPC). Each workflow exports a camel-cased caller plus its input/output types:
After adding, renaming, or deleting a workflow file, regenerate the client with npx zitejs generate from the workspace root (the build tools do this for you). Workflows are also reachable publicly at POST /public/{flowId}/api/{name}.

Streaming

Set stream: true and write chunks as you go:
On the frontend, streaming workflows are called with the generated streaming caller, which yields chunks and resolves a final result.