Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/cgwire/zou/llms.txt

Use this file to discover all available pages before exploring further.

Quick Start Guide

This guide will help you start the Zou API server and make your first authenticated requests. By the end, you’ll have created a project and retrieved production data via the REST API.
This guide assumes you’ve completed the Installation steps, including creating the database and admin user.

Start the API Server

Development Mode

For development and testing, use the built-in Flask development server:
python -m zou.debug
You should see:
The Kitsu API server is listening on http://127.0.0.1:5000
The server is now running and ready to accept requests.
The debug server auto-reloads on code changes and provides detailed error messages. Perfect for development!

Production Mode

For production deployments, use Gunicorn (installed automatically with Zou):
gunicorn -c gunicorn.conf.py zou.app:app
Or with custom settings:
gunicorn -w 4 -b 0.0.0.0:5000 --timeout 600 zou.app:app
# gunicorn.conf.py
import multiprocessing

bind = "0.0.0.0:5000"
workers = multiprocessing.cpu_count() * 2 + 1
worker_class = "gevent"
timeout = 600
keepalive = 2

# Logging
accesslog = "-"
errorlog = "-"
loglevel = "info"
Never use the debug server in production. Always use a production WSGI server like Gunicorn.

Authentication

Login and Get Tokens

Zou uses JWT (JSON Web Tokens) for authentication. First, log in with your admin credentials:
curl -X POST "http://localhost:5000/auth/login" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "admin@example.com",
    "password": "YourSecurePassword123"
  }'

Response Structure

Successful login returns:
{
  "login": true,
  "user": {
    "id": "a24a6ea4-ce75-4665-a070-57453082c25",
    "email": "admin@example.com",
    "first_name": "Super",
    "last_name": "Admin",
    "full_name": "Super Admin",
    "role": "admin",
    "active": true,
    "has_avatar": false
  },
  "organisation": {
    "id": "org-id",
    "name": "Your Studio",
    "hours_by_day": 8,
    "has_avatar": false
  },
  "access_token": "eyJ0eXAiOiJKV1QiLCJhbGc...",
  "refresh_token": "eyJ0eXAiOiJKV1QiLCJhbGc..."
}
The access_token is valid for 7 days by default. Use the refresh_token to get a new access token without re-entering credentials.

Make Authenticated Requests

Use the access token in the Authorization header for all subsequent requests:

Check Authentication Status

curl -X GET "http://localhost:5000/auth/authenticated" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Working with Projects

Get All Open Projects

Retrieve currently active projects:
curl -X GET "http://localhost:5000/data/projects/open" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Create a New Project

Create your first production project:
curl -X POST "http://localhost:5000/data/projects" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "My First Project",
    "production_type": "short",
    "fps": "24",
    "ratio": "16:9",
    "resolution": "1920x1080"
  }'

Response Example

{
  "id": "b35b7fb5-df86-5776-b181-68564193d36",
  "name": "My First Project",
  "code": null,
  "description": null,
  "production_type": "short",
  "project_status_id": "status-id",
  "project_status_name": "Open",
  "fps": "24",
  "ratio": "16:9",
  "resolution": "1920x1080",
  "start_date": null,
  "end_date": null,
  "man_days": null,
  "nb_episodes": 0,
  "episode_span": 0,
  "created_at": "2026-03-03T23:00:00",
  "updated_at": "2026-03-03T23:00:00"
}

Working with Assets

Get Asset Types for a Project

response = requests.get(
    f"http://localhost:5000/data/projects/{project_id}/asset-types",
    headers=headers
)

asset_types = response.json()
for asset_type in asset_types:
    print(f"- {asset_type['name']}")

Create an Asset

# First, get an asset type ID (e.g., "Character")
asset_type_id = asset_types[0]['id']

asset_data = {
    "name": "Hero Character",
    "description": "Main protagonist character",
    "data": {}
}

response = requests.post(
    f"http://localhost:5000/data/projects/{project_id}/asset-types/{asset_type_id}/assets/new",
    headers=headers,
    json=asset_data
)

asset = response.json()
print(f"Created asset: {asset['name']} (ID: {asset['id']})")

Complete Example: End-to-End Workflow

Here’s a complete script that demonstrates the full workflow:
import requests
from pprint import pprint

# Configuration
API_URL = "http://localhost:5000"
EMAIL = "admin@example.com"
PASSWORD = "YourSecurePassword123"

# Step 1: Login
print("\n=== Step 1: Login ===")
response = requests.post(
    f"{API_URL}/auth/login",
    json={"email": EMAIL, "password": PASSWORD}
)
response.raise_for_status()
data = response.json()

access_token = data['access_token']
user = data['user']
print(f"Logged in as: {user['full_name']} ({user['role']})")

# Headers for authenticated requests
headers = {"Authorization": f"Bearer {access_token}"}

# Step 2: Create a Project
print("\n=== Step 2: Create Project ===")
project_data = {
    "name": "Demo Project",
    "production_type": "short",
    "fps": "24",
    "ratio": "16:9",
    "resolution": "1920x1080"
}

response = requests.post(
    f"{API_URL}/data/projects",
    headers=headers,
    json=project_data
)
response.raise_for_status()
project = response.json()
project_id = project['id']
print(f"Created: {project['name']} (ID: {project_id})")

# Step 3: List Asset Types
print("\n=== Step 3: Get Asset Types ===")
response = requests.get(
    f"{API_URL}/data/projects/{project_id}/asset-types",
    headers=headers
)
response.raise_for_status()
asset_types = response.json()

if asset_types:
    print("Available asset types:")
    for at in asset_types:
        print(f"  - {at['name']} (ID: {at['id']})")
    
    # Step 4: Create an Asset
    print("\n=== Step 4: Create Asset ===")
    asset_type_id = asset_types[0]['id']
    
    asset_data = {
        "name": "Main Character",
        "description": "The hero of our story"
    }
    
    response = requests.post(
        f"{API_URL}/data/projects/{project_id}/asset-types/{asset_type_id}/assets/new",
        headers=headers,
        json=asset_data
    )
    response.raise_for_status()
    asset = response.json()
    print(f"Created: {asset['name']} (ID: {asset['id']})")
else:
    print("No asset types available yet. Create them via the Kitsu UI.")

# Step 5: List All Projects
print("\n=== Step 5: List All Open Projects ===")
response = requests.get(
    f"{API_URL}/data/projects/open",
    headers=headers
)
response.raise_for_status()
projects = response.json()

print(f"Total open projects: {len(projects)}")
for p in projects:
    print(f"  - {p['name']}")

print("\n=== Workflow Complete ===")

Run the Example

python quickstart_example.py
Expected output:
=== Step 1: Login ===
Logged in as: Super Admin (admin)

=== Step 2: Create Project ===
Created: Demo Project (ID: b35b7fb5-df86-5776-b181-68564193d36)

=== Step 3: Get Asset Types ===
Available asset types:
  - Character (ID: ...)
  - Props (ID: ...)
  - Environment (ID: ...)

=== Step 4: Create Asset ===
Created: Main Character (ID: ...)

=== Step 5: List All Open Projects ===
Total open projects: 1
  - Demo Project

=== Workflow Complete ===

Using the Gazu Python Client

For production integrations, use the Gazu Python client instead of raw HTTP requests:
pip install gazu
import gazu

# Configure API endpoint
gazu.set_host("http://localhost:5000/api")

# Login
gazu.log_in("admin@example.com", "YourSecurePassword123")

# Create a project
project = gazu.project.new_project(
    name="My Project",
    production_type="short"
)

print(f"Created project: {project['name']}")

# Get all projects
projects = gazu.project.all_open_projects()
for p in projects:
    print(f"- {p['name']}")

# Create an asset
asset = gazu.asset.new_asset(
    project,
    "Character",
    "Hero",
    description="Main character"
)

print(f"Created asset: {asset['name']}")
Gazu provides a high-level, Pythonic interface to Zou and is the recommended way to integrate with your pipeline tools.

Event Stream

Zou publishes real-time events for all data changes. Start the event stream server:
python -m zou.event_stream
Connect to the event stream:
import socketio

sio = socketio.Client()

@sio.on('connect')
def on_connect():
    print('Connected to event stream')

@sio.on('asset:new')
def on_asset_created(data):
    print(f"New asset created: {data}")

sio.connect('http://localhost:5001')
sio.wait()

Next Steps

Now that you’re familiar with the basics:

API Reference

Explore all available endpoints and their parameters

Authentication Guide

Learn about 2FA, API keys, and advanced auth features

Data Models

Understand projects, assets, shots, tasks, and relationships

File Management

Work with working files, previews, and file trees

Troubleshooting

Connection Refused

If you get connection errors, ensure:
  • The Zou server is running
  • You’re using the correct host and port
  • Firewall rules allow connections

401 Unauthorized

If requests fail with 401:
  • Check that your access token is valid
  • Tokens expire after 7 days - log in again
  • Ensure the Authorization header is formatted correctly: Bearer YOUR_TOKEN

500 Internal Server Error

If you encounter server errors:
  • Check the Zou server logs for stack traces
  • Verify database connectivity
  • Ensure all required environment variables are set

Database Connection Failed

If Zou can’t connect to PostgreSQL:
  • Verify PostgreSQL is running: pg_isready
  • Check database credentials in environment variables
  • Ensure the zoudb database exists

Get Help

If you run into issues:

Build docs developers (and LLMs) love