> ## Documentation Index
> Fetch the complete documentation index at: https://game-narrative.docs.kitefrost.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# GraphQL API

> Query complex state, run typed mutations, and stream real-time events over a single GraphQL endpoint.

Alongside the REST API, KiteFrost serves a GraphQL endpoint for the cases where
REST is awkward: fetching several related things in one round-trip, and
subscribing to real-time events. **Start with REST** for onboarding and simple
writes; reach for GraphQL when your reads become graph-shaped or you need a
live stream.

## Endpoint

```
POST https://api.kitefrost.ai/v1/graphql
```

Authentication is identical to REST: send your API key as a Bearer token.

```bash theme={null}
curl https://api.kitefrost.ai/v1/graphql \
  -H "Authorization: Bearer sk_your_secret_key_here" \
  -H "Content-Type: application/json" \
  -d '{"query": "query($id:String!){ project(id:$id){ id } }", "variables": {"id": "my-project"}}'
```

The response is the standard GraphQL envelope. GraphQL validation and execution
errors are returned **in-band** with HTTP 200, in the `errors` array:

```json theme={null}
{
  "data": { "project": { "id": "my-project" } },
  "errors": null
}
```

Each error carries an `extensions.code` that lines up with the REST error
codes (for example `budget_exceeded`, `content_policy_violation`,
`invalid_byok_key`), plus a `request_id` for support.

## Get the schema

Schema introspection is disabled in production. To discover the schema, fetch
the SDL from the authenticated schema endpoint:

```bash theme={null}
curl https://api.kitefrost.ai/v1/graphql/schema \
  -H "Authorization: Bearer sk_your_secret_key_here"
```

It returns the SDL as `text/plain`, with an `ETag` (use `If-None-Match` for
conditional requests) and an `X-Schema-Pack` header naming the edition your key
sees. A static copy is also published for each launched product edition so you
can run codegen in CI without a live call.

There is **no separate GraphQL SDK** - the SDL *is* the contract. Run your own
codegen against it (for example `graphql-codegen` for TypeScript or `gql` for
Python) to get fully-typed clients in your stack.

## From the SDKs

There is no purpose-built `graphql()` method on the current per-pack SDKs
(`kitefrost-game-narrative`, `kitefrost-ttrpg-gm`) yet. Every per-pack
client's shared core transport is a generic HTTP client that reuses your
client's auth and error mapping (`AuthError`/`RateLimited`/etc. raise the
same as any other call) - use it directly. The attribute name differs by
language: `client.core.transport` in Python, `client.core.http` in
TypeScript.

<CodeGroup>
  ```python Python theme={null}
  from kitefrost_game_narrative import GameNarrativeClient

  client = GameNarrativeClient.from_api_key("sk_your_secret_key_here")

  result = client.core.transport.post(
      "/v1/graphql",
      {
          "query": "query($id:String!){ project(id:$id){ id } }",
          "variables": {"id": "my-project"},
      },
  )
  print(result["data"]["project"]["id"])
  ```

  ```typescript TypeScript theme={null}
  import { GameNarrativeClient } from "@kitefrost/game-narrative";

  const client = new GameNarrativeClient({ apiKey: "sk_your_secret_key_here" });

  const result = await client.core.http.post<{ data: { project: { id: string } } }>(
    "/v1/graphql",
    {
      query: "query($id:String!){ project(id:$id){ id } }",
      variables: { id: "my-project" },
    },
  );
  console.log(result.data.project.id);
  ```
</CodeGroup>

<Note>
  No automatic retry beyond a one-shot re-auth on a 401 - same as any other
  SDK call. See [SDKs overview](../sdks/overview) for the full error-handling
  model.
</Note>

## Real-time events

The GraphQL surface offers a subscription for streaming project events as they
happen - useful for keeping multiple clients in sync without polling. Streaming
delivery is currently in beta; see the SDL for the subscription shape.

## Limits

* Queries are bounded by depth and complexity limits; very deep or very wide
  documents are rejected with a validation error before execution.
* Standard per-tier rate limits apply, identical to REST. See
  [Authentication](../core-concepts/auth).

## When to use GraphQL vs REST

| Use REST when                         | Use GraphQL when                                        |
| ------------------------------------- | ------------------------------------------------------- |
| Onboarding, first integration         | Your reads span several related records                 |
| Simple create / update / delete       | You want exactly the fields you need, in one round-trip |
| You want the smallest possible client | You need a real-time event stream                       |
