Skip to main content
Every chat completion you create through the API is saved as a chat, but that chat is headless by default. It does not appear in anyone’s chat history, not in the GC AI web app and not in the API’s own chat search. It exists only as a record you can reach by its chat_id. To put it into a person’s history, you materialize it.

Why API chats are hidden by default

When you call POST /chat/completions, GC AI creates a chat to hold the exchange and returns its chat_id in the result envelope. Most API traffic is automated, though: batch contract reviews, a triage bot, a nightly job. If every one of those calls dropped a chat into someone’s sidebar, real users’ histories would fill up with machine-generated chats they never started. So API chats start headless. The chat is saved and reachable by chat_id, but it stays out of every human-facing listing until a person explicitly claims it by materializing it.

What “hidden” means

An API chat that has not been materialized:
  • Does not appear in the web app. It never shows up in the chat sidebar or history.
  • Does not appear in API chat search. GET /chat/search searches the caller’s chat history, and the chat is not in it, so search will not return it.
  • Addressable only by chat_id. Visibility is about listings, not access. The chat is still reachable directly by the chat_id from the completion response, but you have to hold that id yourself. Capture it (and the answer text) at the time of the call; an unmaterialized chat whose chat_id you never kept is effectively lost.
Materialization is not a web-app-only flag. The same gate controls both the web sidebar and API chat search, so an unmaterialized chat is hidden in both. Materializing it makes the chat show up in both at once.

Materializing a chat

There are two ways to do it. If you already know at request time that a person is meant to open the chat, set materialize: true on the completion and skip the second call:
The response carries a chat_url you can hand straight to a person. If chat_url is missing, materialization did not happen and the chat is still headless, so fall back to the endpoint below. Otherwise call POST /chat/{id}/materialize with the chat_id from a completed POST /chat/completions response:
Materializing a chat does four things:
  • Adds it to chat history, so it shows up in the GC AI web app and becomes findable through GET /chat/search.
  • Sets who can use it. An organization key shares the chat with the whole organization. See Who can materialize what.
  • Returns a shareable deep link (chat_url) that opens the chat in the web app.
  • Is idempotent. Calling it again on an already-materialized chat returns the same link and changes nothing else.
A common pattern is to run completions fire-and-forget and then materialize only the chats a person actually needs to see, such as the ones a review flagged, rather than every chat the integration creates. That is the case the two-call shape exists for. Use the inline materialize: true flag when every chat in a flow is meant for a person, for example one chat per matter that an attorney opens from your own system.
Filing a chat into a project with project_id is not the same as materializing it. project_id grounds the completion in the project’s files and records the chat under that project, but the chat stays out of GET /projects/{id}/chats and out of the web app until it is materialized. If you file chats into projects for people to browse, materialize them too.

Who can materialize what

Materialization claims a chat into a person’s or an organization’s history, so it is scoped to the key that created the chat: When an organization key materializes a chat that another organization key created, the chat is shared with the whole organization. An organization key has no user identity, so the chat has no owner, and every member gets full access: they can open it from the shared link and keep chatting in it themselves. A chat a personal key created keeps that user as its owner. Materializing it puts it in that user’s history. Teammates reach it only through the normal sharing rules, the same as a chat the user started in the web app. Use an organization key when you want the whole team to be able to pick up the conversation. See API Keys for the difference between the two key types. GET /chat/search runs hybrid search over the caller’s chat history. Two things follow from how visibility works:
  • Search needs a personal key. Chat history is per-user, so chat search rejects organization keys with 400. Use a personal key.
  • Search only finds materialized chats. An API chat is not in anyone’s history until it is materialized, so it cannot show up in search before then. If you want an integration’s chats to be searchable later, materialize the ones worth keeping.