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

# Unity SDK

> Install the Alakazam Unity package and generate programmable worlds and drop-in AI characters from C#.

The **Alakazam Portal** Unity package (`com.alakazam.portal`) gives you the whole `/v1` surface from C# — generate and manage worlds, create talk-to characters, mint runtime sessions — plus a drop-in **AI Character** component. Requires **Unity 6000.0+**.

## Install

Add the package from its git URL (Unity Package Manager → **+** → **Add package from git URL…**), or declare it in your manifest:

<CodeGroup>
  ```text Add package from git URL theme={null}
  https://github.com/Alakazam-studios/alakazam-unity.git
  ```

  ```json Packages/manifest.json theme={null}
  {
    "dependencies": {
      "com.alakazam.portal": "https://github.com/Alakazam-studios/alakazam-unity.git"
    }
  }
  ```
</CodeGroup>

## Setup

<Steps>
  <Step title="Open the Setup Wizard">
    The wizard opens automatically on first import, or launch it from the menu bar: **Alakazam → Setup Wizard**.
  </Step>

  <Step title="Paste your key">
    Start with an `sk_test_…` secret key ([get one here](https://play.alakazam.gg/?view=developer)) — it runs a free, mocked [sandbox](/testing). Swap to `sk_live_…` when you ship.
  </Step>

  <Step title="Set your data preferences">
    Accept the terms and choose your telemetry opt-ins. You're ready.
  </Step>
</Steps>

<Warning>
  **Secret keys are editor/server-only.** Your `sk_` key can create worlds and mint sessions, so never ship it inside a build. At runtime, a player talks to a world through a short-lived **session token** minted by your backend — the [two-token rule](/embedding).
</Warning>

## Programmable worlds & AI characters (`/v1`)

`AlakazamClient` is a thin C# wrapper over the [`/v1` API](/api-reference). Construct it with your secret key (or pass nothing in the editor to reuse the key from the Setup Wizard).

```csharp theme={null}
using AlakazamPortal;

var client = new AlakazamClient("sk_test_…");

// Generate a world from a premise
CreateWorldResponse world = await client.CreateWorld("a neon rooftop at night");
Debug.Log($"{world.worldId} · {world.slug} · mock={world.mock}");
```

List, fork, edit, and import compiled worlds:

```csharp theme={null}
string mine    = await client.ListWorlds(limit: 20);              // raw JSON
string forked  = await client.ForkWorld(world.worldId);
string edited  = await client.EditWorld(world.worldId, "add rain and a flickering sign");

// Import a pre-compiled SMWorld graph verbatim (no recompile)
CreateWorldResponse imported = await client.ImportWorld(compiledWorldJson, name: "My Level");
```

<Note>
  A `sk_test_…` key returns **deterministic mocks** — every response carries `mock: true`, and nothing spends GPU or quota. Build your whole integration for free, then swap to `sk_live_…`. See [Testing](/testing).
</Note>

### Drop-in AI Character

The **Alakazam Character** component wraps the session → say → speak loop as an NPC you can drop into any scene.

<Steps>
  <Step title="Add it to your scene">
    Menu bar: **AlakazamPortal → Add AI Character to Scene**. This creates an `AlakazamCharacter` (plus an `AudioSource` for spoken replies), or add the component to any GameObject via **Add Component → Alakazam → Alakazam Character (AI NPC)**.
  </Step>

  <Step title="Point it at a character">
    Create a character first (`client.CreateCharacter(...)`, below), then set **Character Id** and **World Id** on the component (they're the same id).
  </Step>

  <Step title="Authorize runtime turns">
    In the editor, leave **Session Token** empty — the component mints one with your stored secret key for prototyping. **In a shipped build, assign a `sessionToken` minted by your backend** and never ship the secret key.
  </Step>
</Steps>

Wire `OnReply` and call `Say(...)`. With `autoSpeak` on (default) and a `voice` AudioSource assigned, replies play back through TTS automatically:

```csharp theme={null}
using AlakazamPortal;
using UnityEngine;

public class Innkeeper : MonoBehaviour
{
    public AlakazamCharacter npc;   // the Alakazam Character component

    void Awake()
    {
        npc.OnReply += r => Debug.Log($"{r.stance}: {r.reply}");
        npc.OnError += Debug.LogError;
    }

    public async void Greet()
    {
        await npc.Say("hello");     // fires OnReply, then auto-speaks via the AudioSource
    }
}
```

To create the character the component talks to (an editor/server step — needs the secret key), and to mint the session token your backend would hand the client:

```csharp theme={null}
var npc      = await client.CreateCharacter("Iris", "a wry rooftop hacker");
SessionToken session = await client.MintSession(npc.id);   // do this on YOUR backend in production
SayResult    turn    = await client.CharacterSay(npc.id, session.token, "who are you?");
Debug.Log($"{turn.stance}: {turn.reply}");
```

The component also exposes `Speak(line)` (synthesize + play a line) and `ShowImage(prompt)` (returns a `show_image` src). For a public, token-free share link to a *public* world, use `AlakazamClient.PublicPlayUrl(slug)`.

## `AlakazamClient` reference

Every method maps 1:1 to a `/v1` endpoint. Typed methods deserialize the response; the rest return the raw JSON string.

| Method                                              | Auth           | Returns                                                  |
| --------------------------------------------------- | -------------- | -------------------------------------------------------- |
| `CreateWorld(premise, name?, frameUrl?, pov?)`      | secret         | `CreateWorldResponse` — `{ worldId, slug, cover, mock }` |
| `ImportWorld(worldJson, name?, description?)`       | secret         | `CreateWorldResponse` — `{ worldId, slug, imported }`    |
| `GetWorld(id)`                                      | secret         | JSON (graph nested under `world`)                        |
| `ListWorlds(limit = 20)`                            | secret         | JSON — `{ worlds, nextCursor }`                          |
| `ForkWorld(id)`                                     | secret         | JSON — `{ worldId }`                                     |
| `EditWorld(id, instruction)`                        | secret         | JSON — `{ world, diagnostics, reply }`                   |
| `LikeWorld(id)`                                     | publishable ok | —                                                        |
| `CreateCharacter(name, premise, authored = true)`   | secret         | `CreateCharacterResponse` — `{ id, slug, kind, mock }`   |
| `ListCharacters(limit = 20)`                        | secret         | JSON — `{ characters, nextCursor }`                      |
| `MintSession(worldId, playerIdentity?, origin?)`    | secret         | `SessionToken` — `{ token, expiresIn, slug }`            |
| `ConnectSession(sessionToken)`                      | session        | JSON — `{ worldId, reactorJwt, world }`                  |
| `RefreshSession(sessionToken)`                      | session        | JSON — `{ reactorJwt, expiresIn }`                       |
| `TrackSession(sessionToken, reactorSessionId)`      | session        | —                                                        |
| `EndSession(sessionToken)`                          | session        | JSON — `{ ok, seconds }`                                 |
| `CharacterSay(characterId, sessionToken, text)`     | session        | `SayResult` — `{ reply, stance, emotion, action }`       |
| `CharacterTts(characterId, sessionToken, text)`     | session        | `byte[]` — raw `audio/mpeg` (MP3)                        |
| `CharacterImage(characterId, sessionToken, prompt)` | session        | JSON — `{ src, generated }`                              |
| `GetUsage()`                                        | secret         | JSON — `{ day, usage, caps, plan }`                      |
| `PublicPlayUrl(slug)`                               | —              | `string` (static helper)                                 |

<Note>
  Optional params: `frameB64` on **CreateWorld** / **CreateCharacter** (seed creation from a base64
  image), `cursor` on **ListWorlds** / **ListCharacters** (pagination — pass the prior `nextCursor`),
  and `ttlSeconds` on **MintSession** (60–3600, omitted when 0). Empty optionals are omitted from the
  request body (never sent as `""`), so payloads match the Unreal SDK.
</Note>

## Next steps

<CardGroup cols={2}>
  <Card title="Unreal SDK" icon="gamepad-2" href="/unreal">
    The same worlds + AI characters in Unreal Engine.
  </Card>

  <Card title="Characters" icon="user-round" href="/characters">
    Stances, memory, voice, and images in depth.
  </Card>

  <Card title="The two-token rule" icon="key-round" href="/embedding">
    Mint session tokens server-side; never ship a secret key.
  </Card>

  <Card title="Testing sandbox" icon="flask" href="/testing">
    Build the whole integration free with a test key.
  </Card>
</CardGroup>
