Skip to content

Glossary drawing · Bots, flows & AI

Conversation State Management

Also called: Conversation state · Dialogue state · Bot session state · Chatbot state

Note 1 —Storing where each customer is in a bot journey — current step, answers so far, last intent — so stateless webhook calls can pick up the thread.

Term
Conversation State Management
Abbreviation
—
Category
Bots, flows & AI
Level
Practitioner
Reviewed
Sheet no.
GLO-AUT-793
Drawn by
InfiQ Glossary

Drawing 1 · Interactive

Follow one customer's booking state

6 / 6
  • Idle to Awaiting service: taps Book
  • Awaiting service to Awaiting date: picks service
  • Awaiting date to Confirming: valid date
  • Confirming to Booked: taps Yes
  • Confirming to Awaiting date: taps Change
  • Booked to Idle: done
  • Awaiting service to Expired: silent 2 h
  • Awaiting date to Expired: silent 2 h
  • Expired to Idle: reset
  • Awaiting date to With agent: asks for a person
  • With agent to Idle: agent hands back
  1. Idle
  2. Awaiting service
  3. Awaiting date
  4. Confirming
  5. Booked
  6. Idle

Step 6 of 6 · end of walk

Idle

No journey in progress. Any message shows the main menu.

Arrived from Booked via done.

Can move to

  • Awaiting service · taps Book
A salon booking bot as stored states. Walk the happy path, then switch walks to see a timeout reset the journey and an agent take over.

Happy path, step 6 of 6: Idle (via done). No journey in progress. Any message shows the main menu.

What is conversation state management?

Conversation state management is how a chatbot remembers, between messages, where each customer is in a journey. Every WhatsApp message reaches your server as an independent messages webhook with no memory of what came before, so the bot must store — and reload on every message — a small record per customer: the current step (awaiting_date), the answers collected so far, the last intent, timestamps and who currently owns the chat.

That record turns a series of stateless HTTP requests into a conversation. When a customer replies "tomorrow 5 pm", the stored step tells the bot this is the appointment time, not a new question. State is usually keyed by your phone_number_id together with the customer's WhatsApp ID (wa_id, or the business-scoped user ID where usernames apply), kept in a fast shared store such as Redis or a database table, and modelled as a finite state machine with explicit transitions.

It is your system's memory, not the model's. An LLM's context window holds what you send in one call and forgets it afterwards; conversation state persists across calls, survives restarts and can be read by your inbox, analytics and agents. It also has to respect time: a half-finished state should expire, and the 24-hour customer service window decides whether the bot can still pick up where it left off with free-form messages or needs a template.

Key takeaways

  • Conversation state management stores each customer's place in a bot journey so stateless webhooks can be read in context.
  • Key state by phone_number_id plus the customer's wa_id (or BSUID) and keep it in a shared store such as Redis, never in server memory.
  • Save the last processed wamid: Meta retries webhooks for up to 7 days, and a duplicate must not advance the journey twice.
  • Give every in-progress state a TTL and a reset path, and silence the bot while a human agent owns the chat.
  • State is your system's memory; the LLM context window is what one model call can see and forgets afterwards.

Implementation

Conversation state management in code

import { createClient } from "redis";
const redis = createClient({ url: process.env.REDIS_URL });
await redis.connect();
const TTL_SECONDS = 2 * 60 * 60; // abandon half-finished journeys after 2 hours
const key = (phoneNumberId, waId) => `conv:${phoneNumberId}:${waId}`;
export async function handleMessage(phoneNumberId, msg) {
const k = key(phoneNumberId, msg.from);
const state = JSON.parse((await redis.get(k)) ?? '{"step":"idle","owner":"bot"}');
if (state.lastWamid === msg.id) return; // webhook retry: already handled
if (state.owner === "agent") return; // a human owns the chat
const input = msg.interactive?.button_reply?.id ?? msg.text?.body ?? "";
const next = transition(state, input); // pure: (state, input) -> state + reply
next.lastWamid = msg.id;
next.updatedAt = Date.now();
await redis.set(k, JSON.stringify(next), { EX: TTL_SECONDS });
if (next.reply) await sendWhatsApp(phoneNumberId, msg.from, next.reply);
}

Load, transition, save with a TTL. In production, add a per-customer lock or optimistic version check so two parallel deliveries cannot race.

Operating sequence

How does conversation state management work?

  1. Choose the key

    Key state by business number plus customer: phone_number_id + wa_id (or BSUID). One customer messaging two of your numbers has two separate conversations.

  2. Model the journey as states

    List the steps — idle, awaiting_service, awaiting_date, confirming, booked, with_agent — and the events that move between them. Input that is not valid in the current state goes to a fallback.

  3. Load, decide, save — atomically

    On each webhook, load the record, compute the next state and reply, then save with a version check or a per-customer lock so two parallel deliveries cannot both advance it.

  4. Make it idempotent

    Store the last processed wamid. When a retry delivers the same message again, skip it instead of moving the journey forward twice.

  5. Expire and reset

    Give every in-progress state a TTL (30 minutes to a few hours is common). On expiry reset to idle and, if the customer returns, ask whether to continue. Support a reset word such as "menu".

  6. Coordinate with humans

    When the chat is handed to an agent, set the owner to the agent so the bot stays silent, and resume from a clean state when the agent hands back.

Design intent

Why does conversation state management matter?

Without reliable state, bots ask the same question twice, treat answers as new requests, or send two replies when a webhook is retried. Clean state management is what lets a bot cope with a customer who wanders off for an hour mid-booking, an agent who takes over and hands back, and thousands of conversations running in parallel across several server instances — without anyone losing their place.

Example: A Chennai salon's booking bot

A customer taps "Book" and picks "Haircut"; the bot saves {step: "awaiting_date", service: "haircut"}. She disappears for 40 minutes, then replies "Sunday 11". The record is still within its two-hour TTL, so the bot reads the reply as a date, offers 11:00 and 11:30 and moves to confirming. After a network blip Meta re-sends one webhook; the stored last wamid matches, so the duplicate is ignored. Had she replied the next evening, the state would have expired and the bot would have asked whether to restart the booking.

Failure modes

Common mistakes with conversation state management

  • Keeping state in server memory

    An in-process map disappears on every restart and is invisible to other instances behind a load balancer. Use a shared store.

  • No expiry

    A customer who abandoned a booking last week gets their next "hi" read as a date. Put a TTL on every in-progress state and reset to idle.

  • Race conditions

    Two quick messages or a retried webhook can be processed in parallel, both reading the old state. Use a per-customer lock, a queue partitioned by customer, or optimistic versioning.

  • Bot and agent both replying

    If handoff does not change state, the bot keeps answering while an agent types. Record the owner and silence the bot until the agent hands back.

  • Leaving the step to the model

    Asking an LLM to infer the current step from chat history is fragile. Store the step explicitly and pass it to the model as a fact.

Specifications

Conversation state management: key facts and limits

Key
phone_number_id + wa_id (or BSUID)
Typical store
Redis or a database table, with a TTL
Minimum fields
step, collected slots, owner, last wamid, timestamps
Webhook retries
Up to 7 days — deduplicate on wamid
Delivery order
Not guaranteed — never assume sequence
Free-form resume
Within 24 hours of the customer's last message

Recommended practice

Best practices for conversation state management

  • Keep the transition logic a pure function so every path is easy to unit-test.
  • Log each transition with the triggering wamid for debugging.
  • Offer escape words ("menu", "agent") that work from any state.
  • Store when the 24-hour window closes so the bot knows when only templates will work.

Conversation State Management FAQs

Why do chatbots need to store conversation state?+
Because each webhook arrives on its own, with no memory of earlier messages. Stored state tells the bot what the customer is answering, so "5 pm" is read as a time rather than as a new request.
Where should I store WhatsApp bot state?+
In a shared, fast store such as Redis or a database table, keyed by your phone number ID and the customer's WhatsApp ID. Avoid in-memory maps, which vanish on restart and do not work across several servers.
How long should conversation state last?+
Long enough for a customer to finish a journey — usually 30 minutes to a few hours for an in-progress flow. Long-term facts such as preferences belong in the customer profile, not in session state.
What is the difference between conversation state and an LLM's context window?+
Conversation state is your stored record and persists across messages; the context window is what a single model call can see, and it is forgotten afterwards. You load state first, then put the relevant parts into the context.
How do I stop duplicate webhooks from advancing the flow twice?+
Save the `wamid` of the last processed message in the state and skip any message with the same ID. Meta retries failed deliveries for up to seven days, so duplicates are normal, not exceptional.

See InfiQ pricing

Put Conversation State Management to work

InfiQ is an official Meta Business Partner. Start a 7-day free trial — no card needed — and see this running on your own number.