---
title: Stateless APIs for Stateful Conversations
description: "AI agent conversations are stateful but REST APIs are stateless. Session tokens, context passing, offer validity windows, and conversation-aware rate limiting bridge the gap."
canonical: https://nowah.xyz/blog/stateless-apis-stateful-conversations
lastModified: "2026-08-07T08:12:23.763Z"
---

# Stateless APIs for Stateful Conversations

AI agent conversations are stateful but REST APIs are stateless. Session tokens, context passing, offer validity windows, and conversation-aware rate limiting bridge the gap.

An agent searched for a flight. Found a good option. Then tried to book it. The booking endpoint returned: "Offer not found." The agent had the offer ID from the search response. But between the search and the booking, the session context had been lost. The booking endpoint had no way to connect the booking request to the search that produced the offer.

This is the fundamental tension between REST APIs and AI agent conversations. REST is stateless by design — each request is independent, carrying all the context it needs. Agent conversations are stateful by nature — each message builds on the previous ones, preferences accumulate, and decisions reference earlier context.

Bridging this gap without abandoning REST's statelessness requires a few specific patterns.

## Session tokens

![Illustration for this section](https://pics.nowah.xyz/website-media/developer-experience-034-img-1-session-flow.webp)

A session token is a lightweight reference that carries conversation context across stateless API calls. When an agent starts a conversation, the API creates a session and returns a token. Subsequent requests include the token, allowing the API to retrieve the session context without storing state in the connection itself.

The token is not a session cookie. It is an explicit parameter in the request, typically in a header or the request body. The agent manages it like any other parameter — receiving it from one API response and including it in the next request.

```
POST /flights/search
X-Session-Token: ses_abc123

{
 "origin": "JFK",
 "destination": "CDG",
 "departureDate": "2026-06-15"
}
```

The session token lets the API do several useful things:

- **Retrieve conversation context.** The search knows that the user previously expressed a preference for direct flights, so it weights direct options higher.
- **Link related requests.** The booking endpoint can validate that the offer ID came from a search within this session.
- **Maintain \[agentic memory\]\(/blog/agentic\-memory\-smarter\-over\-time\)\.** Across conversations, the session token links to a persistent memory store that remembers the user's preferences, past trips, and travel patterns.

The API remains stateless. Each request is self-contained — the session token is just another parameter. But the session token gives the server a key to look up accumulated context, which makes the response smarter.

## Context passing through offer IDs

The most common state-bridging pattern is the offer ID. A search returns offers, each with a unique ID. The booking endpoint accepts an offer ID, which references the specific search result including its price, availability, and terms.

The offer ID is a time-limited reference. It expires after a validity window (typically 15-30 minutes for flights). After expiry, the offer must be re-searched because the price may have changed.

This pattern preserves REST statelessness while enabling multi-step workflows:

1. \`POST /flights/search\` returns offers with IDs: \`flt\_001\`, \`flt\_002\`, \`flt\_003\`\.
2. \`POST /bookings\` accepts \`offerId: "flt\_001"\` and creates the booking\.

The booking endpoint does not need to know about the search that produced the offer. It validates the offer ID against the stored offer data (price, availability, terms) and processes the booking. The state lives in the offer record, not in the connection between requests.

For agents, this is straightforward. The agent receives offer IDs from the search, presents them to the user, and submits the selected one to the booking endpoint. The agent does not need to manage complex state — it just passes the ID.

## Offer validity windows

![Supporting diagram](https://pics.nowah.xyz/website-media/developer-experience-034-img-2-validity-window.webp)

Offer IDs expire for good reason. Flight prices change constantly. An offer that was $500 during the search might be $550 ten minutes later. Allowing bookings on stale offers would create a discrepancy between the quoted price and the actual charge.

The validity window creates a bounded time during which the offer is guaranteed at the quoted price. Within the window, the booking endpoint honors the price from the search. After the window, the offer ID is invalid and the agent must search again.

We include the validity window in the offer response:

```
{
 "id": "flt_001",
 "price": {"amount": 50000, "currency": "USD"},
 "validUntil": "2026-03-15T14:30:00Z",
 "validFor": 900
}
```

The agent can use \`validFor\` \(seconds\) to set a timer or \`validUntil\` \(absolute timestamp\) to check before proceeding\. If the offer is expired, the agent should search again rather than attempting to book\.

For fast-moving conversations, the 15-minute window is usually sufficient. The user searches, reviews options, selects one, confirms, and the booking processes within a few minutes. For slower conversations (the user steps away, comes back later), the agent needs to re-search and re-present offers.

## Conversation-aware rate limiting

Standard [rate limiting](/blog/rate-limiting-ai-agent-experience) counts requests per API key per time window. This works for traditional API usage where each request is independent. For agent conversations, it can create problems.

An agent handling multiple concurrent conversations on the same API key generates more traffic than a single conversation. If the rate limit is per-key, a busy agent with ten active conversations might exhaust the limit, causing failures for all conversations simultaneously.

We implement conversation-aware rate limiting that tracks usage per session token in addition to per API key. Each session gets a sub-limit within the key's overall limit. A runaway conversation that makes too many requests gets throttled without affecting other conversations on the same key.

The rate limit headers reflect both levels:

```
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 950
X-Session-RateLimit-Limit: 50
X-Session-RateLimit-Remaining: 47
```

An agent can monitor its per-session usage and pace its requests within a single conversation while knowing that other conversations are not affected.

## Persistent memory across conversations

Some state persists not just within a conversation but across conversations. A user's travel preferences (aisle seat, vegetarian meals, preferred airlines) are relevant in every session. Remembering them makes the agent better over time.

Our agentic memory system stores user-level context that persists across sessions. When an agent starts a new conversation, it retrieves the user's memory and uses it to personalize search parameters, ranking weights, and recommendations.

The memory is accessible through the session. When the API receives a request with a session token, it retrieves the associated user profile and applies the stored preferences. The agent does not need to re-ask for preferences it has learned in previous conversations.

This persistence happens transparently to the agent developer. Include a session token and the API handles the rest — retrieving preferences, applying them to the search, and storing new preferences learned during the conversation.

## Design patterns summary

Bridging stateless APIs and stateful conversations comes down to four patterns:

**Session tokens** carry conversation context without server-side session state. The token is a key to stored context, not the context itself.

**Offer IDs** link multi-step workflows (search to book) through time-limited references. State lives in the offer record, not in the connection.

**Validity windows** bound the time during which stateless references are valid, ensuring consistency between quoted and actual prices.

**Conversation-aware rate limiting** prevents one chatty session from affecting others, acknowledging that agent traffic has a session structure that flat rate limits do not capture.

These patterns keep the API RESTful while supporting the inherently stateful nature of travel conversations. The API does not maintain session state in the traditional sense — every request is still self-contained. But it provides the primitives (tokens, references, validity bounds) that let agents manage state on their side while keeping the API clean, cacheable, and scalable.

REST and statefulness are not opposites. They are design constraints that can coexist when you build the right abstractions. The session token is that abstraction — a lightweight bridge between stateless infrastructure and stateful user experiences.

---

Nowah is an AI travel agent that searches and books real flights and hotels through conversation — no filters, no thirty open tabs. [Plan your next trip](https://app.nowah.xyz).
