---
title: "Create chat completion"
method: POST
path: "/chat/completions"
tags: ["Chat"]
---

# Create chat completion

`POST /chat/completions`

## Request

- Base URL: `https://app.gc.ai/api/external/v1`
- URL: `https://app.gc.ai/api/external/v1/chat/completions`
- Auth: API key in header `Authorization`
- TypeScript: <https://skmtc.dev/gc-ai/apis/gc-ai-external-api/docs/chat/completions/post.ts> (a function with typed inputs; its response is checked against the schema)

Send a message to GC AI and receive an AI-generated response.

<Note>
This endpoint is asynchronous: it returns a job envelope, and the result is filled in once the job completes. See [Asynchronous Requests](https://docs.gc.ai/api-reference/concepts/async-jobs) for how waiting, polling, and the envelope work.
</Note>

## Multi-turn conversations

Every response includes a `chat_id`. Pass it back as `chat_id` on a later request to continue that conversation. Conversation state (prior messages, tool calls, and any returned documents, emails, or diagrams) is held server-side, so each turn sends only the new `message` (plus any new `file_ids`); never re-send prior turns or previously returned outputs.

Only one turn may be in flight per chat at a time: a request whose `chat_id` already has a turn running is rejected with `409`. Wait for the prior turn to reach a terminal state before sending the next.

Continuing a chat is scoped like [materialization](https://docs.gc.ai/api-reference/concepts/chat-visibility): personal keys continue chats they created; organization keys continue organization chats. See [Multi-turn Conversations](https://docs.gc.ai/api-reference/concepts/multi-turn).

## Server-side tools

The model can invoke tools automatically while generating a response. Tool calls happen server-side. Only the final assistant text is returned in `result.result`.

| Tool | Description | Key type |
|---|---|---|
| Document reading | Read, query, and search within attached files | All |
| Web search | Search the web for current information | All |
| Research agent | Multi-step web research with synthesis | All |
| Case-law lookup | Search case law databases | User-scoped only |
| Playbook review | Run playbook checks against attached files (requires `playbook_ids`) | All |
| Document editing | Apply redline edits to an attached DOCX. All proposed revisions are auto-accepted; the edited file is returned via `result.documents`. The original file is preserved. | All |
| Document generation | Generate a new DOCX from a prompt. The generated file is returned via `result.documents`. | All |
| Slide generation | Generate a new PowerPoint deck (PPTX) from a prompt. The generated file is returned via `result.documents`. | User-scoped only |
| Email drafting | Draft a structured email from a prompt. The draft is returned via `result.emails` (`to`, `subject`, `body`, …). | All |
| Diagrams | Generate a diagram from a prompt. The diagram is returned via `result.diagrams` as validated Mermaid source (`mermaid`) you can render with any Mermaid-compatible renderer. | All |
| Vault query | Query documents and fields in an attached Contract Intelligence vault (requires `vault_id`) | All (subject to vault authorization) |

The following tools are **not available** via API (the response is plain text with no channel for rich outputs):

| Tool | Reason unavailable |
|---|---|
| Interactive clarification | Requires a back-and-forth channel with the caller |

<Note>
This is an inference endpoint and is [rate limited](https://docs.gc.ai/api-reference/concepts/rate-limits): 60 requests/minute per organization (20 per API key). Exceeding the limit returns `429` with a `Retry-After` header.
</Note>

## Query parameters

- `wait` integer, nullable: Optional long-poll wait time in seconds. Use `0` for fire-and-forget behavior. If both `wait` and `Prefer: wait=...` are supplied, they must match. Values above 90 are clamped.

## Headers

- `Prefer` string: Optional RFC 7240 wait preference, for example `wait=0`. If both this header and the `wait` query parameter are supplied, they must match.

## Request body

- ChatCompletionRequest
  - `message` string, required: The user's message or prompt
  - `file_ids` string[]: Optional uploaded file IDs to attach as context for this completion. Upload files first via `POST /files`.
  - `playbook_ids` string[]: Optional playbook IDs to ground the completion in (up to 20). The model uses each playbook's checks and guidance to structure its review of the attached files. Discover playbooks via `GET /playbooks`. Org-scoped keys can use org-visible and official playbooks.
  - `playbook_id` string: Deprecated and no longer accepted. Use `playbook_ids` (an array) instead. Requests that include this field are rejected with a 400 so the playbook is never silently dropped.
  - `skill_ids` string[]: Optional skill IDs to run on the first turn. Each skill's instructions are injected as context for this completion. Discover skills via `GET /skills`. Only valid when starting a new chat; omit them when continuing with `chat_id`.
  - `blocked_tools` string[]: Optional GC AI actions to leave out of this completion, by tool name. This can only remove actions the user who owns the key allows. It never adds one.
  - `auto_accept` boolean: Set to `true` to let a safety check approve GC AI actions that would otherwise need a person, one call at a time. It works only when the user who owns the key has turned on the safety check. It never approves actions that delete, share or send. When the check says no, that step is skipped and listed in `skipped_steps`.
  - `chat_id` string, uuid: Optional chat ID to continue an existing conversation, from the `chat_id` of a prior completion. Omit to start a new chat. Conversation state is held server-side, so send only your new `message` (plus any new `file_ids`); do **not** re-send prior turns or previously returned documents/emails/diagrams. Only one turn may be in flight per chat at a time. See [Multi-turn Conversations](https://docs.gc.ai/api-reference/concepts/multi-turn).
  - `project_id` string, uuid: Optional project to file the chat into. Requires write access to the project. Discover projects via `GET /projects`. This does two things: it grounds the completion in the project's files, and it files the chat under that project. Filing alone does not make the chat visible to people, because API chats stay out of chat history until they are materialized. Pass `materialize: true` (or call `POST /chat/{id}/materialize` afterwards) for the chat to appear under `GET /projects/{id}/chats` and in the GC AI web app. See [Chat Visibility](https://docs.gc.ai/api-reference/concepts/chat-visibility).
  - `materialize` boolean: Surface this chat into chat history as part of the same call, instead of making a second request to `POST /chat/{id}/materialize`. Defaults to `false`, which keeps the chat headless. Set this when a person is meant to open the chat. An organization-scoped key shares the materialized chat with the whole organization, so any member can open it and keep chatting in it. A user-scoped key keeps the chat owned by the caller. On success the response includes `chat_url`. If that field is absent, materialization did not happen and the chat is still headless; retry with `POST /chat/{id}/materialize` using the returned `chat_id`. Continuing an already-materialized chat is a no-op. See [Chat Visibility](https://docs.gc.ai/api-reference/concepts/chat-visibility).
  - `company_id` string, uuid: Optional company profile to ground the completion in, so the model has that company's context (industry, jurisdiction, regulations, risk posture). Discover company profiles via `GET /company-profiles`. The company is fixed for the life of a chat. On a new chat, supply this to target a specific company (the only way to ground against a particular one in a multi-company organization); when omitted, a company is auto-resolved: user-scoped keys use the caller's active (default) company, and org-scoped keys use the organization's sole company, or none when there are several. When continuing a chat with `chat_id`, omit this to reuse the chat's company. You may echo the same `company_id`, but a different one is rejected with `409` (start a new chat to use a different company).
  - `vault_id` string, uuid: Optional Contract Intelligence vault to ground the completion in, so the model can query that vault's documents and fields. Copy the vault ID from the GC AI web app. **Authorization:** User-scoped keys need the Vault Chat permission, Contract Intelligence access, and view access to the vault. Organization-scoped keys need the organization to have Contract Intelligence entitlement; they can attach any vault in the organization because organization keys have no user identity for per-vault access checks. The vault is fixed for the life of a chat. When continuing with `chat_id`, omit this to reuse the chat's vault. You may echo the same `vault_id`, but a different one is rejected with `409` (start a new chat to use a different vault). When also filing into a project (`project_id`) that is linked to a vault, `vault_id` must match that vault. A different one is rejected with `409`; omit `vault_id` to use the project's vault.

## Response `200`

Job completed within the effective wait window

- ChatCompletionResponse
  - `job_id` string, uuid, required: Async job identifier
  - `kind` 'chat/completions', required: Stable job kind for this endpoint
  - `status` 'pending' | 'running' | 'succeeded' | 'failed' | 'canceled', required: Current job status
  - `result` ChatCompletionResult, nullable, required: Completion payload when the job has succeeded
    - `result` string, required: The AI-generated response text
    - `chat_id` string, uuid, required: The chat ID. API chats stay out of chat history until you materialize them. Pass `materialize: true` on the request to surface the chat in the same call, or pass this ID to `POST /chat/{id}/materialize` afterwards. See [Chat Visibility](https://docs.gc.ai/api-reference/concepts/chat-visibility).
    - `chat_url` string, uri: Deep link that opens the chat in the GC AI web app. Present only when the request set `materialize: true` and the chat was surfaced, so its absence means the chat is still headless.
    - `documents` ApiDocument[]: Documents produced during this completion: either edits of an attached file (the document editing tool) or newly generated files (the document or slide generation tools). Present only when at least one document was produced. Each entry exposes a new `file_id` and a signed download URL; edits also reference the preserved original via `original_file_id`.
      - `file_id` string, uuid, required: The new uploaded file ID for the produced document. Use it with `POST /chat/completions` `file_ids` to apply further edits to this version. Download via `signed_url` for the finished file.
      - `original_file_id` string, uuid: The `file_id` the changes were applied to (edits only). The original file is preserved unchanged. Omitted for newly generated documents.
      - `filename` string, required: Filename of the produced document (edits are prefixed with `edited_`).
      - `original_filename` string: Filename of the source file the changes were applied to (edits only). Omitted for newly generated documents.
      - `signed_url` string, uri, required: Time-limited direct download URL (no login required). Valid for 7 days from job completion. Download promptly; the URL is not re-minted, so re-fetching the job after expiry returns the same expired URL. For a link you intend to store or email, use `download_url` instead.
      - `download_url` string, uri, required: Permanent, login-gated download link for the produced document. Unlike `signed_url` it never expires: it points at a GC AI web page that mints a fresh download for a signed-in user with access to the file, and redirects to GC AI login otherwise. Use this for links you store or email. The produced document inherits the source document's access, so it opens for whoever can already see the source: a document uploaded to the organization is org-visible, one in a project is visible to that project's members, and one in personal files stays private to the uploader. Recipients still need a GC AI login.
    - `emails` ApiEmail[]: Email drafts produced during this completion by the email drafting tool. Present only when at least one email was drafted. Each entry is a structured draft (`to`, `subject`, `body`, …) ready to send via your own mail client or provider.
      - `to` string, required: Recipient email address(es), comma-separated. May be an empty string when the prompt named no recipient; fill it in before sending.
      - `cc` string: CC recipient email address(es), comma-separated.
      - `bcc` string: BCC recipient email address(es), comma-separated.
      - `subject` string, required: Email subject line. Always present, inferred from context when the prompt did not specify one.
      - `body` string, required: Email body in markdown.
      - `plaintext_body` string, required: Email body with markdown stripped, for plain-text clients.
    - `diagrams` ApiDiagram[]: Diagrams produced during this completion by the diagram tool. Present only when at least one diagram was produced. Each entry exposes validated Mermaid source (`mermaid`) you can render with any Mermaid-compatible renderer.
      - `title` string, required: A short descriptive title for the diagram.
      - `diagram_type` string, required: The semantic type of diagram, e.g. orgChart, dealStructure, workflow, timeline, sequence, entityRelationship, or general.
      - `mermaid` string, required: The diagram source as validated Mermaid syntax. Render it with any Mermaid-compatible renderer.
    - `judge_approved_steps` string[]: GC AI actions, by tool name, that the safety check approved during this completion. Present only when `auto_accept` was set and the check approved at least one step.
    - `skipped_steps` string[]: GC AI actions, by tool name, that needed a person to approve them and did not run. Present only when at least one step was skipped.
  - `error` object, nullable, required: Failure payload when the job has failed
    - `code` string, required
    - `message` string, required
  - `created_at` string, required: ISO 8601 creation timestamp
  - `completed_at` string, nullable, required: ISO 8601 completion timestamp, or null when not terminal

## Other responses

- `202`: Job accepted but not yet complete, including fire-and-forget requests
- `400`: Invalid request body or invalid/conflicting wait controls
- `401`: Missing or malformed API key
- `403`: Invalid API key, playbook review not enabled for this account, or Contract Intelligence is not enabled for this account (when `vault_id` is set)
- `404`: One or more `file_ids`, one or more `playbook_ids`, the `chat_id`, the `company_id`, or the `vault_id` were not found
- `409`: The `chat_id` refers to a non-API chat, a turn is already in progress for that chat, `company_id` / `vault_id` differs from the chat's pinned value (each is fixed for the life of a chat), or `vault_id` conflicts with the vault linked to `project_id`
- `429`: Rate limit exceeded. See [Rate Limits](https://docs.gc.ai/api-reference/concepts/rate-limits) for the tiers, limits, and how to back off.
- `500`: Internal server error
- `503`: Service temporarily unavailable

---

[API](https://skmtc.dev/gc-ai/apis/gc-ai-external-api.md) - [All operations](https://skmtc.dev/gc-ai/apis/gc-ai-external-api/llms.txt) - [OpenAPI document](https://skmtc.dev/gc-ai/apis/gc-ai-external-api/revisions/2bbdfddf7107?raw)
