Skip to main content
JobsClient is the typed interface for interacting with job instances from your Cloudflare Worker’s fetch handler. All methods return Effects and can be yielded inside Effect.gen.

Creating the client

const client = JobsClient.fromBinding(env.JOBS);
JobsClient.fromBinding takes the Durable Object namespace binding and returns a fully typed client scoped to your registered jobs.

Instance IDs

Every job instance is identified by a user-provided ID. Internally, the Durable Object instance ID follows this pattern:
{jobType}:{jobName}:{userProvidedId}
For example:
continuous:tokenRefresher:user-123
debounce:webhookBatcher:contact-456
task:orderProcessor:order-789
You only need to provide the user-facing ID (e.g. "user-123") when calling client methods. The prefix is added automatically.

Continuous client

Access via client.continuous(jobName).

start(options)

Start a continuous job instance. If the instance already exists, returns the existing status without restarting.
const result = yield* client.continuous("tokenRefresher").start({
  id: "user-123",
  input: { accessToken: "", refreshToken: "rt_abc", expiresAt: 0 },
});
// Returns: { created: boolean, instanceId: string, status: JobStatus }

trigger(id)

Trigger an immediate execution, bypassing the schedule.
yield* client.continuous("tokenRefresher").trigger("user-123");

status(id)

Get the current status of a continuous job instance.
const status = yield* client.continuous("tokenRefresher").status("user-123");
// Returns: { status: "running" | "stopped" | "not_found", runCount?, nextRunAt? }

getState(id)

Get the current persisted state of a job instance.
const { state } = yield* client.continuous("tokenRefresher").getState("user-123");

terminate(id, options?)

Terminate the job instance: cancel the alarm and delete all state. The instance ID can be reused to start a fresh instance.
yield* client.continuous("tokenRefresher").terminate("user-123", {
  reason: "User requested",
});

Debounce client

Access via client.debounce(jobName).

add(options)

Add an event to the debounce buffer. Creates the instance if it does not exist yet.
const result = yield* client.debounce("webhookBatcher").add({
  id: "contact-456",
  event: { type: "contact.updated", contactId: "456", data: {} },
});
// Returns: { created: boolean, eventCount: number, willFlushAt: number | null }

flush(id)

Force an immediate flush of accumulated events, bypassing flushAfter.
yield* client.debounce("webhookBatcher").flush("contact-456");

clear(id)

Clear the accumulated event buffer without processing it.
yield* client.debounce("webhookBatcher").clear("contact-456");

status(id)

Get the current status of a debounce instance.
const status = yield* client.debounce("webhookBatcher").status("contact-456");
// Returns: { status: "debouncing" | "idle" | "not_found", eventCount?, willFlushAt? }

getState(id)

Get the current accumulated state.
const { state } = yield* client.debounce("webhookBatcher").getState("contact-456");

Task client

Access via client.task(jobName).

send(options)

Send an event to a task instance. Creates the instance if it does not exist yet.
const result = yield* client.task("orderProcessor").send({
  id: "order-789",
  event: { _tag: "OrderPlaced", orderId: "order-789" },
});
// Returns: { created: boolean, instanceId: string, scheduledAt: number | null }

trigger(id)

Trigger immediate execution, bypassing any scheduled alarm.
yield* client.task("orderProcessor").trigger("order-789");

status(id)

Get the current status of a task instance.
const status = yield* client.task("orderProcessor").status("order-789");
// Returns: { status: "active" | "idle" | "not_found", scheduledAt?, eventCount? }

getState(id)

Get the current state and schedule information.
const { state, scheduledAt } = yield* client.task("orderProcessor").getState("order-789");

terminate(id)

Terminate the task instance: cancel the alarm and delete all state.
yield* client.task("orderProcessor").terminate("order-789");

Using the client in Effect.gen

All client methods return Effects. Use them inside Effect.gen with yield*:
export default {
  async fetch(request: Request, env: Env) {
    const client = JobsClient.fromBinding(env.JOBS);

    return Effect.runPromise(
      Effect.gen(function* () {
        const result = yield* client.task("orderProcessor").send({
          id: "order-789",
          event: { _tag: "OrderPlaced", orderId: "order-789" },
        });

        const { state } = yield* client.task("orderProcessor").getState("order-789");

        return new Response(JSON.stringify({ result, state }));
      })
    );
  },
};
You can also use Effect.runPromise with a single chained expression if you prefer a more functional style.

Build docs developers (and LLMs) love