Getting started
This walks through one workflow from a file on disk to a running session: an expense claim that an employee submits and a manager reviews.
1. Install
bash
bun add @clipless/reactThe package holds the components, the hooks and the clipless CLI.
2. Write the workflow
A workflow is one file. Stages are the people involved (each has an actor role), steps are pages within a stage, fields are the answers.
tsx
// expense-claim.tsx
import { Field, Stage, Step, Workflow, type WorkflowProps, useWorkflowNav } from '@clipless/react'
function Nav() {
const { next, canGoNext, isSubmitting } = useWorkflowNav()
return (
<button type="button" disabled={!canGoNext || isSubmitting} onClick={next}>
Continue
</button>
)
}
export default function ExpenseClaim(props: Partial<WorkflowProps>) {
return (
<Workflow id="expense_claim_v1" version="1.0.0" {...props}>
<Stage name="claim" actor="role:employee" label="Your claim">
<Step name="details">
<Field name="description" type="string" label="What was it for?" required />
<Field name="amount" type="number" label="Amount (USD)" required min={1} />
<Field name="receipt" type="file" label="Receipt" accept=".pdf,image/*" required />
</Step>
</Stage>
<Stage name="review" actor="role:manager" label="Manager review">
<Step name="decision">
<Field
name="decision"
type="select"
label="Decision"
required
options={[{ value: 'Approve' }, { value: 'Reject' }]}
/>
</Step>
</Stage>
<Nav />
</Workflow>
)
}A <Field> without children renders a default input for its type. Give it children to use your own components — see Components.
next() moves to the next step, and on the last step submits the stage.
3. Run it in the browser
Without a token, <Workflow> runs locally: the whole engine runs in the browser, nothing is saved. It is how you build and try a workflow.
tsx
<ExpenseClaim actor={{ role: 'role:employee' }} />Only the stage the current actor owns renders. To play the reviewer after submitting the claim, switch the actor on the store — onStoreReady hands it to you:
tsx
<ExpenseClaim
actor={{ role: 'role:employee' }}
onStoreReady={(store) => (window.store = store)}
/>
// later: store.setActor({ role: 'role:manager' })The playground does this with an actor switcher, and shows the ledger and state beside the form. Run it locally with bun dev:playground.
4. Deploy it
Deploying reads the workflow out of your file and sends it to the engine.
bash
export CLIPLESS_URL=https://… # the engine
export CLIPLESS_API_KEY=fd_test_… # a secret key for your test environment
bunx clipless deploy expense-claim.tsxEach deploy is kept: sessions already running stay on the version they started with.
5. Start a session from your backend
A session is one run of the workflow. Your backend starts it with the API key and gets a token for the first actor:
bash
curl -X POST "$CLIPLESS_URL/api/v1/workflows/expense_claim_v1/sessions" \
-H "authorization: Bearer $CLIPLESS_API_KEY" \
-H 'content-type: application/json' \
-d '{ "role": "role:employee", "externalUserId": "user_42" }'json
{ "session": "Xk2…", "token": "eyJ…", "created": true, "state": { … } }externalUserId is your own id for the person; the engine only stores it.
6. Render with the token
Pass the token to the same component. <Workflow> now talks to the engine: the server validates, runs actions and routes; the browser renders.
tsx
<ExpenseClaim runtimeToken={token} serverUrl={CLIPLESS_URL} />The token is scoped to one session and one role. Never send the API key to a browser.
7. Hand off to the next person
When the employee submits, the session moves to the manager's stage. Mint the manager a token and give it to them however your app does that — an email link, their dashboard:
bash
curl -X POST "$CLIPLESS_URL/api/v1/sessions/Xk2…/tokens" \
-H "authorization: Bearer $CLIPLESS_API_KEY" \
-H 'content-type: application/json' \
-d '{ "role": "role:manager", "externalUserId": "mgr_7" }'A browser that has the session open hears it move (another actor, a provider callback) and updates itself.
8. See what happened
bash
bunx clipless sessions expense_claim_v1 # what is in flight
bunx clipless session expense_claim_v1 Xk2… # one session, event by event9. Ship one bundle per role
The workflow file describes everyone's stages and every action. No browser needs all of it:
bash
bunx clipless build expense-claim.tsx --out dist/cliplesswrites expense_claim_v1.employee.js and expense_claim_v1.manager.js — each with only that role's stages and none of the action settings. See CLI.
Next
- Concepts — what a stage, an actor and the ledger are, precisely.
- Actions — email the manager when a claim arrives, run a check, generate a PDF.
- Loop-backs and rounds — let the manager send the claim back.