
Use encrypted content for agentic multi-turn
Continue an agentic tool conversation without storing history on xAI by asking the first turn to return encrypted reasoning and tool output, then appending that response into the next turn on the client. Official Advanced Usage documents use_encrypted_content=True on the first chat.create, streaming or sampling as usual, then chat.append(response) before the follow-up user message so the encrypted agentic state travels with your client. This path fits Zero Data Retention teams that cannot use Continue an agentic conversation with store_messages. Create a key and load credits at console.x.ai.
What you need
An XAI_API_KEY, server-side tools on the chat, and a client process that can hold the first response object (or OpenAI SDK include=["reasoning.encrypted_content"] output) until the next user turn. Neighboring jobs include Mix client-side and server-side tools on Grok when local functions also pause the loop, Send a safety_identifier on Grok API requests for per-user abuse attribution on the same key, and Include an image in an agentic tool request when the opener carries a picture. More API jobs live on the API hub.
Return encrypted state, then append it
- Create the first agentic chat with
use_encrypted_content=Trueand stream the opener:
import os
from xai_sdk import Client
from xai_sdk.chat import user
from xai_sdk.tools import web_search, x_search
client = Client(api_key=os.getenv("XAI_API_KEY"))
chat = client.chat.create(
model="grok-4.7",
tools=[web_search(), x_search()],
use_encrypted_content=True,
)
chat.append(user("What is xAI?"))
print("##### First turn #####")
for response, chunk in chat.stream():
print(chunk.content, end="", flush=True)
print("\nUsage for first turn:", response.server_side_tool_usage)
- Append the full response object into the same chat (or into the conversation you will continue), then add the next user message and stream again:
chat.append(response)
print("\n##### Second turn #####")
chat.append(user("What is its latest mission?"))
for response, chunk in chat.stream():
print(chunk.content, end="", flush=True)
print("\nUsage for second turn:", response.server_side_tool_usage)
With the OpenAI SDK on Responses, request encrypted reasoning on the first call via
include=["reasoning.encrypted_content"], extend your localinput_listwithresponse.output, append any client-sidefunction_call_outputitems, and create the next response with that full input list so the encrypted blocks stay in context withoutstore_messages.When mixing client-side tools under encrypted mode, append the response each stream cycle, execute local tools, append
tool_result/function_call_output, and only break when a turn returns no client-side calls — the same loop shape as the mixed-tools guide, without a remoteprevious_response_id.
When encrypted content is the right default
Pick this path when ZDR is on, when compliance forbids server-side conversation retention, or when you already persist transcript bytes in your own store and only need the encrypted agentic payload to keep tool reasoning coherent. Prefer store_messages when a short response id is enough and retention policy allows it.
Pitfalls
Skipping chat.append(response) before the second user message drops encrypted tool state and the follow-up looks like a cold start. Combining store_messages=True and use_encrypted_content=True without a clear ownership rule doubles the mental model — pick one continuation strategy per flow. Dropping encrypted blobs from your persisted transcript between turns breaks ZDR-safe resume. Mixing Console API credits with SuperGrok weekly pools on grok.com confuses two different meters the docs keep separate.