Overview

The AI Story Engine exports dialogue as a DialogueTree JSON structure designed to map directly to Unity's data model. Each tree contains nodes with speaker text, player choices, conditions, and branching logic.

You have two integration options:

DialogueTree JSON Schema

The export endpoint (GET /v1/projects/{project_id}/export/unity) returns a JSON object conforming to the unity_dialogue_export.json schema. Here is an example:

{
  "version": "1.0",
  "npc_id": "npc_x7y8z9",
  "npc_name": "Greta Ironforge",
  "session_id": "session_001",
  "root_node": "node_001",
  "nodes": {
    "node_001": {
      "type": "npc_line",
      "speaker": "Greta Ironforge",
      "text": "Well, well. Another adventurer looking for trouble. What'll it be?",
      "emotion": "amused_skepticism",
      "audio_hint": "gruff_female_alto",
      "next": "node_002"
    },
    "node_002": {
      "type": "player_choice",
      "choices": [
        {
          "text": "I heard there's something hidden beneath this tavern.",
          "next": "node_003",
          "condition": null
        },
        {
          "text": "Just an ale, thanks.",
          "next": "node_004",
          "condition": null
        },
        {
          "text": "[Persuasion] You look like someone with stories. Share one?",
          "next": "node_005",
          "condition": {
            "stat": "charisma",
            "min_value": 14
          }
        }
      ]
    },
    "node_003": {
      "type": "npc_line",
      "speaker": "Greta Ironforge",
      "text": "Keep your voice down. The walls have ears in this town. Come back after closing — alone.",
      "emotion": "guarded_concern",
      "audio_hint": "whispered_urgent",
      "event": "quest_hook_hidden_passage",
      "next": null
    },
    "node_004": {
      "type": "npc_line",
      "speaker": "Greta Ironforge",
      "text": "Smart choice. Two coppers.",
      "emotion": "neutral_approval",
      "audio_hint": "gruff_female_alto",
      "next": null
    },
    "node_005": {
      "type": "npc_line",
      "speaker": "Greta Ironforge",
      "text": "Ha! Flattery won't get you far here. But since you asked nicely... sit down. I'll tell you about the Siege of Ashenmoor.",
      "emotion": "warming_nostalgic",
      "audio_hint": "softened_reminiscent",
      "event": "lore_unlock_ashenmoor",
      "next": null
    }
  },
  "metadata": {
    "generated_at": "2026-03-13T10:05:00Z",
    "kg_seq": 42,
    "node_count": 5,
    "max_depth": 2
  }
}

Node Types

Type Description Key Fields
npc_line NPC speaks a line of dialogue speaker, text, emotion, audio_hint, next
player_choice Player selects from options choices[] (each with text, next, condition)
event Trigger a game event (no visible dialogue) event, next
condition Branch based on game state condition, if_true, if_false

C# Loader

The following C# classes load the DialogueTree JSON into Unity. Drop these into your project's Scripts/Dialogue/ folder.

Data Classes

using System;
using System.Collections.Generic;
using UnityEngine;

namespace AIStoryEngine
{
    [Serializable]
    public class DialogueTree
    {
        public string version;
        public string npc_id;
        public string npc_name;
        public string session_id;
        public string root_node;
        public Dictionary<string, DialogueNode> nodes;
        public DialogueMetadata metadata;
    }

    [Serializable]
    public class DialogueNode
    {
        public string type;      // "npc_line", "player_choice", "event", "condition"
        public string speaker;
        public string text;
        public string emotion;
        public string audio_hint;
        public string next;       // next node ID, null if terminal
        public string @event;     // game event to trigger
        public List<DialogueChoice> choices;
    }

    [Serializable]
    public class DialogueChoice
    {
        public string text;
        public string next;
        public DialogueCondition condition;
    }

    [Serializable]
    public class DialogueCondition
    {
        public string stat;
        public int min_value;
    }

    [Serializable]
    public class DialogueMetadata
    {
        public string generated_at;
        public int kg_seq;
        public int node_count;
        public int max_depth;
    }
}

Loader Component

using UnityEngine;
using Newtonsoft.Json;

namespace AIStoryEngine
{
    public class DialogueLoader : MonoBehaviour
    {
        [SerializeField]
        private TextAsset dialogueJson;

        private DialogueTree _tree;
        private DialogueNode _currentNode;

        void Start()
        {
            if (dialogueJson != null)
            {
                LoadDialogue(dialogueJson.text);
            }
        }

        /// <summary>
        /// Load a DialogueTree from JSON string.
        /// Works with both TextAsset content and API response bodies.
        /// </summary>
        public void LoadDialogue(string json)
        {
            _tree = JsonConvert.DeserializeObject<DialogueTree>(json);

            if (_tree == null || _tree.nodes == null)
            {
                Debug.LogError("[AIStoryEngine] Failed to parse dialogue JSON");
                return;
            }

            Debug.Log($"[AIStoryEngine] Loaded dialogue for {_tree.npc_name} ({_tree.metadata.node_count} nodes)");
            GoToNode(_tree.root_node);
        }

        /// <summary>
        /// Navigate to a specific node by ID.
        /// </summary>
        public void GoToNode(string nodeId)
        {
            if (nodeId == null || !_tree.nodes.ContainsKey(nodeId))
            {
                Debug.Log("[AIStoryEngine] Dialogue ended");
                _currentNode = null;
                return;
            }

            _currentNode = _tree.nodes[nodeId];

            switch (_currentNode.type)
            {
                case "npc_line":
                    HandleNpcLine(_currentNode);
                    break;
                case "player_choice":
                    HandlePlayerChoice(_currentNode);
                    break;
                case "event":
                    HandleEvent(_currentNode);
                    break;
            }
        }

        private void HandleNpcLine(DialogueNode node)
        {
            Debug.Log($"[{node.speaker}] ({node.emotion}): {node.text}");

            // TODO: Display in your UI system
            // TODO: Play audio using node.audio_hint
            // TODO: Trigger node.@event if present

            // Auto-advance to next node (hook up to "Continue" button in production)
            // GoToNode(node.next);
        }

        private void HandlePlayerChoice(DialogueNode node)
        {
            for (int i = 0; i < node.choices.Count; i++)
            {
                var choice = node.choices[i];
                Debug.Log($"  [{i + 1}] {choice.text}");

                // TODO: Check choice.condition against player stats
                // TODO: Render as UI buttons
            }
        }

        private void HandleEvent(DialogueNode node)
        {
            Debug.Log($"[AIStoryEngine] Event triggered: {node.@event}");
            // TODO: Dispatch to your event system
            GoToNode(node.next);
        }

        /// <summary>
        /// Call this when the player selects a choice (0-indexed).
        /// </summary>
        public void SelectChoice(int choiceIndex)
        {
            if (_currentNode?.type != "player_choice") return;
            if (choiceIndex < 0 || choiceIndex >= _currentNode.choices.Count) return;

            var chosen = _currentNode.choices[choiceIndex];
            GoToNode(chosen.next);
        }

        /// <summary>
        /// Advance past an NPC line to the next node.
        /// </summary>
        public void Continue()
        {
            if (_currentNode?.type == "npc_line")
            {
                GoToNode(_currentNode.next);
            }
        }
    }
}

Static Import (Build Time)

For static dialogue, export the JSON once and include it as a Unity TextAsset:

1

Export from the API

curl -o Assets/Dialogue/greta_session001.json \
  https://api.aistoryengine.dev/v1/projects/proj_a1b2c3d4/export/unity \
  -H "Authorization: Bearer $ASE_API_KEY" \
  -G -d "npc_id=npc_x7y8z9" \
     -d "session_id=session_001" \
     -d "format=dialogue_tree"
2

Assign in Unity Editor

Drag the .json file onto the DialogueLoader component's Dialogue Json field in the Inspector. Unity imports .json files as TextAsset automatically.

3

Wire up your UI

In the HandleNpcLine and HandlePlayerChoice methods, replace the Debug.Log calls with your dialogue UI system. Call Continue() when the player clicks through NPC lines and SelectChoice(index) when they pick a dialogue option.

Runtime Generation (Pro Plan)

For dynamic dialogue that adapts to player behavior in real time, call the generate endpoint from Unity using UnityWebRequest:

using System.Collections;
using System.Text;
using UnityEngine;
using UnityEngine.Networking;
using Newtonsoft.Json;

namespace AIStoryEngine
{
    public class RuntimeDialogueClient : MonoBehaviour
    {
        [SerializeField] private string apiKey;   // Use pk_ key for client-side
        [SerializeField] private string projectId;
        [SerializeField] private string baseUrl = "https://api.aistoryengine.dev";

        public IEnumerator GenerateDialogue(
            string npcId,
            string sessionId,
            string playerAction,
            System.Action<DialogueTree> onSuccess,
            System.Action<string> onError)
        {
            var requestBody = JsonConvert.SerializeObject(new
            {
                npc_id = npcId,
                session_id = sessionId,
                player_action = playerAction,
                output_format = "dialogue_tree"
            });

            var url = $"{baseUrl}/v1/projects/{projectId}/generate";
            var request = new UnityWebRequest(url, "POST");
            request.uploadHandler = new UploadHandlerRaw(
                Encoding.UTF8.GetBytes(requestBody));
            request.downloadHandler = new DownloadHandlerBuffer();
            request.SetRequestHeader("Content-Type", "application/json");
            request.SetRequestHeader("Authorization", $"Bearer {apiKey}");

            yield return request.SendWebRequest();

            if (request.result == UnityWebRequest.Result.Success)
            {
                var tree = JsonConvert.DeserializeObject<DialogueTree>(
                    request.downloadHandler.text);
                onSuccess?.Invoke(tree);
            }
            else
            {
                onError?.Invoke(request.error);
            }
        }
    }
}

Tips for Unity Editor Integration

Preview in Editor

Create a custom Editor window that loads DialogueTree JSON and lets you click through the conversation without entering Play Mode. Use EditorGUILayout.TextArea to display NPC lines and buttons for choices.

Addressables

For large projects, store dialogue JSON as Addressable assets instead of bundling them in Resources. This lets you update dialogue without rebuilding the entire game.

Localization

Export dialogue as CSV (/export/csv) for integration with Unity Localization Tables. The CSV includes string IDs that map to DialogueTree node IDs.

Audio Integration

The audio_hint field on NPC lines maps to voice profiles. Use it to select the right voice clip or to pass to a text-to-speech system like ElevenLabs for runtime audio generation.

Need help? Join the #unity-integration channel on Discord for community support, or reach out to support@aistoryengine.dev for Pro/Enterprise customers.