Conversations API¶
Keep multi-turn state on the server, in Amazon Bedrock session storage in your own AWS account. A conversation holds the items of an exchange — user messages, model output, reasoning and tool calls — so each new turn only has to send the new message.
At a glance¶
- Send only the new turn. Pass a conversation ID as
conversationon a Responses request: the conversation's items become the input prefix, and both the request input and the response output are added back to it. - Explicit item control. Add, list, retrieve and delete items yourself, independently of any model call — 1 to 20 items per add request.
- Attached metadata. Up to 16 key-value pairs per conversation, merged on update and removable key by key.
- Cursor pagination. List items newest- or oldest-first, up to 100 per page, with the
aftercursor. - No gateway state. The thread lives in your AWS account, not in a gateway instance, so any instance behind a load balancer serves any conversation.
- Differs from OpenAI: a conversation expires 30 days after it is created, the window Amazon Bedrock session storage sets.
curl -X POST "$BASE/v1/conversations" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"metadata": {"topic": "travel"}}'
Endpoints¶
| Endpoint | Method | What It Does | MCP Tool |
|---|---|---|---|
/v1/conversations | POST | Create a conversation | openai_conversation |
/v1/conversations/{conversation_id} | GET | Retrieve a conversation | openai_conversation_get |
/v1/conversations/{conversation_id} | POST | Update the conversation's metadata | openai_conversation_update |
/v1/conversations/{conversation_id} | DELETE | Delete a conversation and its items | openai_conversation_delete |
/v1/conversations/{conversation_id}/items | POST | Add items to a conversation | openai_conversation_items |
/v1/conversations/{conversation_id}/items | GET | List a conversation's items | openai_conversation_items_list |
/v1/conversations/{conversation_id}/items/{item_id} | GET | Retrieve one item | openai_conversation_item_get |
/v1/conversations/{conversation_id}/items/{item_id} | DELETE | Delete one item | openai_conversation_item_delete |
Feature compatibility¶
The Notes column says what happens to a field this API does not honor: accepted and ignored when the field only annotates the request, rejected with 400 when honoring it is the request.
| Feature | Status | Notes |
|---|---|---|
| Conversation | ||
items | Up to 20 initial items, prepared before the conversation is created, so a rejected item leaves no empty conversation behind | |
metadata | The limits in Metadata. A key given a null value is accepted and dropped rather than stored; on an update that same null removes the key | |
| A request with no body | Both fields are optional, and the body itself may be omitted | |
| Retrieve, update, delete | metadata is the only field an update takes; a delete removes the conversation and every item it holds | |
| Items | ||
items on an add | 1 to 20 per request; the response is a list envelope of the items added, in that order, not the whole conversation | |
| Item shapes | The Responses input and output items: messages, reasoning items, tool calls and their outputs, and the additional_tools items a client replays from a prior transcript | |
| Items that configure a request rather than record history | A compaction_trigger instructs the request it is sent with, and no conversation item type can express it, so it is accepted and left out of the conversation. The rest of the batch is stored | |
A message content sent as a string | Expanded into an input_text or output_text part according to the message's role | |
An id sent on a new item | Accepted and ignored — the server mints the identifier, prefixed by the item's type | |
item_reference items | One naming an item the conversation already holds is refused with 400 item_already_in_conversation, as upstream. Upstream also copies in an item held by another conversation or a stored response; here an item is reachable only through the conversation holding it, so such a reference answers 404 | |
| Retrieve and delete one item | A delete returns the conversation, and the item leaves both the listing and the prefix of the next Responses turn | |
| Listing items | ||
order, limit, after | Bounds and cursor semantics in Listing | |
include | Accepted on the listing, on a retrieval and on an add. Only reasoning.encrypted_content changes the response; every other value is accepted and ignored | |
first_id / last_id / has_more | Populated on every page; the server never auto-paginates | |
| Lifecycle | ||
| Conversation lifetime | 30 days from creation, after which every route on it answers 404. Amazon Bedrock session storage sets the window and it cannot be extended | |
| Conversation length | Adds and deletes keep succeeding, but a listing reads at most 1,000 invocation steps and stops early past that — see Limits |
Legend:
- Supported — Fully compatible with OpenAI API
- Partial — Supported with limitations
No model is involved
Every endpoint on this page is state management: items are stored and read back as they were sent. Nothing here embeds, summarises or re-runs an item, so no endpoint takes a model identifier and a conversation behaves the same whatever the deployment's catalog holds. Summarising a long exchange is POST /v1/responses/compact, on the Responses API.
Working with conversations¶
Using a Conversation with the Responses API¶
| Request | Effect |
|---|---|
conversation="conv-..." | The conversation's items are prepended to input; the request input and the response output are appended to the conversation. |
conversation={"id": "conv-..."} | Same; both forms are accepted. |
conversation=..., store=false | The conversation is still used as the input prefix, but nothing is added to it. |
conversation=..., stream=true | Items are added once the stream has ended, and the terminal event carries the conversation. |
conversation=... and previous_response_id=... | Rejected with 400 (mutually_exclusive_parameters) — pick one way of continuing the exchange. |
The response echoes the conversation it belongs to as "conversation": {"id": "conv-..."}. A response that fails before generating anything adds nothing to the conversation.
conversation is also accepted on /v1/responses/input_tokens, where the conversation's items are counted ahead of input.
Items¶
Items use the same shapes as the Responses API input and output: messages, reasoning items, function calls and their outputs.
- Item IDs are assigned by the server. An
idsent on a new item is ignored. - Adding items returns the items that were added, as a
listenvelope in the order they were sent — not the whole conversation. item_referenceitems ask for an item that already exists to be brought into the conversation. One naming an item the conversation already holds is refused with400 item_already_in_conversation; send the item itself to add a second copy. An item held by another conversation or by a stored response cannot be reached here and returns404.- An item that configures a request rather than recording history — a
compaction_trigger— is accepted and left out of the conversation: no conversation item type can express it, so it never appears in a listing, in a retrieval, or in the page counts. Every other item of the same request is stored. - Deleting an item returns the conversation, and the item disappears from the listing.
include=reasoning.encrypted_contentreturns the encrypted content of reasoning items; otherincludevalues are accepted and ignored.
Listing¶
| Parameter | Default | Notes |
|---|---|---|
order | desc | asc is conversation order. |
limit | 20 | Up to 100 items per page; 0 returns an empty page. |
after | — | An item ID; only items strictly after it are returned. An ID that is not in the conversation returns 404. |
include | — | Extra item fields to return. |
Each page carries first_id, last_id and has_more. Pass the page's last_id as after to read the next page; the server never auto-paginates.
Metadata¶
| Limit | Value |
|---|---|
| Key-value pairs | 16 |
| Key length | 64 |
| Value length | 512 |
Updating merges: keys that are not sent keep their value, and a key sent as null is removed. metadata is required on update — omitting it returns 400 (missing_required_parameter), and sending null returns 400 (invalid_type).
Limits and behaviour to know¶
| Limit | Value |
|---|---|
| Items per add request | 20 |
| Invocation steps read when listing a conversation | 1,000 — a large item spans several, so a listing can stop early |
| Conversation lifetime | 30 days after creation |
Adding and deleting items is never rejected on a count: past 1,000 invocation steps a listing stops early instead, so it returns only part of the conversation and the prefix handed to the next Responses turn is truncated with it. Start a new conversation to continue an exchange that has reached the limit, and see Troubleshooting if a listing stops returning items earlier than expected.
Past the 30-day lifetime every route on a conversation answers 404, so an exchange that has to outlive the window keeps its own copy of the items.
Errors¶
| Status | When |
|---|---|
400 | A malformed conversation_id or item_id, a metadata limit, an empty or oversized items list, conversation combined with previous_response_id, or a phase on a message whose role is not assistant (unknown_parameter) — on an assistant message it is accepted and dropped rather than stored. |
404 | A well-formed identifier that names no conversation or item, including one created on another provider. |
Prerequisites¶
Conversations are stored in Amazon Bedrock session storage in your own account. The gateway's IAM role needs the Bedrock Session Storage permissions; without them, conversation requests fail with 503. Set AWS_BEDROCK_SESSION_ENCRYPTION_KEY_ARN to encrypt conversation content with your own AWS KMS key.
Try it¶
from openai import OpenAI
client = OpenAI(base_url="https://your-gateway/v1", api_key="YOUR_API_KEY")
conversation = client.conversations.create(metadata={"topic": "travel"})
first = client.responses.create(
model="amazon.nova-micro-v1:0",
input="My favourite city is Lisbon.",
conversation=conversation.id,
)
second = client.responses.create(
model="amazon.nova-micro-v1:0",
input="Which city did I name?",
conversation=conversation.id,
)
print(second.output_text)
The second request carries no history: the conversation supplies it.
Next steps¶
Next: Responses API · Session storage settings · IAM permissions