Chatbot Async Workflows: Handle Long-Running Tasks Safely

Design chatbot async workflows that survive long-running tasks with clear job states, progress updates, retries, cancellation, and durable results.

Cover Image for Chatbot Async Workflows: Handle Long-Running Tasks Safely

AI work is stretching beyond the lifespan of a normal chat session. OpenAI introduced ChatGPT Work on July 9 with tasks that can continue for hours, run on a schedule, or respond to a trigger. Its June research on agent adoption found that more than 70% of sampled individual users had delegated at least one Codex task estimated to take a person over an hour. Long-running work is moving from a specialist pattern into an interface customers will expect.

A website chatbot cannot keep a visitor staring at a typing indicator for an hour. The useful pattern is to turn the request into a durable job, return control immediately, and make every later state understandable. Start with this job contract:

FieldWhat the visitor needs to know
Job IDA stable reference for status, support, and deduplication
Accepted scopeThe exact task, inputs, account, and requested output
Current stateQueued, running, waiting for input, completed, failed, cancelled, or expired
Next checkpointWhen the system will update the visitor, without inventing a completion time
ControlHow to resume, add input, cancel, or safely retry
DeliveryWhere the result will appear and who is allowed to open it
ReceiptWhat ran, what changed, what failed, and links to created records

That contract turns “I’m working on it” into an operation the customer can leave, inspect, and trust.

Split the Conversation From the Work

The chat session and the background job have different lifecycles. A browser tab can close while the job continues. A visitor can return from another device. A worker can restart. An external API can accept a request and finish much later.

Store job state outside the model conversation. The transcript should explain the request and decisions; a database or queue should own execution state, attempts, timestamps, authorization scope, and result locations. Reconstructing those fields from a model-generated summary is fragile and can cause duplicate or unauthorized work.

Use an explicit state machine:

StateEntry conditionRequired customer-facing response
QueuedRequest validated and storedConfirm scope, job ID, and next checkpoint
RunningA worker owns the attemptShow the current stage, not a permanent spinner
Waiting for inputProgress needs a customer decision or missing valueName the question, deadline, and safe default
CompletedResult stored and side effects confirmedDeliver the result and an execution receipt
FailedAttempt ended without a confirmed outcomeExplain what did not happen and whether retry is safe
CancelledAuthorized cancellation acceptedState which completed effects remain
ExpiredInput, approval, or result window endedExplain how to start a fresh job

Avoid using “pending” for everything between request and result. It hides the difference between waiting in a queue, actively processing, blocked on the visitor, and stuck after an error. Those states demand different actions.

The broader chatbot actions guide helps decide which customer requests should trigger tools. The async boundary begins when that tool cannot produce a trustworthy final result within the current response.

Worked Example: An 11-Minute Account Report

Suppose a customer asks a billing chatbot to analyze a year of invoices and produce a downloadable reconciliation report. The work normally takes 8 to 15 minutes because the system must retrieve records, check duplicates, calculate totals, and render a file.

A weak interaction holds the request open:

Customer: Reconcile our 2025 invoices and flag duplicate charges.

Chatbot: I'm working on that now. This may take a few minutes...

[The browser sleeps after four minutes. The customer refreshes and asks again.]

Chatbot: I'll start the reconciliation now.

The second message may launch a duplicate report. The customer has no reference, cannot distinguish a retry from a restart, and does not know whether the first job changed anything.

A durable interaction acknowledges the handoff:

Customer: Reconcile our 2025 invoices and flag duplicate charges.

Chatbot: I accepted a read-only reconciliation for account Northwind EU.
Job AR-2841 will check 1,842 invoices from Jan 1 through Dec 31, 2025
and create one CSV report. No invoice will be changed.

Current state: queued
Next update: when invoice retrieval finishes, or within 5 minutes
Delivery: this conversation and [email protected]

[View status] [Cancel job]

Six minutes later, the job finds that an encrypted archive needs a password. It enters waiting_for_input instead of failing or guessing:

Job AR-2841 is paused. The March archive is encrypted, so 123 invoices
have not been checked. Add the archive password by July 23 at 14:00 UTC,
continue without March, or cancel the job.

[Add password] [Continue without March] [Cancel]

After the customer supplies the password, the original job resumes. At 11 minutes, it returns a receipt:

Job AR-2841 completed.

Checked: 1,842 invoices
Potential duplicates: 7 invoice pairs (€4,380 total)
Records changed: 0
Report: reconciliation-2025.csv
Completed: 2026-07-22 09:41 UTC

The useful artifact is not merely the CSV. It is the combination of scoped acceptance, resumable state, customer controls, and a final receipt.

Promise a Checkpoint, Not a Fake ETA

Background work often depends on queue depth, rate limits, third-party systems, and the size of customer data. A precise completion time may look reassuring but becomes a broken promise when any dependency slows down.

Give the visitor two separate signals:

Current stage. “Retrieving invoices,” “waiting for approval,” and “rendering the report” explain what is happening without exposing noisy internal logs.

Next checkpoint. Promise when the system will communicate again: after a stage completes, when input is required, or within a maximum silence window. “We will update you within five minutes even if the state has not changed” is controllable. “Your report will be ready in five minutes” may not be.

Send progress only when it changes the customer's understanding or options. A feed of “still working” messages creates notification fatigue and makes the important blocked state easier to miss. For predictable tasks, one acceptance, one blocked-state alert if needed, and one terminal result are often enough.

Make Resume, Retry, and Cancel Mean Different Things

These controls are commonly presented as variations of “run it again,” but they carry different risks.

Resume continues the same job. Use it after missing input, an approval, or a recoverable pause. Preserve the job ID, completed stages, and idempotency keys.

Retry creates a new attempt under the same job. Use it after a transient failure when repeating the next step is known to be safe. Increment an attempt number and keep the earlier error in the receipt.

Restart creates a new job. Use it when the scope, inputs, authorization, or desired output changed. It needs a new job ID and a fresh acceptance summary.

Cancel stops future work where possible. It cannot pretend already completed side effects never happened. If the job sent three of five approved emails before cancellation, say that three were sent and two were stopped.

Idempotency belongs at every side-effect boundary. A job-level key prevents double submission; step-level keys prevent a worker retry from issuing the same refund, message, or record update twice. The AI agent audit-trail guide shows how intent, attempt, and confirmed effect fit together when a retry crosses several systems.

Recheck Authority When the Work Resumes

Authorization at request time is not permanent authorization. A person can lose access, leave a team, change roles, or withdraw approval while a job is queued. The target record may move to another account before the worker acts.

Bind the job to an account, actor, requested capability, input snapshot, and approval version. Recheck authority before each consequential step and again before delivering a sensitive result. A valid job ID should not act as a bearer token that anyone can use to open the output.

If the job pauses for a material change, request a new approval. Adding a missing report format can be harmless; expanding a read-only reconciliation into automatic invoice deletion is new scope. The chatbot approval workflow explains how to show the exact payload, consequence, and expiry instead of asking for a vague “continue?”

Set retention explicitly. Raw input, intermediate files, progress events, final artifacts, and audit records may need different deletion windows. Expiring the download link should not erase the minimal receipt required to explain what happened.

Deliver the Result Without Breaking Context

A result can arrive after the visitor closed the page, logged out, or started another conversation. Choose delivery at acceptance time and confirm it before sending sensitive content.

For low-risk work, an in-product notification can link back to the original conversation. For private reports, notify the customer that the result is ready and require authentication before opening it. Avoid placing the report itself, customer data, or a permanent access URL in an email subject or push notification.

When the visitor returns, restore a compact job summary rather than replaying every progress event into the model context. Include the original scope, current state, outstanding decision, last meaningful checkpoint, and result reference. This follows the same discipline as a deliberate chatbot context-window policy: preserve verified state and current evidence, while keeping operational noise outside the prompt.

If several jobs share a conversation, name them. “Your export is finished” becomes ambiguous when a customer requested an invoice export, a user export, and a security report. Stable labels and IDs make follow-up questions route correctly.

Test the Hours Between Messages

Happy-path demos compress time and hide the failures unique to async work. Test the gaps:

ScenarioInjected conditionPassing behavior
Duplicate submitVisitor clicks twice or repeats after refreshOne job runs; the duplicate returns the existing job ID
Worker restartProcess exits after a side effectJob resumes without repeating the confirmed effect
Lost accessRequester is removed from the account mid-runNext protected step stops; result is not delivered
Late inputVisitor responds after the deadlineExpired job stays closed; a fresh job is offered
Partial cancellationSome steps completed before cancelRemaining work stops and completed effects are listed
Notification failureEmail or push delivery failsResult remains available in the authenticated status view
Stale callbackAn old attempt reports success after a retryAttempt ordering prevents the job from regressing state

Measure acceptance-to-start time, time spent in each state, jobs waiting past their deadline, duplicate suppression, retry success, cancellation latency, notification delivery, and result retrieval. Completion rate alone can hide jobs that finished correctly but never reached the customer.

Review a sample of receipts beside the conversation. The wording should let a support person answer three questions without querying several systems: What did the customer authorize? What actually happened? What can safely happen next?

Let the Customer Leave Without Losing Control

Long-running AI work changes the basic promise of a chatbot. The conversation no longer needs to remain open, but the commitment must remain visible. A durable job contract, explicit states, careful checkpoints, separate controls, renewed authorization, and a final receipt give the customer that continuity.

Design the hours between request and result with the same care as the first chatbot reply. That is where an impressive agent becomes a dependable service.

Build your chatbot for free →

No credit card required.

Empieza gratisNo se requiere tarjeta de crédito