# Run State

Store and share data across steps within a single workflow run.

`api.runStore` lets you persist values between steps within the same run. All keys are scoped to the current run and are deleted automatically when the run completes.

## Methods

| Method | Description |
| --- | --- |
| `api.runStore.get(key)` | Read a stored value (returns `null` if not set) |
| `api.runStore.set(key, value)` | Write any JSON-serialisable value |
| `api.runStore.push(key, item)` | Append an item to an ordered list |
| `api.runStore.increment(key, delta?)` | Atomically increment a counter (default delta: `1`) |
| `api.runStore.delete(key)` | Remove a key |

## Use run state for compact data

`api.runStore` is best for small run-scoped state such as:

- IDs and keys
- row numbers or chunk indexes
- counters
- warnings and compact error records
- summary inputs for `onWorkflowComplete`

Do not use `api.runStore` as a cache for large raw datasets such as:

- full parsed CSV row arrays
- raw attachment text or buffers
- entire imported API responses

For large files or imports, parse or import once, then pass compact references between steps such as chunk keys, row ranges, cursors, or counters.

Each stored value or pushed item should stay compact. Use run state for small operational data, not multi-megabyte payloads.

## Passing data between steps

```js
export class Workflow {
  async start(data, headers, api) {
    // Store the customer ID before scheduling the next step
    await api.runStore.set('customerId', data.customer.id);

    await api.scheduleNextStep({
      delay: 10,
      action: 'sendEmail',
      payload: {},
    });
  }

  async sendEmail(data, headers, api) {
    // Retrieve the value stored in the previous step
    const customerId = await api.runStore.get('customerId');
    console.log('Sending to customer:', customerId);
  }
}
```

## Collecting results across fan-out branches

`api.runStore.push()` is safe to call from concurrent parallel branches. Use it to append items without building your own shared array in memory:

```js
export class Workflow {
  async start(data, headers, api) {
    // Fan out — schedule one branch per line item
    for (const item of data.line_items) {
      await api.scheduleNextStep({
        delay: 10,
        action: 'processItem',
        payload: { item },
      });
    }
  }

  async processItem({ item }, headers, api) {
    // Each parallel branch pushes its result
    await api.runStore.push('results', { id: item.id, status: 'done' });
  }
}
```

Do not use `get()` plus `set()` on the same key for shared aggregation in fan-out workflows. Parallel branches can race and overwrite each other. Use `increment()` for counters and `push()` for collected records instead.

## Atomic counters

`api.runStore.increment()` is safe for concurrent fan-out use. The optional second argument adds or subtracts that delta:

```js
const newCount = await api.runStore.increment('processedCount');
// Decrement:
const newCount = await api.runStore.increment('processedCount', -1);
```

The method returns the new value after the increment.

> Note: Run state is scoped to a single run. It cannot be shared between different workflow runs. For cross-run persistence, write to Shopify metafields or an external store via `fetch()`.