Free forever, no credit card.Get Started for Free →
← All posts
November 10, 2026 · 6 min read

Does QStash Remember Between Runs? A Schedule Delivers the Same Body Every Time, but the Last Run's Findings Never Ride Along

Does QStash Remember Between Runs? A Schedule Delivers the Same Body Every Time, but the Last Run's Findings Never Ride Along QStash, from Upstash, is a serverless, HTTP-based messaging and scheduling service. There is no worker to run and no queue to poll. Code publishes a message over HTTP, and QStash pushes that message to a destination URL, with retries, delays, and a dead letter queue when delivery keeps failing. Schedules add a cron expression, so the same destination gets called again an

Does QStash Remember Between Runs? A Schedule Delivers the Same Body Every Time, but the Last Run's Findings Never Ride Along

QStash, from Upstash, is a serverless, HTTP-based messaging and scheduling service. There is no worker to run and no queue to poll. Code publishes a message over HTTP, and QStash pushes that message to a destination URL, with retries, delays, and a dead letter queue when delivery keeps failing. Schedules add a cron expression, so the same destination gets called again and again on a pattern. That reliability raises a natural question for anyone whose endpoint does AI work: does the next delivery know anything about the last one? No. Each delivery carries the body and headers set when the message or schedule was created. What the previous run found, decided, or already handled is not in that delivery, unless application code put it there by hand.

What a QStash message actually carries

A published message is a delivery instruction. It names a destination URL and carries a body, plus delivery settings such as a delay, a retry count, a deduplication ID, and optional callbacks. QStash stores that message, waits for the right moment, and sends it as an ordinary HTTP request to an endpoint that already exists.

Every part of that package answers a delivery question. Where should this go. When should it arrive. What fixed payload should it bring. How many times should delivery be retried if the endpoint does not return a success response. None of those fields answers a work question: what did yesterday's run conclude, which records did it already process, and which exception did a human approve last week? A message knows how to arrive. It does not know what happened the last time a message like it arrived.

A schedule repeats an instruction, not a conversation

A QStash schedule is a cron expression attached to a publish. Create a schedule for a daily report endpoint, for example, and QStash will call that endpoint daily, sending the body defined on the schedule each time. Pause and resume change timing. They do not change what the endpoint learns.

That distinction matters for scheduled AI steps. Suppose the endpoint summarizes new support conversations every morning. On Monday it finds forty conversations, flags three as urgent, and notes that one account was already promised a follow-up. On Tuesday, the scheduled delivery arrives with the same body it always carries. It does not bring Monday's summary, the three urgent flags, or the promise. The endpoint starts from the body it was given and whatever it can fetch fresh from other systems. If the summary was never saved somewhere the endpoint reads at startup, the work is reconstructed, guessed at, or simply repeated.

The schedule did its job perfectly. Timing was never the missing piece. The missing piece is shared state between runs.

Logs, callbacks, and the DLQ record delivery, not findings

QStash keeps more around a message than the message itself, and each extra deserves a precise reading. Logs record delivery attempts and outcomes, and they can be listed and filtered. A callback can receive the result of a delivery, and a failure callback can be called when retries are exhausted. Messages that still fail land in the dead letter queue, where they can be retried or deleted, and a DLQ message can be replayed.

All of that is delivery history. A log can show that Tuesday's call returned an error and was retried. A callback can hand a response to another URL. The DLQ can hold a failed payload so it is not lost. None of these mechanisms briefs the next scheduled run. The next delivery does not open the logs, summarize the callbacks, and attach the lessons before calling the endpoint. A replay resends a payload. It does not add context that the original run produced after the payload was sent.

Deduplication draws the same boundary. A deduplication ID prevents the same publish from being delivered twice inside its window. That protects an endpoint from doing identical work twice by accident. It says nothing about related work across days, because preventing a duplicate delivery is a transport guarantee, not a record of what the work found.

The endpoint is stateless unless another system gives it a past

Because QStash pushes to an HTTP route, the endpoint itself is usually a serverless function or an app route that wakes for the request and ends when it responds. Nothing persists inside that function between calls except what it reads from an external store during the call. Teams often fill the gap with a database row, a Redis key, or a document that the endpoint updates at the end of each run and reads at the start of the next one.

That hand-built loop can work for one workflow and one kind of fact. It gets harder to trust as the automation grows. The row holds the fields someone remembered to write. It rarely holds the full reasoning, the corrections made mid-run, or the exact wording a customer used. When a second tool, a different agent, or a chat session needs the same past, the row built for one endpoint does not follow them. Each integration becomes another place where context can drift out of sync.

Give every scheduled run one shared memory

The cleaner pattern is to separate delivery from memory entirely. Let QStash do what it is built for, reliable delivery on time, and give the work itself a memory layer that every run and every tool reads before acting and writes to after finishing. That is the role of Vilix AI.

Vilix AI is cloud-hosted, so there is zero infrastructure to run or maintain. The same memory is available everywhere over MCP, which means a scheduled endpoint, a coding agent, and a chat assistant can all read from and write to one shared past instead of separate local notes. It stores full conversation history, not just extracted facts, so a run can revisit what was actually said and decided, in context, rather than working from a short field in a table.

In practice, the daily endpoint changes in one simple way. Before summarizing, it retrieves the relevant history from Vilix AI: yesterday's summary, the urgent accounts, the promise that was made. After finishing, it saves the new run back. Tuesday no longer starts blank, and the same history is there for any other tool that needs it later that day.

Vilix AI offers a free plan forever, plus a 7-day Pro trial with no credit card required. Data stays under the operator's control, with the ability to export everything or delete it anytime. Delivery keeps the automation on schedule. Shared memory is what lets the automation build on its own work. Start with Vilix AI and let the next QStash delivery arrive to an endpoint that already knows what happened last time.

Get Started for Free

Persistent memory across ChatGPT, Claude, and the AI tools you already use in Vilix AI.

Get Started for Free

Free forever, no credit card.

Keep reading
Does Taskiq Remember Between Runs? A Result Backend Can Keep a Return Value, but the Next Task Still Starts With Only Its Arguments.

Does Taskiq Remember Between Runs? A Result Backend Can Keep a Return Value, but the Next Task Still Starts With Only Its Arguments. Taskiq is a modern async task queue for Python. A task is defined once, sent to a broker with .kiq(), and executed by a worker, possibly in another process or on another machine. Because brokers can be durable, the message itself can survive a restart. That raises a fair question: can the next run look back at the last run? No. Taskiq will deliver the arguments it

Does Graphile Worker Remember Between Runs? A Successful Job Is Deleted, and Its Return Value Is Stored Nowhere.

Does Graphile Worker Remember Between Runs? A Successful Job Is Deleted, and Its Return Value Is Stored Nowhere. Graphile Worker stores every job as a row in PostgreSQL, so the queue itself is durable, inspectable, and transactional. That durability creates a natural assumption: if the jobs live in a real database, the next run of a job should be able to look back at the last run. It cannot. Graphile Worker keeps a job only while the job still needs to run. The moment a job succeeds, its row is

Does Huey Remember Between Runs? The Result Store Deletes on Read, and Periodic Tasks Discard Their Return.

Does Huey Remember Between Runs? The Result Store Deletes on Read, and Periodic Tasks Discard Their Return. Huey keeps three things in its storage backend: a queue of pending task calls, a schedule of delayed and periodic work, and a result store for return values. Because all three can live in Redis, Postgres, or SQLite, it is easy to assume the next run of a task can see what the last run produced. It cannot, at least not in the way an AI task needs. Huey stores delivery state and one return