> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bijection.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Business processes

> Write durable business sequences in code and operate them from the terminal

A process combines native business operations with a customer-owned workflow
runner. Bijection owns accepted requests, exact proposals, authorization and
external-call evidence. The runner owns workflow history, concurrency, retries
and durable waits. The SDK adapter connects these owners without introducing a
second business-operation ledger.

## Create a headless project

```sh theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
bijection create fulfillment --template process
cd fulfillment
pnpm generate
pnpm check:release
bijection dev
```

The supported package is the `bijection` SDK bundled with the installed native
CLI release. The starter's `package.json` selects that exact SDK tarball and
Temporal SDK version `1.24.0`; use `pnpm list --depth 0` to inspect installed
versions. Install the native CLI separately from npm. The optional Workflow
component has no supported Bijection package to install.

The starter includes a native backend, typed workflow interface, worker,
terminal start script, approval operation, process inspector, branch scenario
and portable tests. It needs no frontend or optional Workflow component.
Development preparation also regenerates the configured interface before
ordinary API discovery.

Configure your [application identity provider](/auth/team-members). In the
backend, set `FULFILLMENT_WORKER` to the service's verified `tokenIdentifier`
and `FULFILLMENT_REVIEWERS` to a JSON array of independent reviewers'
identifiers. The example is a shared team application; add tenant-scoped read
rules before using it for separate customers.

Grant the service `read` and `invoke` for `orders:prepare` and `orders:settle`.
Grant reviewers `read` on those operations for inspection, and `read`, `preview`
and `invoke` on `orders:approve`. These native grants and the application role
checks both apply. For example, from a deployment administrator's terminal:

```sh theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
bijection admin permissions set 'issuer|worker' read true --operation orders:prepare
bijection admin permissions set 'issuer|worker' invoke true --operation orders:prepare
```

Repeat for the other operation grants. Platform administrator access alone does
not make a caller the worker or a business reviewer.

On customer worker infrastructure, configure:

| Variable | Meaning |
| - | - |
| `BIJECTION_URL` | Application deployment URL |
| `BIJECTION_TOKEN_FILE` | Private file containing the service's current application token |
| `BIJECTION_PROCESS_OWNER` | Stable journal namespace for that service |
| `BIJECTION_PROCESS_JOURNAL` | Private durable directory shared by every worker recovering these processes |
| `TEMPORAL_ADDRESS`, `TEMPORAL_NAMESPACE` | Customer Temporal service and namespace |
| `TEMPORAL_API_KEY` | Temporal credential, or the worker's configured mTLS files |
| `TEMPORAL_CLIENT_CERT`, `TEMPORAL_CLIENT_KEY` | Paired mTLS certificate and key files, used by both start and worker |
| `TEMPORAL_TASK_QUEUE` | Shared start/worker task queue; defaults to `fulfillment` |
| `FULFILLMENT_SHIPPING_URL`, `FULFILLMENT_SHIPPING_KEY` | Customer shipping API and credential |

The token file must be refreshed by your identity integration. An activity reads
it again before opening its native request. Journal storage must survive worker
replacement; an ephemeral container directory does not satisfy that contract.

The shipping activity is customer-owned. Its destination must honor the process
ID as an idempotency key for the full recovery lifetime. Its retries use that
same key. For a destination without this guarantee, use a native integration
command with the destination's declared delivery/reconciliation contract
instead. The example's stored shipment ID is an authorized worker report, not
independent native proof that the shipping provider completed work.

Start the worker and process from separate terminals on configured
infrastructure:

```sh theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
pnpm worker
pnpm start -- input.json
```

The start script uses the business `process_id` as the Temporal Workflow ID and
rejects reuse. After a lost start reply, inspect that Workflow ID with Temporal
before deciding anything else. Changing the process ID intentionally starts new
business work. Keep Temporal's connection and namespace configuration available
to its CLI as well.

The workflow prepares an order, waits for an independent approval, calls the
shipping service and records the result through a second native operation.
Temporal timers own the waits; a terminal does not need to remain open.

## Capture one operation per logical step

Use `processClient` in a runner activity or task. Keep network calls outside a
deterministic Temporal workflow body.

```ts theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
import { readFile } from "node:fs/promises";
import { processClient } from "bijection/node";
import { api } from "./bijection/_generated/api";

const business = processClient({
  url: process.env.BIJECTION_URL!,
  directory: process.env.BIJECTION_PROCESS_JOURNAL!,
  namespace: process.env.BIJECTION_PROCESS_OWNER!,
  token: async () =>
    (await readFile(process.env.BIJECTION_TOKEN_FILE!, "utf8")).trim(),
});

const step = await business.operation(
  { process_id: "order-123-fulfillment-1", step: "settle" },
  api.orders.settle,
  { args: { process_id: "order-123-fulfillment-1", shipment_id: "SHP-981" } },
);
const status = await step.advance("accepted");
```

`processOperation(client.requests, identity, operation, payload)` is the
portable adapter when you already have an authenticated client with durable
shared request custody. Arguments and target retain their generated operation
types.

The identity is derived from `process_id` and `step`. It excludes worker
attempts, Temporal Run IDs, arguments and contract digests. Reusing a logical
step with changed content conflicts. A loop must include its business item or
ordinal in the step key. Repeating a business process requires a new occurrence
identity. Process and step identities each accept up to 512 UTF-8 bytes.

`advance` first inspects the retained request. It submits absent intentions
under their original key, recovers accepted decisions and continues pending
requests using their original proposal after its outstanding requirements clear.
It performs no submission while a native approval, agreement or verification is
outstanding. When native preparation requires a first proposal, it creates one.
It never approves on another person's behalf, replaces an expired proposal or
renews expired authority. Its returned requirements identify the wait. Expired
proposals or erased inputs produce explicit attention conditions. Native access
failures propagate with their original error data;
`operationErrorFeedback(error)` identifies the required sign-in, permission,
review or reconciliation action. Repair current authority before resuming the
same step; retries cannot grant access.

For long waits, checkpoint the returned status in your runner and use its timers
before calling `advance` again. `step.inspect()` is read-only. `step.wait()`
polls read-only under an observation timeout; stopping it neither cancels nor
resubmits the operation. Refusals, withdrawals and unsuccessful/partial
execution stop observation. `step.request` exposes the ordinary typed request,
including its retained local result and proposal API.

Select the completion evidence your next step needs:

| Completion | Evidence |
| - | - |
| `accepted` | The native business transaction committed |
| `confirmed` | Every external call has a settled successful delivery, independent of publication; a request with no calls requires completed native execution |
| `published` | Completion plus published observations for every external call |

A request without an external publication obligation cannot satisfy `published`.
The adapter reports `publication_not_declared`; it does not invent a read-back.
`confirmed` may be complete while publication is pending or blocked; the
publication state and attention remain in the same response. Unknown delivery,
unsettled effects, duplicates and independent operation review cannot satisfy
confirmation. Use `published` when the next step depends on observations being
available through Bijection. Confirmation of a Temporal Signal or cancellation
remains provider acceptance, not proof that the workflow handler ran or the
workflow terminated.

## Inspect and approve from the terminal

```sh theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
bijection process inspect orders:inspect process_id=order-123-fulfillment-1
bijection process inspect orders:inspect process_id=order-123-fulfillment-1 --format json
```

The starter's public query authorizes the business subject and calls
`inspectProcess`. Native reads independently check current disclosure
permission. The projection contains the process identity, application-supplied
workflow metadata, original run ID, contract digest, worker release, original
request IDs, acceptance, execution phase, requirements, delivery, publication
and source-refresh state. Application workflow metadata is labeled business
state; use the offered Temporal inspection command for runner state.

`recovery_actions` contains literal argument arrays supplied by the application.
The CLI renders those commands and never executes them automatically. The
starter offers an approval command binding the exact current terms and a
read-only Temporal description, plus commands to inspect each original worker
request. Inspect before deciding. As an independent reviewer:

```sh theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
bijection operation submit orders:approve --args '{"process_id":"order-123-fulfillment-1","expected_terms":"Ship one item to the approved address"}'
```

This starter's business approval checks exact immutable terms, current reviewer
membership and independence from the requesting service. Operations that require
native proposal approval continue to use their native proposal and independent
approval rules. See
[Defining operations](/operations/defining-operations#reviews-and-approvals).

Inside your own query, pass only request IDs linked to the authorized subject.
`inspectProcess` accepts at most 64 distinct steps and eight bounded recovery
actions. It writes no state and dispatches no provider work.

## Recover the original worker request

Run these commands in the generated project on infrastructure with the worker's
original durable journal and current service credentials:

```sh theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
pnpm recover -- order-123-fulfillment-1 prepare
pnpm recover -- order-123-fulfillment-1 settle
```

The command defaults to read-only inspection. `processClient.restore(identity)`
loads the exact saved envelope, including its arguments, operation and contract,
without consulting today's generated operation metadata. Missing custody is an
error; it never captures a replacement. This also works after the worker loses
its acceptance reply or a new application release changes the current API. The
SDK journal is separate from the native CLI's local journal: a worker's request
ID alone is not a CLI custody file.

After inspecting a pending request, explicitly continue it with:

```sh theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
pnpm recover -- order-123-fulfillment-1 prepare --advance
```

This reuses the original identity and checks native requirements again. For an
expired proposal, `--propose` explicitly prepares a proposal on that original
request; inspect and obtain the required independent approvals before choosing
`--advance`. It never approves, renews credentials, withdraws or compensates
automatically. A denied native command prints structured feedback with its
permission or review remedy and exits unsuccessfully. The workflow runner still
owns failed-activity retries and explicit reset decisions.

For an external call, use its exact ID from inspection with
`bijection integration command-status CALL_ID` on the original deployment, then
the native `command-recover` owner when its guidance permits recovery. These
commands inspect or reconcile the accepted call; they do not create another
shipment. Never re-run the shipping activity manually after an unknown result.

## Test scenarios and releases

```sh theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
pnpm test
pnpm typecheck
bijection temporal check fulfillment
bijection temporal check fulfillment --format json
```

The alias resolves the exact contract and worker release from `bijection.json`;
there is no generated hash path to copy. A changed interface requires compatible
worker builds in the manifest. Normal deployment preparation checks and captures
the same inputs. A check validates declared coverage; it does not execute
handlers or attest the running worker image. Keep the declared worker build
aligned with `temporal/release.ts`, retain pinned workers for existing
executions, and use Temporal's deployment tools to control routing. The starter
refuses to start a worker whose selected build does not declare the generated
contract. Its native order records the workflow ID, first run ID, contract
digest and build ID together. Those are accepted worker declarations; inspection
does not present them as live observations of running workers.

For a compatible worker change, retain the old pinned build and add the new
build to the release with the same contract digest. For an incompatible
interface, retain the old contract, release, generated module and bundled worker
under a separate configured alias while old processes wait. Configure the new
interface and worker under a new alias. Never regenerate the old worker against
the new interface or change a waiting process's logical step keys to bypass a
contract conflict. Check each alias with `temporal check`; the existing
`--compare-artifact` and `--compare-release` flags explain interface changes
against captured prior inputs.

Export an actual Temporal history using the customer's Temporal tools, then run:

```sh theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
pnpm replay -- history.json
```

Replay uses the pinned workflow bundle and executes no activities. A successful
replay establishes compatibility with that supplied history; it does not prove
new activity implementations or provider behavior.

The starter also declares `fulfillment` as a native branch scenario. Using the
worker's business identity, capture a snapshot while the order awaits approval
and run:

```sh theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
bijection snapshot capture before-approval --retain-for 24h
bijection run start fulfillment awaitingApproval --from SNAPSHOT_ID --output run.json
bijection run replay RUN_ID
```

For the `approved` case, pause the customer worker before approving, then
capture a snapshot after approval and before shipment settlement. Run the case
against that snapshot and resume the worker. That case calls the same settlement
operation inside the branch with an explicit simulated shipment ID. It does not
contact the shipping service or fabricate live approval. Use explicit scenario
input for a different process ID. Native scenarios test business boundaries;
Temporal replay tests workflow history compatibility. Hosted cross-boundary
behavior requires its own integration qualification.

Use the following recovery cases when changing a process. Each row names the
boundary that must supply evidence:

| Case | Exercise and required result | Evidence boundary |
| - | - | - |
| Approval becomes stale | Let a native proposal expire before the next advance. Inspection reports `proposal_expired`; no replacement is made. Expiry racing submission propagates the native stale-review error. Explicitly propose again and obtain new approval. | SDK adapter checks plus native proposal policy; the starter's immutable-terms business approval is a separate mechanism. |
| Provider accepts but the reply is lost | Preserve the original call ID or shipping idempotency key. Recover its evidence before any retry. Confirmation stays false while the outcome is unknown. | SDK acceptance-recovery checks cover lost native replies. Actual destination reconciliation or shipping idempotency requires provider integration evidence. |
| Worker restarts after acceptance | Replace the worker while keeping its journal. Restore the same process/step and observe acceptance without another submit. | SDK filesystem and HTTP checks; runner restart behavior requires an actual workflow history or hosted run. |
| Confirmation succeeds, publication blocks | Inspect a delivered, settled call with blocked publication. `confirmed` succeeds, `published` does not, and the publication diagnostic remains visible. Repair the existing publication. | SDK projection and CLI checks; native publication admission requires a deployed integration. |
| New release while a process waits | Keep the old pinned worker and contract available. Check both aliases, replay the old history, and restore original custody. Changed arguments or a new operation digest must conflict when capturing the same step. | Release checks, SDK custody checks and supplied-history replay; live routing requires provider evidence. |

The starter's `pnpm test` checks durable waits, terminal refusals, connection
configuration and selected worker release. Its native branch cases use the same
live settlement operation under isolated branch effects. Neither suite claims to
simulate a provider's lost reply or a worker crash. Use Atlas on a real
deployment when qualifying the actual cross-boundary failure cases.

On a production repair, preserve the original process/step IDs, worker version,
journal and credentials. Recover the accepted request before any new provider
write. Cancellation and compensation are explicit business decisions; a workflow
failure does not roll back previously accepted operations or external effects.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.