# Client, Monitor & Operator API

BackWave's runtime exposes three public surfaces. The **client** (`BackWaveClient`) puts work
into the system: it enqueues jobs for now or the future, enqueues jobs gated on a parent, and
defines Recurring Schedules. The **monitor** (`BackWaveMonitor`) reads the system without ever
changing it: single-job snapshots, filtered listings, queue depths, tag facets, schedule
status, and observer delivery health. The **operator** (`BackWaveOperator`) performs the
audited state-machine transitions (cancel, requeue, pause, resume,
set a concurrency limit, trigger a schedule), each stamped with the acting identity and
recorded in an append-only audit log.

This page is the API reference for all three, plus the public types they exchange. For the
operational, task-oriented walkthrough of the monitor reads, see
[The Monitor API](/docs/dashboard-operations/monitor-api).

## The client — `BackWaveClient`

`BackWaveClient` lives in the `BackWave` namespace and is a `sealed` class. You construct it
with a job store, a job registry, and an optional clock.

```csharp title="BackWaveClient.cs"
public sealed class BackWaveClient
{
    public BackWaveClient(IJobStore store, JobRegistry registry, TimeProvider? clock = null);
}
```

The client owns the clock. When you omit `clock` it defaults to `TimeProvider.System`, and
every default instant the client needs (a job's due time, a dependent's enqueued-at, a
schedule's starting cursor) comes from that `TimeProvider`. You pass an explicit instant only
when you want to. Supplying a virtual clock is what makes enqueue-side timing deterministic
under [Virtual Time](/docs/testing/behavior-over-time). A future due time always defers; it is
never treated as "due now."

Each enqueue is serialized through the job type's registration, keyed by its
[Wire Name](/docs/core-concepts/jobs-and-handlers), and routed to the registered
[Queue](/docs/core-concepts/queues) unless a call overrides it.

### Enqueue a job — `EnqueueAsync`

`EnqueueAsync` is the primary entry point. It enqueues a job to become eligible at `dueTime`
and returns the new job's `Guid` id, which you can keep for later tracking or hand to
`EnqueueDependencyAsync` as a parent.

```csharp title="EnqueueAsync.cs"
public async ValueTask<Guid> EnqueueAsync<TJob>(
    TJob job,
    DateTimeOffset dueTime,
    string? queue = null,
    JobTags? tags = null,
    DbTransaction? transaction = null,
    CancellationToken cancellationToken = default)
    where TJob : notnull;
```

```csharp title="ChargeOrder.cs"
// Enqueue for now: a due time of "now" runs as soon as a worker is free.
Guid id = await client.EnqueueAsync(new OrderCharged(orderId), DateTimeOffset.UtcNow);

// Defer: a future due time waits until then.
await client.EnqueueAsync(new SendReminder(orderId), DateTimeOffset.UtcNow.AddHours(24));
```

| Parameter | Type | Default | Meaning |
|---|---|---|---|
| `job` | `TJob` | required | The payload instance, serialized via its registration. |
| `dueTime` | `DateTimeOffset` | required | When the job becomes eligible. Now runs as soon as a worker is free; a future instant defers. There is no zero-argument "enqueue now" overload, so pass the instant explicitly. |
| `queue` | `string?` | `null` | `null` uses the job type's registered Queue; a value overrides it for this call. |
| `tags` | `JobTags?` | `null` | Tags attached at enqueue. The job type's default tags are always unioned on top, additive only and never dropped at enqueue. Set semantics collapse identical tags. |
| `transaction` | `DbTransaction?` | `null` | A caller-owned `DbTransaction`. When supplied, the job commits or rolls back atomically with the caller's own writes. Requires `store.SupportsTransactionalEnqueue`. |
| `cancellationToken` | `CancellationToken` | `default` | Standard cancellation. |

Enqueuing "now" and scheduling for the future are the same code path: a plain enqueue is just
a job whose due time is the current instant. The [Scheduling](/docs/core-concepts/scheduling)
page covers due time as the universal mechanism.

**Transactional Enqueue.** Passing a `transaction` writes the job inside your own database
transaction, so it is created if and only if your surrounding work commits, with no outbox
required. This is a store capability, not universal. If you supply a non-null `transaction`
while `store.SupportsTransactionalEnqueue` is `false`, the client throws before touching the
store:

```csharp title="TransactionalEnqueue.cs"
await using var tx = await connection.BeginTransactionAsync();
await client.EnqueueAsync(new OrderCharged(orderId), DateTimeOffset.UtcNow, transaction: tx);
// ... your other writes on the same transaction ...
await tx.CommitAsync(); // the job becomes visible only now
```

The W3C trace context, both the `traceparent` and the `tracestate`, is captured onto the job at
enqueue, so the eventual `process` span links back to the enqueue's `send` span (through an
`ActivityLink`, not a parent edge) even when the job runs hours later on another node. The `send`
span also records the call site of the enqueue, as `code.function.name`, `code.file.path`, and
`code.line.number`.

The client maps the store's result to an exception on any non-success outcome:

| Condition | Exception | `paramName` | Message |
|---|---|---|---|
| `transaction` supplied but the store does not support it (checked before the store is touched) | `NotSupportedException` | — | `This storage adapter does not support Transactional Enqueue (SupportsTransactionalEnqueue is false); enqueue without a transaction instead.` |
| Serialized payload exceeds the store's bound | `ArgumentException` | `job` | `Payload for wire name '{WireName}' is {n} bytes, which exceeds the MaxPayloadBytes bound. Store a reference (id, blob key) instead of the data itself.` |
| Any other non-`Ok` result (`Duplicate`, `WireNameTooLong`, `TooManyParents`) | `InvalidOperationException` | — | `Enqueue failed: {result}.` |

On success the client records the enqueue in diagnostics against the wire name and target
queue.

### Enqueue a dependent job — `EnqueueDependencyAsync`

`EnqueueDependencyAsync` creates a job that waits for a single parent to reach a terminal state
before it is released. It returns the new dependent's `Guid` id.

```csharp title="EnqueueDependencyAsync.cs"
public async ValueTask<Guid> EnqueueDependencyAsync<TJob>(
    TJob job,
    Guid parentId,
    DateTimeOffset? enqueuedAt = null,
    DependencyMode mode = DependencyMode.OnSuccess,
    string? queue = null,
    JobTags? tags = null,
    CancellationToken cancellationToken = default)
    where TJob : notnull;
```

```csharp title="AfterCharge.cs"
Guid chargeId = await client.EnqueueAsync(new OrderCharged(orderId), DateTimeOffset.UtcNow);

// Runs only if the charge succeeds; any other terminal outcome cancels it.
await client.EnqueueDependencyAsync(new SendReceipt(orderId), chargeId);
```

The dependent sits in the `AwaitingParent` state until `parentId` becomes terminal, then
releases according to `mode`.

| Parameter | Type | Default | Meaning |
|---|---|---|---|
| `parentId` | `Guid` | required | The single gating parent. This method builds a one-element parent set. |
| `enqueuedAt` | `DateTimeOffset?` | `null` → the clock's now | When the dependent was created. A released dependent becomes due at release time if that is later than `enqueuedAt`. |
| `mode` | `DependencyMode` | `OnSuccess` | `OnSuccess` runs only if the parent succeeded. Any other terminal outcome cancels the dependent. `OnAnyTerminal` runs once the parent is terminal, whatever the outcome. |
| `queue` | `string?` | `null` | Same as `EnqueueAsync`: `null` uses the registered Queue. |
| `tags` | `JobTags?` | `null` | Same as `EnqueueAsync`: default tags are always unioned. |

There is no `transaction` parameter on this overload. On an unknown parent the store returns
`UnknownParent` and the client throws `ArgumentException` (`paramName` `parentId`) with message
`Parent job {parentId} does not exist.`; any other non-`Ok` result throws
`InvalidOperationException` with `Enqueue failed: {result}.`

This surface enqueues against exactly one parent. Fan-in on multiple parents and full
dependency graphs are the Workflow builder, a Pro feature that is not part of the free client.
[Dependencies](/docs/core-concepts/dependencies) covers the single-parent latch in depth.

### Define a Recurring Schedule — `UpsertRecurringAsync`

`UpsertRecurringAsync` defines or redefines a Recurring Schedule: a cron template that mints
job instances on each tick. The schedule itself is not a job. Reusing a `scheduleId` redefines
that schedule in place.

```csharp title="UpsertRecurringAsync.cs"
public ValueTask UpsertRecurringAsync<TJob>(
    string scheduleId,
    Core.CronExpression cron,
    TJob template,
    DateTimeOffset? now = null,
    string? queue = null,
    string? timeZone = null,
    CatchUpPolicy catchUp = CatchUpPolicy.Skip,
    bool noOverlap = false,
    CancellationToken cancellationToken = default)
    where TJob : notnull;
```

```csharp title="NightlyRollup.cs"
await client.UpsertRecurringAsync(
    "nightly-rollup",
    Cron.Daily(hour: 2),
    new RollupJob(),
    timeZone: "America/New_York");
```

| Parameter | Type | Default | Meaning |
|---|---|---|---|
| `scheduleId` | `string` | required | The stable id. Reusing it redefines the schedule in place. |
| `cron` | `Core.CronExpression` | required | A parsed cron expression. Stored as its canonical six-field form. |
| `template` | `TJob` | required | The payload template; each minted instance carries a copy. |
| `now` | `DateTimeOffset?` | `null` → the clock's now | The instant the schedule begins watching from, stored as its cursor. Only ticks after `now` mint. Defining a schedule never back-fills past occurrences. |
| `queue` | `string?` | `null` | `null` uses the registered Queue. |
| `timeZone` | `string?` | `null` → UTC | IANA zone id (for example `"America/New_York"`). `null` evaluates the cron in UTC. |
| `catchUp` | `CatchUpPolicy` | `Skip` | `Skip` mints nothing for missed ticks; `Coalesce` mints exactly one make-up run for the whole missed set. |
| `noOverlap` | `bool` | `false` | When `true`, a tick is skipped while a previously minted instance is still non-terminal. The skip is always recorded. |

The cron is already a parsed `CronExpression`, so the only thing that can be wrong at this door
is the time zone: an unresolvable `timeZone` throws `ArgumentException` (`paramName` `timeZone`,
message from the resolver) before anything reaches storage. Build the `cron` value with the
`Cron` helpers or `CronExpression.Parse`. See the [Cron API](/docs/reference/cron-api) for the
builder, the accepted grammar, and how the canonical form is derived.

### Remove a Recurring Schedule — `RemoveRecurringAsync`

`RemoveRecurringAsync` stops future minting for a schedule.

```csharp title="RemoveRecurringAsync.cs"
public ValueTask RemoveRecurringAsync(
    string scheduleId, CancellationToken cancellationToken = default);
```

Already-minted instances are left untouched and run to completion. Removing a schedule that
does not exist is a no-op and does not throw.

## The monitor — `BackWaveMonitor`

`BackWaveMonitor` lives in the `BackWave.Monitor` namespace and is a `sealed` class. Every
method is read-only and never mutates the store; every read returns a stable public shape and
never leaks storage internals.

```csharp title="BackWaveMonitor.cs"
public sealed class BackWaveMonitor
{
    public BackWaveMonitor(IJobStore store, JobRegistry? registry = null);
}
```

The `registry` is optional and is used only to answer `GetKnownWireNames()`. When you omit it,
that method returns an empty list and every other read still works.

### History-policy properties

Two properties tell you what the store is actually recording, so you can distinguish a
genuinely empty timeline from one that is empty because history recording is off.

```csharp title="HistoryPolicy.cs"
public JobHistoryPolicy JobHistoryPolicy { get; }      // read straight from the store
public bool IsHistoryRecordingDisabled { get; }        // == (JobHistoryPolicy == Off)
```

### The read methods

| Method | Returns | What it answers |
|---|---|---|
| `GetKnownWireNames()` | `IReadOnlyList<string>` | Every registered job type's Wire Name, alphabetical. Empty when no registry was supplied. |
| `GetJobAsync(jobId)` | `ValueTask<JobSnapshot?>` | One job's current snapshot, or `null` when no such job exists. |
| `GetJobHistoryAsync(jobId)` | `ValueTask<IReadOnlyList<JobTransition>>` | The append-only Transition Log for one job, oldest first. |
| `ListJobsAsync(query)` | `ValueTask<IReadOnlyList<JobSnapshot>>` | Jobs matching a `JobQuery`, oldest first by default. |
| `GetJobPayloadAsync(jobId)` | `ValueTask<JobPayloadView?>` | The job's payload rendered for display. Sensitive. |
| `GetJobOutputAsync(jobId)` | `ValueTask<ReadOnlyMemory<byte>?>` | The raw Job Output bytes a handler emitted on success. |
| `GetJobOutputViewAsync(jobId)` | `ValueTask<JobPayloadView?>` | The Job Output rendered for display. Sensitive. |
| `GetQueueDepthsAsync()` | `ValueTask<IReadOnlyList<QueueStateCount>>` | Job counts grouped by (queue, state). |
| `GetTagFacetAsync(key, baseQuery)` | `ValueTask<IReadOnlyList<TagFacet>>` | Distinct job counts grouped by one tag dimension. |
| `GetQueueSettingsAsync()` | `ValueTask<IReadOnlyList<QueueSettings>>` | Each queue's `Paused` flag and configured `ConcurrencyLimit`. |
| `GetDependencyEdgesAsync(jobId)` | `ValueTask<DependencyEdges>` | The dependency edges around one job. |
| `ListSchedulesAsync()` | `ValueTask<IReadOnlyList<ScheduleStatus>>` | Every Recurring Schedule with cursor, next-due tick, and recent skips. |
| `GetObserverCursorAsync(observerId)` | `ValueTask<long>` | One observer's durable delivery cursor. |
| `GetObserverLagAsync(observerId, subscription)` | `ValueTask<ObserverLag>` | One observer's subscription-aware delivery lag. |
| `ListObserverDeadLettersAsync(observerId)` | `ValueTask<IReadOnlyList<ObserverDeadLetterRecord>>` | One observer's dead-lettered deliveries, oldest first. |

**Single-job reads.** `GetJobAsync` returns `null` for an unknown id. `GetJobHistoryAsync`
returns the Transition Log oldest first (each entry a timestamp, resulting state, attempt
number, and optional failure detail) and returns empty both when the job is unknown and when
history recording is off. Use `JobHistoryPolicy` or `IsHistoryRecordingDisabled` to tell the
two cases apart.

**Listing and paging.** `ListJobsAsync` takes a `JobQuery`; passing `null` matches all jobs.
The returned page is capped by the store's maximum monitor page size. The store clamps
`JobQuery.MaxResults` down to that ceiling, so a request never returns every job at once. Page
further by passing the last row's `JobSnapshot.Sequence` as the next query's `AfterSequence`.
The cursor is direction-aware.

**Payload and output are sensitive.** `GetJobPayloadAsync`, `GetJobOutputViewAsync`, and the
raw `GetJobOutputAsync` are the only reads that surface payload or output bytes; a
`JobSnapshot` never carries them. The payload and view methods render the opaque bytes as
strict UTF-8, falling back to an uppercase hex dump for non-text bytes. BackWave itself does
not gate these. A payload or output may carry secrets or PII, so gate them behind the
`ViewSensitiveData` permission (or your own equivalent authorization) before you call.
`GetJobOutputAsync` returns the raw bytes for an in-process dependent to deserialize to the
producer's shape; it returns `null` when the job set no output. See
[Reading a parent's output](/docs/core-concepts/dependencies#reading-a-parents-output).

**Aggregates.** `GetQueueDepthsAsync` returns one `QueueStateCount` per (queue, state) pair
that currently has jobs, giving backlog depths, in-flight, and failure counts in a single read.
`GetTagFacetAsync` groups jobs by one tag dimension and counts distinct jobs per value, ordered
by count descending with value ascending as the tiebreak; a non-empty `key` facets a Keyed Tag
while the empty string `""` facets plain Labels. `GetQueueSettingsAsync` reports each queue's
`Paused` flag and configured `ConcurrencyLimit`. In-use slots are not included, so derive them
from the Leased count in `GetQueueDepthsAsync`. [Tags](/docs/core-concepts/tags) covers Labels
and Keyed Tags.

**Dependency edges.** `GetDependencyEdgesAsync` returns the edges around one job.
`GatingParents` is the set still gating it (a parent drops off once it completes, so this is
not the full original parent list), and `Children` are the jobs waiting on this one. Both sides
are empty for a job with no dependencies and for an unknown job.

**Schedules.** `ListSchedulesAsync` returns every Recurring Schedule with its cursor, next-due
tick, and recently skipped ticks. A schedule that cannot be resolved on this host (an
unparseable cron or an unknown time zone) is returned with `ScheduleStatus.Error` set rather
than dropped or thrown; `NextDue` is computed when the schedule is resolvable and is `null`
otherwise.

**Observer delivery health.** `GetObserverCursorAsync` returns one Transition Observer's
durable delivery cursor (the global timeline position through which all matching transitions
have been delivered or dead-lettered) and returns `-1` when the observer has no cursor yet,
including when the observer id is unknown. `GetObserverLagAsync` is subscription-aware: it
counts only the transitions this observer would actually deliver, and throws
`ArgumentNullException` if `observerId` or `subscription` is null. `ListObserverDeadLettersAsync`
returns the observer's dead-lettered deliveries (those that exhausted the retry ceiling),
oldest first, as delivery metadata only, with no payload or failure detail. The
[Observers API](/docs/reference/observers-api) covers authoring observers.

Workflow reads are a Pro feature and are not part of the free monitor surface.

## The operator — `BackWaveOperator`

`BackWaveOperator` lives in the `BackWave.Operations` namespace and is a `sealed` class. It
performs the operator actions, each one a defined state-machine transition rather than a raw row edit.
Every action is stamped with the acting operator's identity and written to the append-only
operator audit log as exactly one record.

```csharp title="BackWaveOperator.cs"
public sealed class BackWaveOperator
{
    public BackWaveOperator(IJobStore store, TimeProvider? clock = null);
}
```

`clock` defaults to `TimeProvider.System`. Every action method takes a `string actor`, an
optional `DateTimeOffset? now = null` (defaulting to the clock's current instant), and a
`CancellationToken`.

### `CancelJobAsync`

```csharp title="CancelJobAsync.cs"
public ValueTask<CancelResult> CancelJobAsync(
    Guid jobId, string actor, DateTimeOffset? now = null,
    CancellationToken cancellationToken = default);
```

A not-yet-started job cancels immediately. A running job is asked to stop cooperatively (its
handler's cancellation token fires via the heartbeat, and threads are never killed), and it
transitions to `Cancelled` when it next checks. The result is `CancelledImmediately`,
`CancellationRequested`, or `NotCancellable` (the job is absent or already terminal, so nothing
changed).

### `RequeueAsync`

```csharp title="RequeueAsync.cs"
public ValueTask<RequeueResult> RequeueAsync(
    Guid jobId, string actor, DateTimeOffset? now = null,
    CancellationToken cancellationToken = default);
```

Requeues a Dead-Lettered or Quarantined job: it returns to `Scheduled` with its `Attempt` reset
to `0` and runs again. Any other state is rejected unchanged. The result is `Requeued` or
`NotRequeueable`.

### `PauseQueueAsync` and `ResumeQueueAsync`

```csharp title="PauseResumeQueue.cs"
public ValueTask PauseQueueAsync(
    string queue, string actor, DateTimeOffset? now = null,
    CancellationToken cancellationToken = default);

public ValueTask ResumeQueueAsync(
    string queue, string actor, DateTimeOffset? now = null,
    CancellationToken cancellationToken = default);
```

Pausing a queue is cluster-wide: no worker claims from it until it is resumed. Jobs already
running are unaffected. Resuming makes the queue's due jobs claimable again. Neither returns a
value.

### `SetConcurrencyLimitAsync`

```csharp title="SetConcurrencyLimitAsync.cs"
public ValueTask SetConcurrencyLimitAsync(
    string queue, int? limit, string actor, DateTimeOffset? now = null,
    CancellationToken cancellationToken = default);
```

Sets or clears a queue's cluster-wide Concurrency Limit: at most `limit` of the queue's jobs
run at once across all workers, and `limit == null` removes the cap. It takes effect on the
next claim; running jobs are unaffected. [Queues](/docs/core-concepts/queues#concurrency-limits)
covers the limit in full.

### `TriggerScheduleNowAsync`

```csharp title="TriggerScheduleNowAsync.cs"
public ValueTask<TriggerScheduleResult> TriggerScheduleNowAsync(
    string scheduleId, string actor, DateTimeOffset? now = null,
    CancellationToken cancellationToken = default);
```

Mints one instance of a Recurring Schedule immediately, without moving the schedule's cursor or
disturbing its future ticks, a one-off, on-demand run. Here `now` is the instant the minted
instance becomes due. The result is `Triggered` or `ScheduleNotFound`.

### `ListAuditRecordsAsync`

```csharp title="ListAuditRecordsAsync.cs"
public ValueTask<IReadOnlyList<OperatorAuditRecord>> ListAuditRecordsAsync(
    string target, CancellationToken cancellationToken = default);
```

Returns the operator audit trail for one `target` (a job id, a queue name, or a schedule id,
depending on the action type), oldest first. Every operator action contributes exactly one
record. Empty when none exist.

Workflow operator actions are a Pro feature and are not part of the free operator surface.

## Public types

The types below are the shapes these surfaces take and return.

### `JobSnapshot`

A `sealed record` of displayable facts about one job, with no payload bytes.

| Member | Type | Notes |
|---|---|---|
| `JobId` | `Guid` | Required. |
| `WireName` | `string` | Required. The job type's stable string identity. |
| `Queue` | `string` | Required. |
| `State` | `JobState` | Required. |
| `Attempt` | `int` | Required. Execution tries so far; claiming to run starts an attempt. |
| `DueTime` | `DateTimeOffset` | Required. |
| `LeaseOwner` | `string?` | The worker holding it while Leased, else `null`. |
| `LeaseExpiry` | `DateTimeOffset?` | While leased, else `null`. |
| `CancelRequested` | `bool` | A cooperative cancel has been asked for. |
| `TerminalAt` | `DateTimeOffset?` | `null` while active. |
| `TerminalCause` | `string?` | Short reason; `null` while active. |
| `ScheduleId` | `string?` | The minting schedule; `null` for a directly enqueued job. |
| `Sequence` | `long` | Monotonic paging cursor. |
| `WorkflowId` | `Guid?` | `null` if not in a workflow. |
| `Tags` | `IReadOnlyList<JobTag>` | Defaults to empty. |

### `JobPayloadView` and `PayloadEncoding`

`GetJobPayloadAsync` and `GetJobOutputViewAsync` return a `JobPayloadView`.

```csharp title="JobPayloadView.cs"
public sealed record JobPayloadView
{
    public required int ByteCount { get; init; }        // raw length before rendering
    public required PayloadEncoding Encoding { get; init; }
    public required string Text { get; init; }          // decoded UTF-8, or an uppercase hex dump
}

public enum PayloadEncoding { Utf8, Hex }
```

`Encoding` is `Utf8` when the bytes decoded cleanly as UTF-8, and `Hex` when they did not and
`Text` is an uppercase hex dump.

### `ScheduleStatus`

Returned by `ListSchedulesAsync`.

| Member | Type | Notes |
|---|---|---|
| `ScheduleId` | `string` | Required. |
| `Cron` | `string` | Required. Canonical six-field form. |
| `WireName` | `string` | Required. |
| `Queue` | `string` | Required. |
| `Cursor` | `DateTimeOffset` | Required. Instant up to which ticks are resolved, inclusive. |
| `TimeZoneId` | `string?` | `null` means UTC. |
| `CatchUp` | `CatchUpPolicy` | |
| `NoOverlap` | `bool` | |
| `NextDue` | `DateTimeOffset?` | Next tick that will mint, or `null` if the cron has no future occurrence or the schedule cannot resolve. |
| `HasLiveInstance` | `bool` | A minted instance is currently non-terminal, which is what No-Overlap watches. |
| `SkippedTicks` | `IReadOnlyList<DateTimeOffset>` | Recently skipped, newest last, bounded. Defaults to empty. |
| `Error` | `string?` | Non-null means the schedule is unresolvable on this host (unparseable cron or absent IANA zone); minting skips it. `null` when healthy. |

### `JobQuery`

The filter for `ListJobsAsync`. It is a `sealed record`; every `null` or empty field adds no
constraint and matches everything.

| Member | Type | Default | Meaning |
|---|---|---|---|
| `State` | `JobState?` | `null` | `null` matches any state. |
| `Queue` | `string?` | `null` | `null` matches any queue. |
| `WireName` | `string?` | `null` | `null` matches any type. |
| `ScheduleId` | `string?` | `null` | `null` matches jobs from any source. |
| `TagPredicates` | `IReadOnlyList<JobTagPredicate>` | `[]` | AND-ed together and AND-composed with the scalar filters. OR is out of scope, so run two queries. |
| `AfterSequence` | `long?` | `null` | Pagination cursor; only jobs strictly beyond it in the requested direction are returned. `null` starts at page one. |
| `SortDirection` | `JobSortDirection` | `OldestFirst` | |
| `MaxResults` | `int` | `int.MaxValue` | Requested page size. The store clamps it down to its maximum monitor page size, so the effective size is whatever the store allows. |

### `JobTagPredicate`

A `sealed record` built through static factories, used in `JobQuery.TagPredicates`. Predicates
on a query AND together.

| Factory | Matches |
|---|---|
| `HasLabel(string value)` | The job carries the Label with this value. |
| `HasKeyValue(string key, string value)` | The job carries this exact keyed tag. |
| `HasKey(string key)` | The job carries any tag under this key, any value. |

It exposes `string Key` (empty string for a has-label predicate), `string? Value` (`null` to
match any value under `Key`), and `bool Matches(JobTags tags)`.

### `JobSortDirection`

```csharp title="JobSortDirection.cs"
public enum JobSortDirection { OldestFirst, NewestFirst }
```

`OldestFirst` is ascending by `Sequence` (the default; enqueue and claim order). `NewestFirst`
is descending, most recently enqueued first.

### Aggregate result records

```csharp title="AggregateRecords.cs"
public sealed record QueueStateCount(string Queue, JobState State, int Count);

public sealed record TagFacet(string Value, int Count);

public sealed record QueueSettings(string Queue, bool Paused, int? ConcurrencyLimit);

public sealed record DependencyEdges(
    IReadOnlyList<Guid> GatingParents, IReadOnlyList<Guid> Children);
```

For `TagFacet`, `Value` is the tag value (or the Label text for the empty-key facet) and
`Count` is distinct jobs; buckets are ordered by count descending, value ascending as the
tiebreak. For `QueueSettings`, `ConcurrencyLimit` is `null` when there is no cap, and in-use
slots are not included, so derive them from the Leased depth. For `DependencyEdges`,
`GatingParents` resolves away as each parent terminates, so it is not the full original set.

### `JobTransition`

An entry in a job's Transition Log, returned by `GetJobHistoryAsync`.

```csharp title="JobTransition.cs"
public sealed record JobTransition(
    long Ordinal, DateTimeOffset Timestamp, JobState State, int Attempt, string? FailureDetail);
```

`Ordinal` is a 0-based per-job sequence, oldest first, preserved even when older entries age
out beyond the cap. `FailureDetail` carries captured diagnostics (exception type, message,
stack), bounded for storage, and is `null` on every non-failing transition.

### Operator result enums

```csharp title="OperatorResults.cs"
public enum CancelResult { CancelledImmediately, CancellationRequested, NotCancellable }
public enum RequeueResult { Requeued, NotRequeueable }
public enum TriggerScheduleResult { Triggered, ScheduleNotFound }
```

| Value | Meaning |
|---|---|
| `CancelledImmediately` | The job was not yet running; it is now terminal `Cancelled`. |
| `CancellationRequested` | The job was Leased; the cancel-requested flag is set and it cancels cooperatively on the next heartbeat. |
| `NotCancellable` | The job is absent or already terminal; nothing changed. |
| `Requeued` | A Dead-Lettered or Quarantined job returned to `Scheduled` with `Attempt` reset to 0. |
| `NotRequeueable` | The job is absent or not in a requeueable state. |
| `Triggered` | One instance was minted immediately; the cursor did not move. |
| `ScheduleNotFound` | No schedule with that id. |

### `OperatorAction` and `OperatorAuditRecord`

```csharp title="OperatorAudit.cs"
public enum OperatorAction
{
    Cancel, Requeue, TriggerScheduleNow, PauseQueue, ResumeQueue, SetConcurrencyLimit
}

public sealed record OperatorAuditRecord(
    string Actor, OperatorAction Action, string Target, DateTimeOffset RecordedAt);
```

`Target` is a job id, queue name, or schedule id depending on the action.

### `EnqueueResult`

The store-level outcome the client maps to a return value or exception.

| Value | Meaning |
|---|---|
| `Ok` | Created. |
| `Duplicate` | A job with the same id already exists; nothing created, the existing job is left as-is. |
| `PayloadTooLarge` | The serialized payload exceeds the store's bound. |
| `WireNameTooLong` | The wire name exceeds the store's length bound. |
| `UnknownParent` | A declared gating parent does not exist. |
| `TooManyParents` | The declared parent set exceeds the store's maximum parent count. |

### `DependencyMode`

```csharp title="DependencyMode.cs"
public enum DependencyMode { OnSuccess, OnAnyTerminal }
```

`OnSuccess` releases only if every parent Succeeded. Any other terminal outcome cancels the
dependency. `OnAnyTerminal` releases once every parent is terminal, whatever the states.

### `JobState` and `JobStates`

```csharp title="JobState.cs"
public enum JobState
{
    Scheduled, AwaitingParent, Leased, Succeeded, Cancelled, DeadLettered, Quarantined
}
```

| State | Meaning |
|---|---|
| `Scheduled` | Enqueued, waiting for its due time; claimable once due. |
| `AwaitingParent` | Held until parents are terminal; becomes `Scheduled` once the last parent resolves. |
| `Leased` | Claimed by a worker, running under a heartbeat-renewed lease. |
| `Succeeded` | Terminal. |
| `Cancelled` | Terminal. |
| `DeadLettered` | Terminal. |
| `Quarantined` | Terminal. |

`JobStates.IsTerminal(this JobState)` is `true` for `Succeeded`, `Cancelled`, `DeadLettered`,
and `Quarantined`. A terminal job never transitions on its own; only an explicit operator
action, such as requeue, can move it. [The Job Lifecycle](/docs/core-concepts/job-lifecycle)
covers the states and their transitions.

### `JobHistoryPolicy`

A ladder, where each rung adds to the one below. It gates what the store records, not the schema.

| Value | Records |
|---|---|
| `Off` | Nothing. |
| `Transitions` | Transition rows, with failure detail forced `null`. |
| `TransitionsAndFailureDetail` | The full log. The default. |

A kill-switch environment variable, `BACKWAVE_DISABLE_FAILURE_DETAIL`, downgrades the top rung
to `Transitions` when set. Truthy values are `"1"`, or `"true"`, `"yes"`, and `"on"`
(case-insensitive). See
[Retention and purge](/docs/dashboard-operations/retention-and-purge) for the operational side.

### `CatchUpPolicy`

```csharp title="CatchUpPolicy.cs"
public enum CatchUpPolicy { Skip, Coalesce }
```

`Skip` (the default) mints nothing for missed ticks. `Coalesce` mints exactly one make-up run
for the whole missed set.

### `JobTag` and `JobTags`

A `JobTag` is either a Label (empty `Key`) or a Keyed Tag. `Value` is never empty. The kind is
what the empty key marks. A colon inside a value is ordinary data, not a parsed separator.

```csharp title="JobTag.cs"
public sealed record JobTag
{
    public string Key { get; }             // empty for a Label
    public string Value { get; }           // never empty
    public bool IsLabel => Key.Length == 0;

    public static JobTag Label(string value);            // throws if value is null/empty
    public static JobTag Keyed(string key, string value); // throws if key or value is null/empty
}
```

`JobTags` is an immutable set: re-adding an identical tag is a no-op, iteration is in first-seen
order, and equality is order-independent set equality.

```csharp title="JobTags.cs"
public sealed class JobTags : IReadOnlyList<JobTag>, IEquatable<JobTags>
{
    public static readonly JobTags Empty;

    public JobTags WithLabel(string value);
    public JobTags WithTag(string key, string value);
    public JobTags With(JobTag tag);
    public static JobTags From(IEnumerable<JobTag> tags);   // collapses duplicates, keeps first-seen order

    public bool Contains(JobTag tag);
}
```

```csharp title="TaggingAtEnqueue.cs"
var tags = JobTags.Empty
    .WithLabel("urgent")
    .WithTag("tenant", "acme");

await client.EnqueueAsync(new OrderCharged(orderId), DateTimeOffset.UtcNow, tags: tags);
```

[Tags](/docs/core-concepts/tags) covers Labels, Keyed Tags, and set semantics in full.

### Observer types

`GetObserverLagAsync` takes an `ObserverSubscription` and the monitor returns `ObserverLag`;
`ListObserverDeadLettersAsync` returns `ObserverDeadLetterRecord`.

```csharp title="ObserverSubscription.cs"
public sealed record ObserverSubscription(IReadOnlyList<JobState> States)
{
    public static ObserverSubscription AllTransitions { get; } // every state
    public string? WireName { get; init; }   // null matches every type
    public string? Queue { get; init; }      // null matches every queue

    public bool Matches(JobState state, string wireName, string queue);
}
```

A transition into any state in `States` matches, further constrained by `WireName` and `Queue`
when set. Narrow `AllTransitions` with a `with` expression, for example
`ObserverSubscription.AllTransitions with { WireName = "PaymentJob" }`.

```csharp title="ObserverReadTypes.cs"
public sealed record ObserverLag(long Cursor, int Pending, DateTimeOffset? OldestPendingAt);

public sealed record ObserverDeadLetterRecord(
    long Position, Guid JobId, long Ordinal, JobState State,
    int Attempt, int DeliveryAttempts, DateTimeOffset DeadLetteredAt);
```

In `ObserverLag`, `Cursor` is `-1` when nothing has been delivered, `Pending` is `0` when the
observer is caught up, and `OldestPendingAt` is `null` when caught up. An
`ObserverDeadLetterRecord` is delivery metadata only, with no payload or failure detail. The
[Observers API](/docs/reference/observers-api) covers authoring and delivery.

## Where to go next

- [The Monitor API](/docs/dashboard-operations/monitor-api): the task-oriented walkthrough of
  the monitor reads, including composing a health rollup and honoring the sensitive-data gate.
- [Scheduling](/docs/core-concepts/scheduling): due time as the universal mechanism, one-off
  future work, and Recurring Schedules.
- [Cron API](/docs/reference/cron-api): the `Cron` builder and `CronExpression` behind
  `UpsertRecurringAsync`.
- [Dependencies](/docs/core-concepts/dependencies): the single-parent latch, reaction modes,
  and reading a parent's output.
- [Tags](/docs/core-concepts/tags): Labels, Keyed Tags, set semantics, and finding jobs by tag.
- [Queues](/docs/core-concepts/queues): named streams, concurrency limits, and pausing.
- [The Job Lifecycle](/docs/core-concepts/job-lifecycle): every state and the transitions
  between them.
- [Observers API](/docs/reference/observers-api): authoring Transition Observers whose delivery
  health the monitor reports.
