API Documentation

Complete guide to integrating with TellsTree's powerful storytelling API.

Quick Start

Get up and running with the TellsTree API in minutes

1
Get JWT Token
2
Make API Calls
3
Start Building

Features

RESTful API
CRUD operations & consistent JSON responses
No Complex Setup
Get JWT Token → Make API Calls → Start Building
Copy & Paste Examples
Working code for JavaScript, Python, PHP, cURL
Clear Error Messages
HTTP status codes and RFC 7807 compliant error messages
Generous Limits
60 api calls per minute

Ressources

Projects /api/projects
Nodes /api/nodes
Edges /api/edges
Media /api/media
Comments /api/comments
Themes /api/themes
Designs /api/designs

Rate Limits

API rate limiting prevents abuse and ensures fair usage. Limits are enforced per IP address and vary by endpoint type.

All API responses include rate limit information in the headers. When limits are exceeded, a 429 status is returned.

Header Fields

  • X-RateLimit-LimitMaximum requests allowed in the current window
  • X-RateLimit-RemainingRemaining requests in the current window
  • Retry-AfterSeconds to wait before retry (429 responses only)
API 300 requests/minute General API limit (optimized for graph editor)
AUTH 5 attempts/minute Login attempts
REFRESH 30 requests/minute Token refresh
REG 3 attempts/10 minutes User registration

Error Handling

The API uses conventional HTTP response codes to indicate success or failure. All error responses follow a consistent JSON format with RFC 7807 Problem Details.

Error responses include a `type` (URL to error documentation), `title` (human-readable summary), `detail` (explanation), and `status` (HTTP code).

2xx 200, 201, 204 Success responses
4xx 400, 401, 403, 404, 422, 429 Client errors
5xx 500, 503 Server errors

Authentication

The TellsTree API uses JWT (JSON Web Token) based authentication. You need to obtain a token by logging in with valid credentials.

POST /api/auth/login Login / Obtain token
GET /api/auth/me Get current user info
POST /api/auth/refresh Refresh token
POST /api/auth/logout Logout / Invalidate token
GET /api/.well-known/jwks.json Public keys (JWKS)

Login

Most API endpoints require authentication. Get your JWT token through the authentication endpoint.

Required Credentials

  • email - Your account email
  • password - Your account password

Response

200 Success / Login successful
401 Authentication failed / Invalid credentials
422 Validation failed / Missing fields
429 Too Many Requests / Rate limited
POSThttps://tellstree.com/api/auth/login
{
    "data": {
        "user": {
            "id": 105,
            "name": "Owner",
            "email": "***REDACTED***",
            "created_at": "2025-10-01T13:25:26.000000Z"
        },
        "token": {
            "access_token": "***REDACTED***",
            "token_type": "Bearer",
            "expires_in": 3600,
            "expires_at": "2025-10-17T23:41:32+00:00"
        }
    }
}

Refresh Token

Tokens expire after 1 hour. Use the refresh endpoint to get a new access token without re-authentication.

Token Refresh

  • • Refresh token is stored as HttpOnly cookie
  • • No request body required
  • • Returns new access token
  • • Include cookies with request

Response

200 Success / Token refreshed
401 Unauthorized / Invalid refresh token
403 Forbidden / Token expired
POSThttps://tellstree.com/api/auth/refresh
{
    "data": {
        "token": {
            "access_token": "***REDACTED***",
            "token_type": "bearer",
            "expires_in": 3600,
            "expires_at": "2025-10-17T23:41:33+00:00"
        }
    }
}

Get Current User

Get information about the currently authenticated user and token details.

Authentication Required

  • • Include JWT token in Authorization header
  • • Returns user profile and token information
  • • Useful for validating token status

Response

200 Success / User information
401 Unauthorized / Missing or invalid token
GEThttps://tellstree.com/api/auth/me
{
    "data": {
        "user": {
            "id": 105,
            "name": "Owner",
            "email": "***REDACTED***",
            "created_at": "2025-10-01T13:25:26.000000Z",
            "last_api_access": "2025-10-17T22:41:33.000000Z"
        },
        "token": {
            "token_type": "Bearer",
            "expires_in": 3600,
            "expires_at": "2025-10-17T23:41:33+00:00",
            "issued_at": "2025-10-17T22:41:33+00:00"
        }
    }
}

Logout

Logout and invalidate all tokens for the current user. This prevents further API access with existing tokens.

Token Invalidation

  • • Invalidates current access token
  • • Invalidates refresh token
  • • Increments user token version
  • • Clears authentication cookies

Response

204 No Content / Logout successful
401 Unauthorized / Invalid token
POSThttps://tellstree.com/api/auth/logout

JSON Web Key Set (JWKS)

Public cryptographic keys for verifying JWT tokens issued by TellsTree. Required for external systems that need to validate TellsTree tokens independently.

Use Cases

  • Microservices: Verify TellsTree tokens without API calls
  • API Gateways: Validate tokens at gateway level
  • Single Sign-On (SSO): Enable SSO integrations
  • Third-party Apps: Verify token authenticity offline

Technical Details

  • • Standard RFC 7517 compliant endpoint
  • • RSA-256 public keys included
  • • Key rotation support via 'kid' field
  • • No authentication required (public endpoint)

Response

200 Success / Public keys
GEThttps://tellstree.com/api/.well-known/jwks.json
{
    "data": {
        "keys": [
            {
                "kty": "RSA",
                "use": "sig",
                "alg": "RS256",
                "kid": "23269d84700638a0",
                "n": "tGkd9DTtdP223c0nKK1sEIeMffQ-Tv56fQlC3aXZkN1eL5B50oocvMH9GMQ24Z4MjvpJ6_5g61OKvf6kBVjmgNeDIRr46nCQ0Gx5-7Y6yxAQAUVP150Y0gJ4dhU5gOF1miOfN0WLJe3MNUAUw-1nGkzyTteWbfRhVgfGfQyFDGfwyJMMbQRiOTLGuC0XvlbfcuCzUieDYfEGTq7T1yrUQLtHtC2LOynty1DmE5bdJqdJ8VTqvBZLCtpkaLUJ-s2qvDBecp2mM5X5OstJvUp4dRucnk_7kp0qdwn2UXirLkKEokPHJi0SQ6Gc6s94GKNbCaDa2W3BztazsKvQmLSTPQ",
                "e": "AQAB"
            }
        ]
    }
}

Using Tokens for API Access

Once you have an access token, include it in the Authorization header for all API requests.

Authorization Header

  • Authorization: Bearer YOUR_TOKEN
  • • Required for all protected endpoints
  • • Token expires after 1 hour
  • • Use refresh endpoint to get new tokens

Example API Call

GET /api/projects
Authorization: Bearer eyJ0eXAiOiJKV1Q...
Accept: application/json
GEThttps://tellstree.com/api/projects
{
    "method": "GET",
    "url": "/api/projects",
    "headers": {
        "Authorization": [
            "***REDACTED***"
        ]
    },
    "payload": null,
    "recorded_at": "2025-10-17T22:41:34+00:00"
}

?? Projects

Projects are the top-level containers for your storytelling content. Each project can contain multiple nodes (story elements) and edges (connections).

GET /api/projects List all projects
POST /api/projects Create project
GET /api/projects/{id} Get project
PUT /api/projects/{id} Update project
DELETE /api/projects/{id} Delete project

List Public Projects

Retrieve all publicly available projects. No authentication required.

Query Parameters

query[is_public] Set to true to get public projects
query[page] Page number (default: 1)
query[per_page] Items per page (default: 15)

Response

200 Success, data.projects[] (Array of detailed Project objects with theme, design, nodes, edges, members, capabilities, comments), data.pagination{} (page, per_page, total)
GEThttps://tellstree.com/api/projects
{
    "data": {
        "projects": [
            {
                "project_id": 1,
                "title": "Tells Adventure",
                "description": "An interactive tale of courage and consequence - guide your choices through arrows, words and fate.",
                "media": null,
                "is_public": true,
                "created_at": "2025-10-01T13:25:26.000000Z",
                "updated_at": "2025-10-01T13:25:26.000000Z",
                "is_locked": false,
                "nodes": {
                    "nodes_count": 9
                },
                "edges": {
                    "edges_count": 6
                },
                "members": {
                    "members_count": 1,
                    "owner": {
                        "user_id": null,
                        "user_name": "Owner"
                    }
                },
                "theme": {
                    "theme_id": 9,
                    "name": "Dialog",
                    "domain": "dialog"
                },
                "design": {
                    "design_id": 11,
                    "name": "Original",
                    "domain": "original"
                },
                "capabilities": {
                    "userCanDelete": false
                },
                "comments": {
                    "comments_count": 3,
                    "comments_rating": 4.7
                }
            },
            {
                "project_id": 2,
                "title": "The Dragon's Quest",
                "description": "An epic adventure story where choices matter and every decision shapes your destiny through mystical lands.",
                "media": null,
                "is_public": true,
                "created_at": "2025-10-01T13:25:26.000000Z",
                "updated_at": "2025-10-01T13:25:26.000000Z",
                "is_locked": false,
                "nodes": {
                    "nodes_count": 7
                },
                "edges": {
                    "edges_count": 4
                },
                "members": {
                    "members_count": 1,
                    "owner": {
                        "user_id": null,
                        "user_name": "Owner"
                    }
                },
                "theme": {
                    "theme_id": 9,
                    "name": "Dialog",
                    "domain": "dialog"
                },
                "design": {
                    "design_id": 11,
                    "name": "Original",
                    "domain": "original"
                },
                "capabilities": {
                    "userCanDelete": false
                },
                "comments": {
                    "comments_count": 3,
                    "comments_rating": 4
                }
            },
            {
                "project_id": 3,
                "title": "Mystery at Moonlight Manor",
                "description": "A thrilling mystery with multiple endings. Solve the case and uncover the dark secrets of the manor.",
                "media": null,
                "is_public": true,
                "created_at": "2025-10-01T13:25:26.000000Z",
                "updated_at": "2025-10-01T13:25:26.000000Z",
                "is_locked": false,
                "nodes": {
                    "nodes_count": 34
                },
                "edges": {
                    "edges_count": 39
                },
                "members": {
                    "members_count": 1,
                    "owner": {
                        "user_id": null,
                        "user_name": "Owner"
                    }
                },
                "theme": {
                    "theme_id": 9,
                    "name": "Dialog",
                    "domain": "dialog"
                },
                "design": {
                    "design_id": 11,
                    "name": "Original",
                    "domain": "original"
                },
                "capabilities": {
                    "userCanDelete": false
                },
                "comments": {
                    "comments_count": 3,
                    "comments_rating": 4.3
                }
            },
            {
                "project_id": 4,
                "title": "Galactic Romance",
                "description": "Love among the stars in this sci-fi romance adventure across the galaxy and beyond.",
                "media": null,
                "is_public": true,
                "created_at": "2025-10-01T13:25:26.000000Z",
                "updated_at": "2025-10-01T13:25:26.000000Z",
                "is_locked": false,
                "nodes": {
                    "nodes_count": 7
                },
                "edges": {
                    "edges_count": 4
                },
                "members": {
                    "members_count": 1,
                    "owner": {
                        "user_id": null,
                        "user_name": "Owner"
                    }
                },
                "theme": {
                    "theme_id": 9,
                    "name": "Dialog",
                    "domain": "dialog"
                },
                "design": {
                    "design_id": 11,
                    "name": "Original",
                    "domain": "original"
                },
                "capabilities": {
                    "userCanDelete": false
                },
                "comments": {
                    "comments_count": 3,
                    "comments_rating": 4.7
                }
            },
            {
                "project_id": 5,
                "title": "Kaffeehaus-Gespr\u00e4ch",
                "description": "Eine Demonstration des Dialog-Theme Systems mit Charakteren und Gespr\u00e4chsverl\u00e4ufen",
                "media": null,
                "is_public": true,
                "created_at": "2025-10-01T13:25:26.000000Z",
                "updated_at": "2025-10-01T13:25:26.000000Z",
                "is_locked": false,
                "nodes": {
                    "nodes_count": 8
                },
                "edges": {
                    "edges_count": 5
                },
                "members": {
                    "members_count": 1,
                    "owner": {
                        "user_id": null,
                        "user_name": "Owner"
                    }
                },
                "theme": {
                    "theme_id": 9,
                    "name": "Dialog",
                    "domain": "dialog"
                },
                "design": {
                    "design_id": 11,
                    "name": "Original",
                    "domain": "original"
                },
                "capabilities": {
                    "userCanDelete": false
                },
                "comments": {
                    "comments_count": 3,
                    "comments_rating": 4.3
                }
            }
        ],
        "pagination": {
            "page": 1,
            "per_page": 5,
            "total": 5
        }
    }
}

List Private Projects

Retrieve all projects that the authenticated user has access to. Authentication required.

Headers

Authorization Bearer {your_jwt_token}

Query Parameters

query[page] Page number (optional)
query[per_page] Items per page (optional)

Response

200 Success, data.projects[] (Array of detailed Project objects with theme, design, nodes, edges, members, capabilities, comments), data.pagination{} (page, per_page, total)
401 Unauthorized, Invalid or missing token
GEThttps://tellstree.com/api/projects
{
    "data": {
        "projects": [
            {
                "project_id": 1,
                "title": "Tells Adventure",
                "description": "An interactive tale of courage and consequence - guide your choices through arrows, words and fate.",
                "media": null,
                "is_public": true,
                "created_at": "2025-10-01T13:25:26.000000Z",
                "updated_at": "2025-10-01T13:25:26.000000Z",
                "is_locked": false,
                "nodes": {
                    "nodes_count": 9
                },
                "edges": {
                    "edges_count": 6
                },
                "members": {
                    "members_count": 1,
                    "owner": {
                        "user_id": null,
                        "user_name": "Owner"
                    }
                },
                "theme": {
                    "theme_id": 9,
                    "name": "Dialog",
                    "domain": "dialog"
                },
                "design": {
                    "design_id": 11,
                    "name": "Original",
                    "domain": "original"
                },
                "capabilities": {
                    "userCanDelete": false
                },
                "comments": {
                    "comments_count": 3,
                    "comments_rating": 4.7
                }
            },
            {
                "project_id": 2,
                "title": "The Dragon's Quest",
                "description": "An epic adventure story where choices matter and every decision shapes your destiny through mystical lands.",
                "media": null,
                "is_public": true,
                "created_at": "2025-10-01T13:25:26.000000Z",
                "updated_at": "2025-10-01T13:25:26.000000Z",
                "is_locked": false,
                "nodes": {
                    "nodes_count": 7
                },
                "edges": {
                    "edges_count": 4
                },
                "members": {
                    "members_count": 1,
                    "owner": {
                        "user_id": null,
                        "user_name": "Owner"
                    }
                },
                "theme": {
                    "theme_id": 9,
                    "name": "Dialog",
                    "domain": "dialog"
                },
                "design": {
                    "design_id": 11,
                    "name": "Original",
                    "domain": "original"
                },
                "capabilities": {
                    "userCanDelete": false
                },
                "comments": {
                    "comments_count": 3,
                    "comments_rating": 4
                }
            },
            {
                "project_id": 3,
                "title": "Mystery at Moonlight Manor",
                "description": "A thrilling mystery with multiple endings. Solve the case and uncover the dark secrets of the manor.",
                "media": null,
                "is_public": true,
                "created_at": "2025-10-01T13:25:26.000000Z",
                "updated_at": "2025-10-01T13:25:26.000000Z",
                "is_locked": false,
                "nodes": {
                    "nodes_count": 34
                },
                "edges": {
                    "edges_count": 39
                },
                "members": {
                    "members_count": 1,
                    "owner": {
                        "user_id": null,
                        "user_name": "Owner"
                    }
                },
                "theme": {
                    "theme_id": 9,
                    "name": "Dialog",
                    "domain": "dialog"
                },
                "design": {
                    "design_id": 11,
                    "name": "Original",
                    "domain": "original"
                },
                "capabilities": {
                    "userCanDelete": false
                },
                "comments": {
                    "comments_count": 3,
                    "comments_rating": 4.3
                }
            },
            {
                "project_id": 4,
                "title": "Galactic Romance",
                "description": "Love among the stars in this sci-fi romance adventure across the galaxy and beyond.",
                "media": null,
                "is_public": true,
                "created_at": "2025-10-01T13:25:26.000000Z",
                "updated_at": "2025-10-01T13:25:26.000000Z",
                "is_locked": false,
                "nodes": {
                    "nodes_count": 7
                },
                "edges": {
                    "edges_count": 4
                },
                "members": {
                    "members_count": 1,
                    "owner": {
                        "user_id": null,
                        "user_name": "Owner"
                    }
                },
                "theme": {
                    "theme_id": 9,
                    "name": "Dialog",
                    "domain": "dialog"
                },
                "design": {
                    "design_id": 11,
                    "name": "Original",
                    "domain": "original"
                },
                "capabilities": {
                    "userCanDelete": false
                },
                "comments": {
                    "comments_count": 3,
                    "comments_rating": 4.7
                }
            },
            {
                "project_id": 5,
                "title": "Kaffeehaus-Gespr\u00e4ch",
                "description": "Eine Demonstration des Dialog-Theme Systems mit Charakteren und Gespr\u00e4chsverl\u00e4ufen",
                "media": null,
                "is_public": true,
                "created_at": "2025-10-01T13:25:26.000000Z",
                "updated_at": "2025-10-01T13:25:26.000000Z",
                "is_locked": false,
                "nodes": {
                    "nodes_count": 8
                },
                "edges": {
                    "edges_count": 5
                },
                "members": {
                    "members_count": 1,
                    "owner": {
                        "user_id": null,
                        "user_name": "Owner"
                    }
                },
                "theme": {
                    "theme_id": 9,
                    "name": "Dialog",
                    "domain": "dialog"
                },
                "design": {
                    "design_id": 11,
                    "name": "Original",
                    "domain": "original"
                },
                "capabilities": {
                    "userCanDelete": false
                },
                "comments": {
                    "comments_count": 3,
                    "comments_rating": 4.3
                }
            }
        ],
        "pagination": {
            "page": 1,
            "per_page": 5,
            "total": 5
        }
    }
}

Create New Project

Create a new storytelling project with a theme and design.

Response

201 Created, data.project{} (Complete Project object with theme, design, nodes, edges, members, capabilities, comments)
422 Invalid data, Validation errors in data.errors{}
401 Unauthorized, Invalid token
POSThttps://tellstree.com/api/projects
{
    "data": {
        "project": {
            "project_id": 93,
            "title": "API Test Project 1760740895",
            "description": "Created by automated test",
            "is_public": false,
            "created_at": "2025-10-17T22:41:35.000000Z",
            "updated_at": "2025-10-17T22:41:35.000000Z",
            "is_locked": false,
            "nodes": {
                "nodes_count": 2
            },
            "edges": {
                "edges_count": 0
            },
            "members": {
                "members_count": 1,
                "owner": {
                    "user_id": null,
                    "user_name": "Owner"
                }
            },
            "theme": {
                "theme_id": 9,
                "name": "Dialog",
                "domain": "dialog"
            },
            "design": {
                "design_id": 11,
                "name": "Original",
                "domain": "original"
            },
            "capabilities": {
                "userCanDelete": true
            },
            "comments": {
                "comments_count": 0,
                "comments_rating": 0
            }
        }
    }
}

Get Single Project

Retrieve detailed information about a specific project.

Response

200 Success, data.project{} (Complete Project object with theme, design, nodes, edges, members, capabilities, comments, media, lock status)
404 Project not found
403 Forbidden, No access
GEThttps://tellstree.com/api/projects/{id}
{
    "data": {
        "project": {
            "project_id": 93,
            "title": "API Test Project 1760740895",
            "description": "Created by automated test",
            "media": null,
            "is_public": false,
            "created_at": "2025-10-17T22:41:35.000000Z",
            "updated_at": "2025-10-17T22:41:35.000000Z",
            "is_locked": false,
            "nodes": {
                "nodes_count": 2
            },
            "edges": {
                "edges_count": 0
            },
            "members": {
                "members_count": 1,
                "owner": {
                    "user_id": null,
                    "user_name": "Owner"
                }
            },
            "theme": {
                "theme_id": 9,
                "name": "Dialog",
                "domain": "dialog"
            },
            "design": {
                "design_id": 11,
                "name": "Original",
                "domain": "original"
            },
            "capabilities": {
                "userCanDelete": true
            },
            "comments": {
                "comments_count": 0,
                "comments_rating": 0
            }
        }
    }
}

Update Project

Update an existing project's properties, theme, or design.

🔒 Lock Management

Lock/unlock functionality is integrated into the update endpoint:

  • is_locked: true - Lock project for current user
  • is_locked: false - Unlock project (only by lock owner)
  • • Response includes is_locked: true/false
  • Auto-unlock: Locks expire automatically after 300 seconds when project is accessed

Response

200 Success, data.project{} (Updated Project object with theme, design, nodes, edges, members, capabilities, comments)
422 Invalid data, Validation errors in data.errors{}
404 Project not found
403 Forbidden, No permission
PUThttps://tellstree.com/api/projects/{id}
{
    "data": {
        "project": {
            "project_id": 93,
            "title": "API Test Project 1760740895 updated",
            "description": "Created by automated test",
            "media": null,
            "is_public": false,
            "created_at": "2025-10-17T22:41:35.000000Z",
            "updated_at": "2025-10-17T22:41:36.000000Z",
            "is_locked": true,
            "nodes": {
                "nodes_count": 2
            },
            "edges": {
                "edges_count": 0
            },
            "members": {
                "members_count": 1,
                "owner": {
                    "user_id": null,
                    "user_name": "Owner"
                }
            },
            "theme": {
                "theme_id": 9,
                "name": "Dialog",
                "domain": "dialog"
            },
            "design": {
                "design_id": 11,
                "name": "Original",
                "domain": "original"
            },
            "capabilities": {
                "userCanDelete": true
            },
            "comments": {
                "comments_count": 0,
                "comments_rating": 0
            }
        }
    }
}

Delete Project

Permanently delete a project and all its content.

Response

204 No Content, Project deleted successfully
404 Project not found
403 Forbidden, No permission
DELETEhttps://tellstree.com/api/projects/{id}

📋 Nodes

Nodes are the building blocks of your story. Each node represents a narrative element like a scene, chapter or character. A Node can contain text, media, and interactive elements.

GET /api/nodes List all nodes
POST /api/nodes Create new node
GET /api/nodes?query[include]=ids Get specific nodes (lazy loading)
GET /api/nodes/{node} Get node details
PUT /api/nodes/{node} Update node
DELETE /api/nodes/{node} Delete node

List Nodes

Retrieve all nodes or filter by project, including their content, position, and metadata.

Query Parameters

  • query[project_id] - Filter by project ID (required)
  • query[page] - Page number (optional, default: 1)
  • query[per_page] - Items per page (optional, default: 20)

Response

200 Success / Nodes list
400 Missing project_id parameter
401 Unauthorized / Invalid token
GEThttps://tellstree.com/api/nodes
{
    "data": {
        "nodes": [
            {
                "node_id": 1,
                "type": "dialog-start",
                "label": "Start",
                "content": "Starting point of your story.",
                "media": null,
                "created_at": "2025-10-01T13:25:26.000000Z",
                "updated_at": "2025-10-01T13:25:26.000000Z",
                "locked_by": null,
                "locked_at": null,
                "outgoing_edges": [
                    {
                        "edge_id": 1,
                        "from_node_id": 1,
                        "to_node_id": 5,
                        "type": "dialog-link",
                        "condition": "Start of conversation",
                        "created_at": "2025-10-01T13:25:26.000000Z",
                        "updated_at": "2025-10-01T13:25:26.000000Z",
                        "locked_by": null,
                        "locked_at": null
                    }
                ],
                "incoming_edges": [],
                "pivot": {
                    "project_id": 1,
                    "node_id": 1,
                    "created_at": "2025-10-01T13:25:26.000000Z",
                    "updated_at": "2025-10-01T13:25:26.000000Z"
                }
            },
            {
                "node_id": 2,
                "type": "dialog-end",
                "label": "End",
                "content": "Conclusion of your story.",
                "media": null,
                "created_at": "2025-10-01T13:25:26.000000Z",
                "updated_at": "2025-10-01T13:25:26.000000Z",
                "locked_by": null,
                "locked_at": null,
                "outgoing_edges": [],
                "incoming_edges": [
                    {
                        "edge_id": 6,
                        "from_node_id": 7,
                        "to_node_id": 2,
                        "type": "dialog-link",
                        "condition": "End of conversation",
                        "created_at": "2025-10-01T13:25:26.000000Z",
                        "updated_at": "2025-10-01T13:25:26.000000Z",
                        "locked_by": null,
                        "locked_at": null
                    }
                ],
                "pivot": {
                    "project_id": 1,
                    "node_id": 2,
                    "created_at": "2025-10-01T13:25:26.000000Z",
                    "updated_at": "2025-10-01T13:25:26.000000Z"
                }
            },
            {
                "node_id": 3,
                "type": "character",
                "label": "Wilhelm Tell",
                "content": "{\"name\":\"Wilhelm Tell\",\"description\":\"A silent hero of justice - a man of few words, but unshakable principles. He values freedom over obedience, and would rather risk death than bow to a tyrant.\"}",
                "media": null,
                "created_at": "2025-10-01T13:25:26.000000Z",
                "updated_at": "2025-10-01T13:25:26.000000Z",
                "locked_by": null,
                "locked_at": null,
                "outgoing_edges": [],
                "incoming_edges": [],
                "pivot": {
                    "project_id": 1,
                    "node_id": 3,
                    "created_at": "2025-10-01T13:25:26.000000Z",
                    "updated_at": "2025-10-01T13:25:26.000000Z"
                }
            },
            {
                "node_id": 4,
                "type": "character",
                "label": "Walter Tell",
                "content": "{\"name\":\"Walter Tell\",\"description\":\"Wilhelm Tells son.\"}",
                "media": null,
                "created_at": "2025-10-01T13:25:26.000000Z",
                "updated_at": "2025-10-01T13:25:26.000000Z",
                "locked_by": null,
                "locked_at": null,
                "outgoing_edges": [],
                "incoming_edges": [],
                "pivot": {
                    "project_id": 1,
                    "node_id": 4,
                    "created_at": "2025-10-01T13:25:26.000000Z",
                    "updated_at": "2025-10-01T13:25:26.000000Z"
                }
            },
            {
                "node_id": 5,
                "type": "dialog-entry",
                "label": "Trust me!",
                "content": "{\"text\":\"Trust me!\",\"speaker\":3}",
                "media": null,
                "created_at": "2025-10-01T13:25:26.000000Z",
                "updated_at": "2025-10-01T13:25:26.000000Z",
                "locked_by": null,
                "locked_at": null,
                "outgoing_edges": [
                    {
                        "edge_id": 2,
                        "from_node_id": 5,
                        "to_node_id": 6,
                        "type": "dialog-link",
                        "condition": "if Walter Tell is armed",
                        "created_at": "2025-10-01T13:25:26.000000Z",
                        "updated_at": "2025-10-01T13:25:26.000000Z",
                        "locked_by": null,
                        "locked_at": null
                    }
                ],
                "incoming_edges": [
                    {
                        "edge_id": 1,
                        "from_node_id": 1,
                        "to_node_id": 5,
                        "type": "dialog-link",
                        "condition": "Start of conversation",
                        "created_at": "2025-10-01T13:25:26.000000Z",
                        "updated_at": "2025-10-01T13:25:26.000000Z",
                        "locked_by": null,
                        "locked_at": null
                    }
                ],
                "pivot": {
                    "project_id": 1,
                    "node_id": 5,
                    "created_at": "2025-10-01T13:25:26.000000Z",
                    "updated_at": "2025-10-01T13:25:26.000000Z"
                }
            },
            {
                "node_id": 6,
                "type": "dialog-entry",
                "label": "Are you sure you won\u00b4t kill me?",
                "content": "{\"text\":\"Are you sure you won\\u00b4t kill me?\",\"speaker\":4}",
                "media": null,
                "created_at": "2025-10-01T13:25:26.000000Z",
                "updated_at": "2025-10-01T13:25:26.000000Z",
                "locked_by": null,
                "locked_at": null,
                "outgoing_edges": [
                    {
                        "edge_id": 3,
                        "from_node_id": 6,
                        "to_node_id": 7,
                        "type": "dialog-link",
                        "condition": null,
                        "created_at": "2025-10-01T13:25:26.000000Z",
                        "updated_at": "2025-10-01T13:25:26.000000Z",
                        "locked_by": null,
                        "locked_at": null
                    },
                    {
                        "edge_id": 4,
                        "from_node_id": 6,
                        "to_node_id": 8,
                        "type": "dialog-link",
                        "condition": null,
                        "created_at": "2025-10-01T13:25:26.000000Z",
                        "updated_at": "2025-10-01T13:25:26.000000Z",
                        "locked_by": null,
                        "locked_at": null
                    },
                    {
                        "edge_id": 5,
                        "from_node_id": 6,
                        "to_node_id": 9,
                        "type": "dialog-link",
                        "condition": null,
                        "created_at": "2025-10-01T13:25:26.000000Z",
                        "updated_at": "2025-10-01T13:25:26.000000Z",
                        "locked_by": null,
                        "locked_at": null
                    }
                ],
                "incoming_edges": [
                    {
                        "edge_id": 2,
                        "from_node_id": 5,
                        "to_node_id": 6,
                        "type": "dialog-link",
                        "condition": "if Walter Tell is armed",
                        "created_at": "2025-10-01T13:25:26.000000Z",
                        "updated_at": "2025-10-01T13:25:26.000000Z",
                        "locked_by": null,
                        "locked_at": null
                    }
                ],
                "pivot": {
                    "project_id": 1,
                    "node_id": 6,
                    "created_at": "2025-10-01T13:25:26.000000Z",
                    "updated_at": "2025-10-01T13:25:26.000000Z"
                }
            },
            {
                "node_id": 7,
                "type": "dialog-entry",
                "label": "You are right. I can't do that.",
                "content": "{\"text\":\"You are right. I can't do that.\",\"speaker\":3}",
                "media": null,
                "created_at": "2025-10-01T13:25:26.000000Z",
                "updated_at": "2025-10-01T13:25:26.000000Z",
                "locked_by": null,
                "locked_at": null,
                "outgoing_edges": [
                    {
                        "edge_id": 6,
                        "from_node_id": 7,
                        "to_node_id": 2,
                        "type": "dialog-link",
                        "condition": "End of conversation",
                        "created_at": "2025-10-01T13:25:26.000000Z",
                        "updated_at": "2025-10-01T13:25:26.000000Z",
                        "locked_by": null,
                        "locked_at": null
                    }
                ],
                "incoming_edges": [
                    {
                        "edge_id": 3,
                        "from_node_id": 6,
                        "to_node_id": 7,
                        "type": "dialog-link",
                        "condition": null,
                        "created_at": "2025-10-01T13:25:26.000000Z",
                        "updated_at": "2025-10-01T13:25:26.000000Z",
                        "locked_by": null,
                        "locked_at": null
                    }
                ],
                "pivot": {
                    "project_id": 1,
                    "node_id": 7,
                    "created_at": "2025-10-01T13:25:26.000000Z",
                    "updated_at": "2025-10-01T13:25:26.000000Z"
                }
            },
            {
                "node_id": 8,
                "type": "dialog-entry",
                "label": "Trust me son!",
                "content": "{\"text\":\"Trust me son!\",\"speaker\":3}",
                "media": null,
                "created_at": "2025-10-01T13:25:26.000000Z",
                "updated_at": "2025-10-01T13:25:26.000000Z",
                "locked_by": null,
                "locked_at": null,
                "outgoing_edges": [],
                "incoming_edges": [
                    {
                        "edge_id": 4,
                        "from_node_id": 6,
                        "to_node_id": 8,
                        "type": "dialog-link",
                        "condition": null,
                        "created_at": "2025-10-01T13:25:26.000000Z",
                        "updated_at": "2025-10-01T13:25:26.000000Z",
                        "locked_by": null,
                        "locked_at": null
                    }
                ],
                "pivot": {
                    "project_id": 1,
                    "node_id": 8,
                    "created_at": "2025-10-01T13:25:26.000000Z",
                    "updated_at": "2025-10-01T13:25:26.000000Z"
                }
            },
            {
                "node_id": 9,
                "type": "dialog-entry",
                "label": "Son just stop shaking!",
                "content": "{\"text\":\"Son just stop shaking!\",\"speaker\":3}",
                "media": null,
                "created_at": "2025-10-01T13:25:26.000000Z",
                "updated_at": "2025-10-01T13:25:26.000000Z",
                "locked_by": null,
                "locked_at": null,
                "outgoing_edges": [],
                "incoming_edges": [
                    {
                        "edge_id": 5,
                        "from_node_id": 6,
                        "to_node_id": 9,
                        "type": "dialog-link",
                        "condition": null,
                        "created_at": "2025-10-01T13:25:26.000000Z",
                        "updated_at": "2025-10-01T13:25:26.000000Z",
                        "locked_by": null,
                        "locked_at": null
                    }
                ],
                "pivot": {
                    "project_id": 1,
                    "node_id": 9,
                    "created_at": "2025-10-01T13:25:26.000000Z",
                    "updated_at": "2025-10-01T13:25:26.000000Z"
                }
            }
        ],
        "pagination": {
            "current_page": 1,
            "total_pages": 1,
            "per_page": 20,
            "total_items": 9,
            "page": 1
        }
    }
}

Load Specific Nodes

Get specific nodes by IDs for lazy loading in the TellsTreeGraph editor. Used for performance optimization when working with large projects.

Query Parameters

  • query[project_id] - Required project ID
  • query[include] - Comma-separated node IDs
  • • Returns canvas-ready node data
  • • Optimized for editor performance

Response

200 Success / Nodes loaded
400 Missing project_id
GEThttps://tellstree.com/api/nodes
{
    "data": {
        "nodes": [
            {
                "node_id": 1,
                "type": "dialog-start",
                "label": "Start",
                "content": [],
                "media": [],
                "compactView": null,
                "detailView": null
            },
            {
                "node_id": 2,
                "type": "dialog-end",
                "label": "End",
                "content": [],
                "media": [],
                "compactView": null,
                "detailView": null
            },
            {
                "node_id": 3,
                "type": "character",
                "label": "Wilhelm Tell",
                "content": {
                    "name": "Wilhelm Tell",
                    "description": "A silent hero of justice - a man of few words, but unshakable principles. He values freedom over obedience, and would rather risk death than bow to a tyrant."
                },
                "media": [],
                "compactView": null,
                "detailView": null
            }
        ]
    }
}

Create Node

Create a new node. Nodes can exist independently or be associated with a project via project_id.

Required Fields

  • title - Node title

Optional Fields

  • project_id - Associate with project
  • content - Node content/text
  • type - Node type
  • position_x - X coordinate
  • position_y - Y coordinate
  • properties - Custom properties

Response

201 Created / Node created
422 Invalid data / Validation failed
401 Unauthorized / Invalid token
POSThttps://tellstree.com/api/nodes
{
    "data": {
        "node": {
            "node_id": 336,
            "type": "character",
            "label": "API Test Character 1760740894",
            "content": {
                "name": "API Test Character 1760740894",
                "description": "Node created by automated API test"
            },
            "media": [],
            "created_at": "2025-10-17T22:41:34.000000Z",
            "updated_at": "2025-10-17T22:41:34.000000Z",
            "outgoing_edges_count": 0,
            "incoming_edges_count": 0,
            "is_locked": false
        }
    }
}

Get Node

Retrieve detailed information about a specific node, including all its properties and metadata.

URL Parameters

  • project - Project ID
  • node - Node ID

Response

200 Success / Node details
404 Node not found
403 Forbidden / No access
GEThttps://tellstree.com/api/nodes/{node}
{
    "data": {
        "nodes": [
            {
                "node_id": 1,
                "type": "dialog-start",
                "label": "Start",
                "content": "Starting point of your story.",
                "media": null,
                "created_at": "2025-10-01T13:25:26.000000Z",
                "updated_at": "2025-10-01T13:25:26.000000Z",
                "locked_by": null,
                "locked_at": null,
                "outgoing_edges": [
                    {
                        "edge_id": 1,
                        "from_node_id": 1,
                        "to_node_id": 5,
                        "type": "dialog-link",
                        "condition": "Start of conversation",
                        "created_at": "2025-10-01T13:25:26.000000Z",
                        "updated_at": "2025-10-01T13:25:26.000000Z",
                        "locked_by": null,
                        "locked_at": null
                    }
                ],
                "incoming_edges": [],
                "pivot": {
                    "project_id": 1,
                    "node_id": 1,
                    "created_at": "2025-10-01T13:25:26.000000Z",
                    "updated_at": "2025-10-01T13:25:26.000000Z"
                }
            },
            {
                "node_id": 2,
                "type": "dialog-end",
                "label": "End",
                "content": "Conclusion of your story.",
                "media": null,
                "created_at": "2025-10-01T13:25:26.000000Z",
                "updated_at": "2025-10-01T13:25:26.000000Z",
                "locked_by": null,
                "locked_at": null,
                "outgoing_edges": [],
                "incoming_edges": [
                    {
                        "edge_id": 6,
                        "from_node_id": 7,
                        "to_node_id": 2,
                        "type": "dialog-link",
                        "condition": "End of conversation",
                        "created_at": "2025-10-01T13:25:26.000000Z",
                        "updated_at": "2025-10-01T13:25:26.000000Z",
                        "locked_by": null,
                        "locked_at": null
                    }
                ],
                "pivot": {
                    "project_id": 1,
                    "node_id": 2,
                    "created_at": "2025-10-01T13:25:26.000000Z",
                    "updated_at": "2025-10-01T13:25:26.000000Z"
                }
            },
            {
                "node_id": 3,
                "type": "character",
                "label": "Wilhelm Tell",
                "content": "{\"name\":\"Wilhelm Tell\",\"description\":\"A silent hero of justice - a man of few words, but unshakable principles. He values freedom over obedience, and would rather risk death than bow to a tyrant.\"}",
                "media": null,
                "created_at": "2025-10-01T13:25:26.000000Z",
                "updated_at": "2025-10-01T13:25:26.000000Z",
                "locked_by": null,
                "locked_at": null,
                "outgoing_edges": [],
                "incoming_edges": [],
                "pivot": {
                    "project_id": 1,
                    "node_id": 3,
                    "created_at": "2025-10-01T13:25:26.000000Z",
                    "updated_at": "2025-10-01T13:25:26.000000Z"
                }
            },
            {
                "node_id": 4,
                "type": "character",
                "label": "Walter Tell",
                "content": "{\"name\":\"Walter Tell\",\"description\":\"Wilhelm Tells son.\"}",
                "media": null,
                "created_at": "2025-10-01T13:25:26.000000Z",
                "updated_at": "2025-10-01T13:25:26.000000Z",
                "locked_by": null,
                "locked_at": null,
                "outgoing_edges": [],
                "incoming_edges": [],
                "pivot": {
                    "project_id": 1,
                    "node_id": 4,
                    "created_at": "2025-10-01T13:25:26.000000Z",
                    "updated_at": "2025-10-01T13:25:26.000000Z"
                }
            },
            {
                "node_id": 5,
                "type": "dialog-entry",
                "label": "Trust me!",
                "content": "{\"text\":\"Trust me!\",\"speaker\":3}",
                "media": null,
                "created_at": "2025-10-01T13:25:26.000000Z",
                "updated_at": "2025-10-01T13:25:26.000000Z",
                "locked_by": null,
                "locked_at": null,
                "outgoing_edges": [
                    {
                        "edge_id": 2,
                        "from_node_id": 5,
                        "to_node_id": 6,
                        "type": "dialog-link",
                        "condition": "if Walter Tell is armed",
                        "created_at": "2025-10-01T13:25:26.000000Z",
                        "updated_at": "2025-10-01T13:25:26.000000Z",
                        "locked_by": null,
                        "locked_at": null
                    }
                ],
                "incoming_edges": [
                    {
                        "edge_id": 1,
                        "from_node_id": 1,
                        "to_node_id": 5,
                        "type": "dialog-link",
                        "condition": "Start of conversation",
                        "created_at": "2025-10-01T13:25:26.000000Z",
                        "updated_at": "2025-10-01T13:25:26.000000Z",
                        "locked_by": null,
                        "locked_at": null
                    }
                ],
                "pivot": {
                    "project_id": 1,
                    "node_id": 5,
                    "created_at": "2025-10-01T13:25:26.000000Z",
                    "updated_at": "2025-10-01T13:25:26.000000Z"
                }
            },
            {
                "node_id": 6,
                "type": "dialog-entry",
                "label": "Are you sure you won\u00b4t kill me?",
                "content": "{\"text\":\"Are you sure you won\\u00b4t kill me?\",\"speaker\":4}",
                "media": null,
                "created_at": "2025-10-01T13:25:26.000000Z",
                "updated_at": "2025-10-01T13:25:26.000000Z",
                "locked_by": null,
                "locked_at": null,
                "outgoing_edges": [
                    {
                        "edge_id": 3,
                        "from_node_id": 6,
                        "to_node_id": 7,
                        "type": "dialog-link",
                        "condition": null,
                        "created_at": "2025-10-01T13:25:26.000000Z",
                        "updated_at": "2025-10-01T13:25:26.000000Z",
                        "locked_by": null,
                        "locked_at": null
                    },
                    {
                        "edge_id": 4,
                        "from_node_id": 6,
                        "to_node_id": 8,
                        "type": "dialog-link",
                        "condition": null,
                        "created_at": "2025-10-01T13:25:26.000000Z",
                        "updated_at": "2025-10-01T13:25:26.000000Z",
                        "locked_by": null,
                        "locked_at": null
                    },
                    {
                        "edge_id": 5,
                        "from_node_id": 6,
                        "to_node_id": 9,
                        "type": "dialog-link",
                        "condition": null,
                        "created_at": "2025-10-01T13:25:26.000000Z",
                        "updated_at": "2025-10-01T13:25:26.000000Z",
                        "locked_by": null,
                        "locked_at": null
                    }
                ],
                "incoming_edges": [
                    {
                        "edge_id": 2,
                        "from_node_id": 5,
                        "to_node_id": 6,
                        "type": "dialog-link",
                        "condition": "if Walter Tell is armed",
                        "created_at": "2025-10-01T13:25:26.000000Z",
                        "updated_at": "2025-10-01T13:25:26.000000Z",
                        "locked_by": null,
                        "locked_at": null
                    }
                ],
                "pivot": {
                    "project_id": 1,
                    "node_id": 6,
                    "created_at": "2025-10-01T13:25:26.000000Z",
                    "updated_at": "2025-10-01T13:25:26.000000Z"
                }
            },
            {
                "node_id": 7,
                "type": "dialog-entry",
                "label": "You are right. I can't do that.",
                "content": "{\"text\":\"You are right. I can't do that.\",\"speaker\":3}",
                "media": null,
                "created_at": "2025-10-01T13:25:26.000000Z",
                "updated_at": "2025-10-01T13:25:26.000000Z",
                "locked_by": null,
                "locked_at": null,
                "outgoing_edges": [
                    {
                        "edge_id": 6,
                        "from_node_id": 7,
                        "to_node_id": 2,
                        "type": "dialog-link",
                        "condition": "End of conversation",
                        "created_at": "2025-10-01T13:25:26.000000Z",
                        "updated_at": "2025-10-01T13:25:26.000000Z",
                        "locked_by": null,
                        "locked_at": null
                    }
                ],
                "incoming_edges": [
                    {
                        "edge_id": 3,
                        "from_node_id": 6,
                        "to_node_id": 7,
                        "type": "dialog-link",
                        "condition": null,
                        "created_at": "2025-10-01T13:25:26.000000Z",
                        "updated_at": "2025-10-01T13:25:26.000000Z",
                        "locked_by": null,
                        "locked_at": null
                    }
                ],
                "pivot": {
                    "project_id": 1,
                    "node_id": 7,
                    "created_at": "2025-10-01T13:25:26.000000Z",
                    "updated_at": "2025-10-01T13:25:26.000000Z"
                }
            },
            {
                "node_id": 8,
                "type": "dialog-entry",
                "label": "Trust me son!",
                "content": "{\"text\":\"Trust me son!\",\"speaker\":3}",
                "media": null,
                "created_at": "2025-10-01T13:25:26.000000Z",
                "updated_at": "2025-10-01T13:25:26.000000Z",
                "locked_by": null,
                "locked_at": null,
                "outgoing_edges": [],
                "incoming_edges": [
                    {
                        "edge_id": 4,
                        "from_node_id": 6,
                        "to_node_id": 8,
                        "type": "dialog-link",
                        "condition": null,
                        "created_at": "2025-10-01T13:25:26.000000Z",
                        "updated_at": "2025-10-01T13:25:26.000000Z",
                        "locked_by": null,
                        "locked_at": null
                    }
                ],
                "pivot": {
                    "project_id": 1,
                    "node_id": 8,
                    "created_at": "2025-10-01T13:25:26.000000Z",
                    "updated_at": "2025-10-01T13:25:26.000000Z"
                }
            },
            {
                "node_id": 9,
                "type": "dialog-entry",
                "label": "Son just stop shaking!",
                "content": "{\"text\":\"Son just stop shaking!\",\"speaker\":3}",
                "media": null,
                "created_at": "2025-10-01T13:25:26.000000Z",
                "updated_at": "2025-10-01T13:25:26.000000Z",
                "locked_by": null,
                "locked_at": null,
                "outgoing_edges": [],
                "incoming_edges": [
                    {
                        "edge_id": 5,
                        "from_node_id": 6,
                        "to_node_id": 9,
                        "type": "dialog-link",
                        "condition": null,
                        "created_at": "2025-10-01T13:25:26.000000Z",
                        "updated_at": "2025-10-01T13:25:26.000000Z",
                        "locked_by": null,
                        "locked_at": null
                    }
                ],
                "pivot": {
                    "project_id": 1,
                    "node_id": 9,
                    "created_at": "2025-10-01T13:25:26.000000Z",
                    "updated_at": "2025-10-01T13:25:26.000000Z"
                }
            }
        ],
        "pagination": {
            "current_page": 1,
            "total_pages": 1,
            "per_page": 20,
            "total_items": 9,
            "page": 1
        }
    }
}

Update Node

Update an existing node's content, position, properties, or other attributes.

🔒 Lock Management

Lock/unlock functionality is integrated into the update endpoint:

  • is_locked: true - Lock node for current user
  • is_locked: false - Unlock node (only by lock owner)
  • • Response includes is_locked: true/false
  • Auto-unlock: Locks expire automatically after 300 seconds when node is accessed

Updatable Fields

  • title - Node title
  • content - Node content
  • type - Node type
  • position_x - X coordinate
  • position_y - Y coordinate
  • properties - Custom properties
  • locked_by - Lock status (user_id or null)

Response

200 Success / Node updated
422 Invalid data / Validation failed
404 Node not found
403 Forbidden / No permission
PUThttps://tellstree.com/api/nodes/{node}
{
    "data": {
        "node": {
            "node_id": 336,
            "type": "character",
            "label": "API Test Character 1760740894",
            "content": {
                "name": "Updated API Test Character"
            },
            "media": [],
            "created_at": "2025-10-17T22:41:34.000000Z",
            "updated_at": "2025-10-17T22:41:35.000000Z",
            "outgoing_edges_count": 0,
            "incoming_edges_count": 0,
            "is_locked": true
        }
    }
}

Delete Node

Permanently delete a node from the project. This will also remove all connected edges.

Response

204 Success / No Content
404 Node not found
403 Forbidden / No permission
DELETEhttps://tellstree.com/api/nodes/{node}

?? Edges

Edges create connections between nodes, defining the flow and relationships in your interactive stories. They represent transitions, choices, and narrative pathways.

GET /api/edges List all edges
POST /api/edges Create new edge
GET /api/edges/{edge} Get edge details
PUT /api/edges/{edge} Update edge
DELETE /api/edges/{edge} Delete edge

Create Edge

Create a new edge connection between two nodes in a project.

Required Fields

  • source_node_id - ID of source node
  • target_node_id - ID of target node

Optional Fields

  • label - Display text for edge
  • condition - Edge activation condition

Response

201 Created / Edge created
422 Invalid data / Validation failed
404 Source/target node not found
POSThttps://tellstree.com/api/edges
{
    "data": {
        "edge": {
            "edge_id": 107,
            "from_node_id": 332,
            "to_node_id": 333,
            "type": "connection",
            "condition": null,
            "created_at": "2025-10-17T22:41:32.000000Z",
            "updated_at": "2025-10-17T22:41:32.000000Z",
            "from_node": {
                "node_id": 332,
                "type": "character",
                "label": "Edge API Node 1760740892"
            },
            "to_node": {
                "node_id": 333,
                "type": "character",
                "label": "Edge API Node 2 1760740892"
            },
            "is_locked": false
        }
    }
}

Get Edge

Retrieve detailed information about a specific edge, including source and target node details.

URL Parameters

  • project - Project ID
  • edge - Edge ID

Response

200 Success / Edge details
404 Edge not found
403 Forbidden / No access
GEThttps://tellstree.com/api/edges/{edge}
{
    "data": {
        "edge": {
            "edge_id": 107,
            "from_node_id": 332,
            "to_node_id": 333,
            "type": "connection",
            "condition": null,
            "created_at": "2025-10-17T22:41:32.000000Z",
            "updated_at": "2025-10-17T22:41:32.000000Z",
            "from_node": {
                "node_id": 332,
                "type": "character",
                "label": "Edge API Node 1760740892"
            },
            "to_node": {
                "node_id": 333,
                "type": "character",
                "label": "Edge API Node 2 1760740892"
            },
            "is_locked": false
        }
    }
}

Update Edge

Update properties of an existing edge. You can modify the label, condition, and connection endpoints.

🔒 Lock Management

Lock/unlock functionality is integrated into the update endpoint:

  • is_locked: true - Lock edge for current user
  • is_locked: false - Unlock edge (only by lock owner)
  • • Response includes is_locked: true/false
  • Auto-unlock: Locks expire automatically after 300 seconds when edge is accessed

Request Body

  • source_node_id - Source node ID
  • target_node_id - Target node ID
  • label - Edge display label
  • condition - Edge condition
  • locked_by - Lock status (internal use only)

Response

200 Success / Edge updated
422 Invalid data / Validation failed
404 Edge not found
403 Forbidden / No permission
PUThttps://tellstree.com/api/edges/{edge}
{
    "data": {
        "edge": {
            "edge_id": 107,
            "from_node_id": 332,
            "to_node_id": 333,
            "type": "connection",
            "condition": "updated condition",
            "created_at": "2025-10-17T22:41:32.000000Z",
            "updated_at": "2025-10-17T22:41:32.000000Z",
            "from_node": {
                "node_id": 332,
                "type": "character",
                "label": "Edge API Node 1760740892"
            },
            "to_node": {
                "node_id": 333,
                "type": "character",
                "label": "Edge API Node 2 1760740892"
            },
            "is_locked": true
        }
    }
}

Delete Edge

Permanently delete an edge connection. This action cannot be undone and will break the connection between nodes.

Response

204 Success / No Content
404 Edge not found
403 Forbidden / No permission
DELETEhttps://tellstree.com/api/edges/{edge}

🎬 Media

Media files can be attached to projects, nodes, or edges. Support for images, videos, audio, and documents to enrich your interactive stories with multimedia content.

GET /api/media List all media
POST /api/media Upload new media
GET /api/media/{media} Get media details
PUT /api/media/{media} Update media metadata
PATCH /api/media/{media}/visibility Update visibility
DELETE /api/media/{media} Delete media

List Media

Retrieve all media files or filter by project, node, or edge.

Query Parameters

  • project_id - Filter by project ID (optional)
  • node_id - Filter by node ID (optional)
  • edge_id - Filter by edge ID (optional)
  • type - Filter by media type (optional)
  • page - Page number (optional)
  • per_page - Items per page (optional)

Response

200 Success / Media list
401 Unauthorized / Invalid token
403 Forbidden / No access
GEThttps://tellstree.com/api/media
{
    "data": {
        "media": [
            {
                "media_id": 1,
                "filename": "hero-image.jpg",
                "original_name": "Hero Image.jpg",
                "mime_type": "image/jpeg",
                "size": 1048576,
                "path": "/storage/media/hero-image.jpg",
                "url": "https://tellstree.com/storage/media/hero-image.jpg",
                "project_id": 42,
                "node_id": null,
                "edge_id": null,
                "visibility": "public",
                "alt_text": "Hero image for opening scene",
                "description": "Main visual for the story introduction",
                "metadata": {
                    "width": 1920,
                    "height": 1080,
                    "aspect_ratio": "16:9"
                },
                "created_at": "2024-01-15T10:00:00.000000Z",
                "updated_at": "2024-01-15T10:00:00.000000Z"
            }
        ],
        "pagination": {
            "current_page": 1,
            "total_pages": 3,
            "per_page": 10,
            "total_items": 25
        }
    },
    "meta": {
        "request_id": "req_67890",
        "timestamp": "2024-01-15T10:00:00.000000Z"
    }
}

Upload Media

Upload a new media file and attach it to a project, node, or edge.

Required Fields

  • file - Media file to upload

Optional Fields

  • project_id - Attach to project
  • node_id - Attach to node
  • edge_id - Attach to edge
  • alt_text - Alternative text
  • description - Media description
  • visibility - public/private

Response

201 Created / Media uploaded
422 Invalid file / Validation failed
413 Payload Too Large / File too big
415 Unsupported Media Type
POSThttps://tellstree.com/api/media
{
    "data": {
        "media_id": 123,
        "filename": "uploaded-image.jpg",
        "original_name": "My Image.jpg",
        "mime_type": "image/jpeg",
        "size": 2097152,
        "path": "/storage/media/uploaded-image.jpg",
        "url": "https://tellstree.com/storage/media/uploaded-image.jpg",
        "project_id": 42,
        "node_id": null,
        "edge_id": null,
        "visibility": "public",
        "alt_text": "Uploaded image",
        "description": "User uploaded media file",
        "metadata": {
            "width": 1600,
            "height": 900,
            "aspect_ratio": "16:9"
        },
        "created_at": "2024-01-15T10:30:00.000000Z",
        "updated_at": "2024-01-15T10:30:00.000000Z"
    },
    "meta": {
        "request_id": "req_12345",
        "timestamp": "2024-01-15T10:30:00.000000Z"
    }
}

💬 Comments

Comments enable collaborative feedback and discussion within projects. Team members can leave notes, suggestions, and feedback to improve the narrative experience.

GET /api/comments List all comments
POST /api/comments Create new comment
GET /api/comments/{comment} Get comment details
PUT /api/comments/{comment} Update comment
DELETE /api/comments/{comment} Delete comment

List Comments

Retrieve all comments or filter by project to see discussions and feedback.

Query Parameters

  • project_id - Filter by project ID (optional)
  • user_id - Filter by author (optional)
  • status - Filter by status (optional)
  • page - Page number (optional)
  • per_page - Items per page (optional)

Response

200 Success / Comments list
401 Unauthorized / Invalid token
403 Forbidden / No access
GEThttps://tellstree.com/api/comments
{
    "data": {
        "comments": [
            {
                "id": 40,
                "project_id": 90,
                "user_id": 105,
                "content": "{\"content\":\"API Test Comment content with enough length\",\"rating\":4,\"project_id\":90}",
                "rating": 4,
                "created_at": "2025-10-17T22:41:31.000000Z"
            }
        ]
    }
}

Create Comment

Add a new comment or feedback to a project for collaborative discussion.

Required Fields

  • content - Comment text
  • project_id - Project to attach comment to

Optional Fields

  • priority - low/medium/high
  • node_id - Related node ID
  • position - UI position data
  • tags - Array of tags

Response

201 Created / Comment created
422 Invalid data / Validation failed
404 Project not found
403 Forbidden / No access
POSThttps://tellstree.com/api/comments
{
    "data": {
        "comment": {
            "id": 40,
            "project_id": 90,
            "user_id": 105,
            "content": "{\"content\":\"API Test Comment content with enough length\",\"rating\":4,\"project_id\":90}",
            "rating": 4,
            "created_at": "2025-10-17T22:41:31.000000Z"
        }
    }
}

Themes

Themes define the visual styling and appearance of your storytelling projects. Each theme contains color schemes, fonts, and layout configurations.

GET /api/themes List all themes
GET /api/themes/{theme} Get theme by domain

List All Themes

Retrieve a paginated list of all available themes. This is a public endpoint and does not require authentication.

Query Parameters

  • page - Page number (optional, default: 1)
  • per_page - Items per page (optional, default: 15)

Response

200 Success / Themes list
GEThttps://tellstree.com/api/themes
{
    "data": {
        "themes": [
            {
                "theme_id": 9,
                "name": "Dialog",
                "domain": "dialog",
                "created_at": "2025-10-01T13:25:26.000000Z",
                "updated_at": "2025-10-01T13:25:26.000000Z",
                "statistics": {
                    "projects_count": 76,
                    "is_default": true
                },
                "node_types": [
                    "character",
                    "dialog-start",
                    "dialog-end",
                    "dialog-entry"
                ],
                "forms": {
                    "character": {
                        "name": {
                            "form": "input",
                            "type": "string",
                            "required": true,
                            "label": "Character Name",
                            "isLabel": true,
                            "placeholder": "Enter character name..."
                        },
                        "description": {
                            "form": "textarea",
                            "type": "string",
                            "required": false,
                            "label": "Description",
                            "rows": 4,
                            "placeholder": "Character description..."
                        },
                        "traits": {
                            "form": "textarea",
                            "type": "string",
                            "required": false,
                            "label": "Traits",
                            "rows": 3,
                            "placeholder": "Character traits (e.g. brave, shy...)"
                        }
                    },
                    "dialog-end": [],
                    "dialog-entry": {
                        "speaker": {
                            "form": "select",
                            "type": "string",
                            "required": true,
                            "label": "Speaker",
                            "options": [
                                {
                                    "label": "{{node[character].content.name}}",
                                    "value": "{{node[character].node_id}}"
                                }
                            ],
                            "placeholder": "Select character..."
                        },
                        "text": {
                            "form": "textarea",
                            "type": "string",
                            "required": true,
                            "label": "Text/Action",
                            "isLabel": true,
                            "rows": 4,
                            "placeholder": "Dialog text or action description..."
                        }
                    },
                    "dialog-start": {
                        "title": {
                            "form": "input",
                            "type": "string",
                            "required": true,
                            "label": "Dialog Title",
                            "isLabel": true,
                            "placeholder": "What is the dialog about?"
                        },
                        "description": {
                            "form": "textarea",
                            "type": "string",
                            "required": false,
                            "label": "Description",
                            "rows": 4,
                            "placeholder": "Dialog description..."
                        }
                    }
                }
            }
        ]
    },
    "meta": {
        "total_count": 1,
        "timestamp": "2025-10-17T22:41:37.016049Z"
    }
}

Get Specific Theme

Retrieve detailed information about a specific theme by its domain. This is a public endpoint and does not require authentication.

URL Parameters

  • theme - The unique domain identifier of the theme (e.g., "dialog")

Response

200 Success / Theme details
404 Theme not found
GEThttps://tellstree.com/api/themes/{slug}
{
    "data": {
        "theme": {
            "theme_id": 9,
            "name": "Dialog",
            "domain": "dialog",
            "created_at": "2025-10-01T13:25:26.000000Z",
            "updated_at": "2025-10-01T13:25:26.000000Z",
            "statistics": {
                "projects_count": 76,
                "is_default": true
            },
            "node_types": [
                "character",
                "dialog-start",
                "dialog-end",
                "dialog-entry"
            ],
            "forms": {
                "character": {
                    "name": {
                        "form": "input",
                        "type": "string",
                        "required": true,
                        "label": "Character Name",
                        "isLabel": true,
                        "placeholder": "Enter character name..."
                    },
                    "description": {
                        "form": "textarea",
                        "type": "string",
                        "required": false,
                        "label": "Description",
                        "rows": 4,
                        "placeholder": "Character description..."
                    },
                    "traits": {
                        "form": "textarea",
                        "type": "string",
                        "required": false,
                        "label": "Traits",
                        "rows": 3,
                        "placeholder": "Character traits (e.g. brave, shy...)"
                    }
                },
                "dialog-end": [],
                "dialog-entry": {
                    "speaker": {
                        "form": "select",
                        "type": "string",
                        "required": true,
                        "label": "Speaker",
                        "options": [
                            {
                                "label": "{{node[character].content.name}}",
                                "value": "{{node[character].node_id}}"
                            }
                        ],
                        "placeholder": "Select character..."
                    },
                    "text": {
                        "form": "textarea",
                        "type": "string",
                        "required": true,
                        "label": "Text/Action",
                        "isLabel": true,
                        "rows": 4,
                        "placeholder": "Dialog text or action description..."
                    }
                },
                "dialog-start": {
                    "title": {
                        "form": "input",
                        "type": "string",
                        "required": true,
                        "label": "Dialog Title",
                        "isLabel": true,
                        "placeholder": "What is the dialog about?"
                    },
                    "description": {
                        "form": "textarea",
                        "type": "string",
                        "required": false,
                        "label": "Description",
                        "rows": 4,
                        "placeholder": "Dialog description..."
                    }
                }
            }
        }
    },
    "meta": {
        "timestamp": "2025-10-17T22:41:37.076138Z"
    }
}

✨ Designs

Designs provide specific styling configurations within themes, allowing fine-grained control over the visual presentation of story elements.

GET /api/designs List all designs
GET /api/designs/{design} Get design by domain

List All Designs

Retrieve a list of all available designs. This is a public endpoint and does not require authentication.

Response

200 Success / Designs list
GEThttps://tellstree.com/api/designs
{
    "data": {
        "designs": [
            {
                "design_id": 11,
                "name": "Original",
                "domain": "original",
                "created_at": "2025-10-01T13:25:26.000000Z",
                "updated_at": "2025-10-01T13:25:26.000000Z",
                "statistics": {
                    "projects_count": 74,
                    "is_default": false
                },
                "configuration": {
                    "name": "Original",
                    "version": "1.0.0",
                    "description": "The original design recipe for TellsTree - timeless, refined, and expertly crafted.",
                    "author": "TellsTree Team",
                    "category": "classic",
                    "preview": "foundation-preview.jpg",
                    "colors": {
                        "primary": "#3B82F6",
                        "secondary": "#8B5CF6",
                        "accent": "#10B981",
                        "background": "#F8FAFC",
                        "surface": "#FFFFFF",
                        "text": {
                            "primary": "#1F2937",
                            "secondary": "#6B7280",
                            "muted": "#9CA3AF"
                        },
                        "border": "#E5E7EB",
                        "shadow": "rgba(0, 0, 0, 0.1)"
                    },
                    "typography": {
                        "fontFamily": {
                            "primary": "Inter, sans-serif",
                            "heading": "Inter, sans-serif",
                            "code": "JetBrains Mono, monospace"
                        },
                        "fontSize": {
                            "xs": "0.75rem",
                            "sm": "0.875rem",
                            "base": "1rem",
                            "lg": "1.125rem",
                            "xl": "1.25rem",
                            "2xl": "1.5rem",
                            "3xl": "1.875rem"
                        },
                        "fontWeight": {
                            "normal": "400",
                            "medium": "500",
                            "semibold": "600",
                            "bold": "700"
                        },
                        "lineHeight": {
                            "tight": "1.25",
                            "normal": "1.5",
                            "relaxed": "1.75"
                        }
                    },
                    "spacing": {
                        "xs": "0.25rem",
                        "sm": "0.5rem",
                        "md": "1rem",
                        "lg": "1.5rem",
                        "xl": "2rem",
                        "2xl": "3rem"
                    },
                    "borderRadius": {
                        "sm": "0.25rem",
                        "md": "0.375rem",
                        "lg": "0.5rem",
                        "xl": "0.75rem",
                        "full": "9999px"
                    },
                    "shadows": {
                        "sm": "0 1px 2px 0 rgba(0, 0, 0, 0.05)",
                        "md": "0 4px 6px -1px rgba(0, 0, 0, 0.1)",
                        "lg": "0 10px 15px -3px rgba(0, 0, 0, 0.1)",
                        "xl": "0 20px 25px -5px rgba(0, 0, 0, 0.1)"
                    },
                    "components": [
                        "Card",
                        "Circle",
                        "SpeechBubble",
                        "CharacterBadge",
                        "MediaCard"
                    ]
                },
                "components": [
                    "Card",
                    "Circle",
                    "SpeechBubble",
                    "CharacterBadge",
                    "MediaCard"
                ]
            }
        ]
    },
    "meta": {
        "total_count": 1,
        "timestamp": "2025-10-17T22:41:31.858417Z"
    }
}

Get Specific Design

Retrieve detailed configuration for a specific design by its domain identifier.

URL Parameters

  • design - The domain identifier of the design

Response

200 Success / Design details
404 Design not found
GEThttps://tellstree.com/api/designs/{design}
{
    "data": {
        "design": {
            "design_id": 11,
            "name": "Original",
            "domain": "original",
            "created_at": "2025-10-01T13:25:26.000000Z",
            "updated_at": "2025-10-01T13:25:26.000000Z",
            "statistics": {
                "projects_count": 74,
                "is_default": false
            },
            "configuration": {
                "name": "Original",
                "version": "1.0.0",
                "description": "The original design recipe for TellsTree - timeless, refined, and expertly crafted.",
                "author": "TellsTree Team",
                "category": "classic",
                "preview": "foundation-preview.jpg",
                "colors": {
                    "primary": "#3B82F6",
                    "secondary": "#8B5CF6",
                    "accent": "#10B981",
                    "background": "#F8FAFC",
                    "surface": "#FFFFFF",
                    "text": {
                        "primary": "#1F2937",
                        "secondary": "#6B7280",
                        "muted": "#9CA3AF"
                    },
                    "border": "#E5E7EB",
                    "shadow": "rgba(0, 0, 0, 0.1)"
                },
                "typography": {
                    "fontFamily": {
                        "primary": "Inter, sans-serif",
                        "heading": "Inter, sans-serif",
                        "code": "JetBrains Mono, monospace"
                    },
                    "fontSize": {
                        "xs": "0.75rem",
                        "sm": "0.875rem",
                        "base": "1rem",
                        "lg": "1.125rem",
                        "xl": "1.25rem",
                        "2xl": "1.5rem",
                        "3xl": "1.875rem"
                    },
                    "fontWeight": {
                        "normal": "400",
                        "medium": "500",
                        "semibold": "600",
                        "bold": "700"
                    },
                    "lineHeight": {
                        "tight": "1.25",
                        "normal": "1.5",
                        "relaxed": "1.75"
                    }
                },
                "spacing": {
                    "xs": "0.25rem",
                    "sm": "0.5rem",
                    "md": "1rem",
                    "lg": "1.5rem",
                    "xl": "2rem",
                    "2xl": "3rem"
                },
                "borderRadius": {
                    "sm": "0.25rem",
                    "md": "0.375rem",
                    "lg": "0.5rem",
                    "xl": "0.75rem",
                    "full": "9999px"
                },
                "shadows": {
                    "sm": "0 1px 2px 0 rgba(0, 0, 0, 0.05)",
                    "md": "0 4px 6px -1px rgba(0, 0, 0, 0.1)",
                    "lg": "0 10px 15px -3px rgba(0, 0, 0, 0.1)",
                    "xl": "0 20px 25px -5px rgba(0, 0, 0, 0.1)"
                },
                "components": [
                    "Card",
                    "Circle",
                    "SpeechBubble",
                    "CharacterBadge",
                    "MediaCard"
                ]
            },
            "components": [
                "Card",
                "Circle",
                "SpeechBubble",
                "CharacterBadge",
                "MediaCard"
            ]
        }
    },
    "meta": {
        "timestamp": "2025-10-17T22:41:31.904987Z"
    }
}