Key-value store and Storage
A run forgets everything when it ends. Two steps let workflows remember: the Key-value store for small values, counters and flags, and Storage for larger JSON documents. Both belong to your shop and are shared by all your workflows and functions.
| Key-value store | Storage | Files | |
|---|---|---|---|
| Made for | Small values, counters, flags | Larger JSON documents | Whole files: CSV, XML, PDF |
| Count up | Yes | No | No |
| Expiry | Optional, per value | No | No |
| Largest value | 64 KB | 64 KB to 512 KB, by plan | 5 MB to 50 MB, by plan |
| From code | ctx.kv |
ctx.storage |
Not available |
Files have their own page: Files, SFTP and downloads.
Key-value store
Add the step Key-value store and choose What to do:
| Choice | Fields | Result |
|---|---|---|
| Read a value | Key | steps.<id>.output.value, empty when the key does not exist |
| Write a value | Key, Value, optional Forget after (seconds) | The stored value |
| Delete a value | Key | |
| Count up a number | Key, optional Amount, optional Forget after (seconds) | The new number in steps.<id>.output.value |
- Key is free text, up to 512 characters, and takes variables:
order:{{ event.payload.id }}:notified. - Value is text, a number or JSON. Valid JSON is stored as JSON.
- Amount: leave empty to count up by 1. A negative number counts down.
- Forget after (seconds): leave empty to keep the value until a workflow deletes it. At most one year.
Counting up is safe when many runs do it at the same moment: every run gets its own number.
Patterns
Do something only once. Read the key order:<id>:notified. A Condition checks is empty. On that branch do the work, then write the key, with an expiry so the store stays tidy.
Count, then tell. Count up no-results:{{ event.payload.query }} with Forget after set to 86,400 seconds. A Condition on steps.<id>.output.value is equal to 10 posts to Slack exactly once per day and search term. The storefront templates work this way.
Remember the last run. A scheduled workflow writes the current time at its end and reads it at its start, to fetch only what changed since.
Storage
Add the step Storage and choose What to do: Read a document, Write a document, Delete a document or List keys.
- Key: any characters, 256 at most. For List keys, Keys that start with narrows the list; leave it empty to list every key. A list holds up to 100 keys, names and sizes only.
- Value: text, a number or JSON. Valid JSON is stored as JSON. When two runs write the same key at once, the last one wins.
- It is the same store your functions use as
ctx.storage, so a workflow step can prepare a document that code reads later, and the other way round.
Limits by plan
| Plan | Key-value keys | Storage in total | Storage keys | Largest document |
|---|---|---|---|---|
| Free | 1,000 | 5 MB | 500 | 64 KB |
| Starter | 10,000 | 50 MB | 5,000 | 128 KB |
| Grow | 100,000 | 250 MB | 25,000 | 256 KB |
| Unlimited | 1,000,000 | 1 GB | 100,000 | 512 KB |
In tests
In a preview, reads and lists run for real; writes, deletes and counting are previewed and change nothing. The debugger shows the key-value keys your steps use with their current values, and what a previewed write would have made of them. See Test, preview and debug.
Good to know
- A value can hold personal data if you put it there. Prefer ids over names and addresses. When your store asks Shopify to erase its data after uninstalling, both stores are erased too.
- Over the Developer API, a list returns keys and sizes, never values. A single read returns the value as stored. See Developer API and MCP.
- A temporary failure is retried. Counting up is never retried, because it could count twice. See Automatic retries.

