DOC / 01

Blueprint Studio Setup

Install Blueprint Studio, prepare local services, and confirm a working baseline.

1. Database Setup2. Configure Environment3. Start the API Server4. Confirm Bootstrap5. Launch Blueprint Studio Desktop6. Open Settings
DOC / 02

Publishing System Overview

Understand the publishing model, package records, components, metadata, and validation flow.

Core ConceptsContainer GraphPublish ManagerContextContainerVersion

Blueprint publishing is built around a graph-driven workflow. A publish is not only a file copy. It is a controlled process that records context, checks, components, versions, metadata, user data, dependencies, file paths, and file hashes.

Core Concepts

Container Graph

A ContainerGraph represents one publishable source file loaded through a publish blueprint YAML. It owns:

  • The source path being published.
  • The production context such as project, shot, asset, task type, container, and version.
  • The container node, pre-build checks, build node, publish chains, and publish items.
  • Graph metadata and user data.

The graph is loaded with:

from quanta import ContainerGraph

graph = ContainerGraph.load( show_name="DemoProject", filepath="D:/show/seq010/shot020/work/scene_v001.ma", container_config="D:/show/config/publish/maya_publish.yaml", ) ```

Publish Manager

PublishManager is a small runner for one or more graphs. The UI uses it to collect source files and publish them together.

from quanta import PublishManager

manager = PublishManager() manager.add_graph("DemoProject", scene_path, container_config_path) manager.run("Published from a custom tool") ```

Context

The graph context stores production entities and publish selections. It supports common keys such as Project, Episode, Sequence, Shot, AssetCategory, Asset, TaskType, Task, Container, Version, and Component.

Context values are normally entity objects from the ledger or database records returned by the publish API. Dictionaries with id and name fields also work in many places.

Container

A container groups related publishes for one context. Examples include master, anim, camera, render, and model. Containers are saved as DBContainerItem records.

Version

A version belongs to a container. Blueprint creates the next version candidate by looking up existing versions for the selected container. Versions are saved as DBVersionItem records.

Component

A component describes what kind of file is being published inside a version. Examples include maya_scene, abc_cache, usd_layer, thumbnail, and review_movie. Components are saved as DBComponentItem records.

Publish Item

A PublishItem represents one output that should be produced by a file publisher node. The build node creates publish items with session.add_publish_item(...).

A publish item carries its display name, publisher node name, component name, component type, user data, metadata, and its own publish chain.

Published File Record

When a file publisher calls pub_item.register_publish(self, file_path), Blueprint creates a DBPublishItem record. That record stores container, version, component, checkpoint results, metadata, user data, publisher name, file path, path template name, file hash, user id, and timestamps.

Publishing Lifecycle

1. A source file is loaded into a ContainerGraph. 2. Blueprint parses the publish blueprint YAML. 3. The graph resolves project/context from path templates and environment variables. 4. Pre-build checks run. 5. The build node runs and calls session.add_publish_item(...) for each output. 6. A container is selected or created. 7. A version is selected or created. 8. Each checked publish item runs its publish chain. 9. Pre-publish checks set success, warning, failed, or error states. 10. File publisher nodes write output files. 11. File publisher nodes call pub_item.register_publish(self, file_path). 12. Blueprint creates or reuses container, version, and component records. 13. Blueprint creates the publish item record with checkpoints, metadata, user data, and file hash.

Required Graph Structure

A valid publish graph must contain:

  • One `container` node.
  • One `build` node.
  • At least one publish branch containing `pre_publish_check` and/or `file_publisher` nodes.

The container node must define project_name and template_name. If the project_name in the graph does not match the show_name passed to ContainerGraph.load, loading fails.

Environment Context

Blueprint can seed graph context from environment variables:

BLUEPRINT_PROJECT_NAME
BLUEPRINT_EPISODE_NAME
BLUEPRINT_SEQUENCE_NAME
BLUEPRINT_SHOT_NAME
BLUEPRINT_ASSETCATEGORY_NAME
BLUEPRINT_ASSET_NAME
BLUEPRINT_TASKTYPE_NAME
BLUEPRINT_STEP_NAME
BLUEPRINT_TASK_NAME

For Maya and other DCC tools, set these before opening the custom tool so loaders and publishers can resolve context automatically.

When to Use Each API

Use ContainerGraph and PublishManager when you want to run a Blueprint publish workflow.

Use Package only when you already have a container, version, and component and want to manually register a file.

Use create_publish_item, get_all_publish_items, and related record helpers when you are building loaders, browsers, dashboards, or automation tools.

Use BlueprintStudioClient when you need direct HTTP-level control.

DOC / 03

Standalone Publisher Tutorial

Build a first publish flow and learn how Blueprint turns context into registered output.

PrerequisitesMinimal Standalone PublisherWhat Happens InternallyChoosing Context ExplicitlySelecting a ContainerSelecting a Version

This tutorial shows how to write a standalone Python publisher that runs outside the Blueprint UI but still uses the Blueprint publishing system.

Use this approach for command-line publishing, batch publishing, DCC shelf tools that should run without opening the full Blueprint window, and farm-side publish jobs that have the Blueprint runtime available.

Prerequisites

Your Python process must be able to import quanta.

Your workstation or job environment must know the Blueprint API URL when running in studio mode:

$env:BLUEPRINT_API_URL = "http://127.0.0.1:8000/api/v1"

For a remote license/API server:

$env:BLUEPRINT_API_URL = "http://10.10.10.27:8000/api/v1"

You should also provide production context, either through path templates or environment variables:

$env:BLUEPRINT_PROJECT_NAME = "DemoProject"
$env:BLUEPRINT_SEQUENCE_NAME = "sq010"
$env:BLUEPRINT_SHOT_NAME = "sh020"
$env:BLUEPRINT_STEP_NAME = "Animation"
$env:BLUEPRINT_APP_NAME = "maya"

Minimal Standalone Publisher

import os
from quanta import AuthManager, PublishManager

os.environ["BLUEPRINT_API_URL"] = "http://127.0.0.1:8000/api/v1" os.environ["BLUEPRINT_PROJECT_NAME"] = "DemoProject" os.environ["BLUEPRINT_SEQUENCE_NAME"] = "sq010" os.environ["BLUEPRINT_SHOT_NAME"] = "sh020" os.environ["BLUEPRINT_STEP_NAME"] = "Animation" os.environ["BLUEPRINT_APP_NAME"] = "maya"

auth = AuthManager() if not auth.login("artist@example.com", "password", remember_me=True): raise RuntimeError(auth.last_error or "Login failed")

manager = PublishManager() manager.add_graph( show_name="DemoProject", filepath="D:/show/DemoProject/sq010/sh020/work/scene_v001.ma", container_conf_path="D:/show/DemoProject/config/publish/maya_publish.yaml", )

for graph in manager.graphs: containers, default_name = graph.get_containers() container = next((c for c in containers if c.name == default_name), containers[0]) graph.update_context("Container", container)

versions = graph.get_versions() graph.update_context("Version", versions[0])

manager.run("Published from standalone publisher") ```

What Happens Internally

manager.add_graph(...) loads the source file into a ContainerGraph. During load, Blueprint reads the publish blueprint YAML, validates the container and build nodes, resolves path-template context where possible, applies environment context, runs pre-build checks, and runs the build node.

The build node creates publish items. A publish item is one output that will later be processed by a file publisher.

Your standalone script must choose a container and version before calling manager.run(...). The UI normally does this with the context widget; in standalone mode you do it in code.

Choosing Context Explicitly

If your path template or environment variables do not fully resolve context, set it explicitly.

from quanta import get_ledger

ledger = get_ledger() project = ledger.get_project("DemoProject") sequence = project.get_child("Sequence", "sq010") shot = sequence.get_child("Shot", "sh020") task_type = ledger.get_task_type("Animation")

graph.update_context("Project", project) graph.update_context("Sequence", sequence) graph.update_context("Shot", shot) graph.update_context("TaskType", task_type) ```

For asset work:

project = ledger.get_project("DemoProject")
category = project.get_child("AssetCategory", "Character")
asset = project.get_child("Asset", "Hero")
task_type = ledger.get_task_type("Rigging")

graph.update_context("Project", project) graph.update_context("AssetCategory", category) graph.update_context("Asset", asset) graph.update_context("TaskType", task_type) ```

Selecting a Container

graph.get_containers() returns existing containers matching the graph context. It also includes new container candidates from the container node settings.

containers, default_name = graph.get_containers()

container = next( (item for item in containers if item.name == "master"), containers[0], )

graph.update_context("Container", container) ```

If the selected container is new, Blueprint creates it during registration.

Selecting a Version

graph.get_versions() returns existing versions for the selected container, with the next version candidate inserted first. In most standalone publishers, use versions[0] to publish the next version.

versions = graph.get_versions()
next_version = versions[0]
graph.update_context("Version", next_version)

If the selected version is new, Blueprint creates it during registration.

Running One Graph Directly

from quanta import ContainerGraph

graph = ContainerGraph.load("DemoProject", source_path, publish_yaml)

containers, default_name = graph.get_containers() graph.update_context("Container", containers[0])

graph.update_context("Version", graph.get_versions()[0]) graph.run("Published by direct graph runner") ```

Reading Results

result = graph.get_result()
print(result.status, result.message)

for detail in graph.get_details(): print(detail.name, detail.status, detail.message) ```

A status above warning blocks publishing. Check nodes should call one of:

item.set_success("message")
item.set_warning("message")
item.set_failed("message")

Common Failure Cases

Missing container node

The publish YAML must contain a node of type container.

Missing build node

The publish YAML must contain a node of type build.

No publisher found

session.add_publish_item(..., publisher_name="...") must reference a file publisher node name that exists in the graph.

Publish did not register

A file publisher must call:

pub_item.register_publish(self, file_path)

Blueprint warns when a file publisher node does not contain .register_publish( in its code.

Context is missing values

Set the missing values with graph.update_context(...), fix your path template, or provide environment variables before loading the graph.

User is not attached to the publish

register_publish uses AuthManager().current_user. Log in before publishing, or restore an existing remembered session.

DOC / 04

Developer Docs

Core extension concepts for pipeline teams building custom workflow behavior.

Recommended Reading OrderCommon Use CasesI want to publish files from a standalone Python scriptI want to write code inside a Blueprint publish nodeI want to build a custom loader, dashboard, or automationI want to support another DCC or creative application

This documentation is for pipeline developers who want to connect custom tools, standalone publishers, or DCC integrations to Blueprint.

Blueprint has two developer-facing layers:

  • The Quanta Python API, used by tools running inside Blueprint, Maya, or a standalone Python process.
  • The Blueprint Studio HTTP API, used by the Quanta runtime when running against a central server.

Use the Python API when your tool is running on an artist workstation and has access to the Blueprint runtime package. Use the HTTP API when you are writing a service, web tool, sync process, or a custom application that should talk directly to the Blueprint server.

Recommended Reading Order

1. [Publishing System Overview](./publishing-system-overview.md) 2. [DCC Application Integration Guide](./dcc-application-integration-guide.md) 3. [Standalone Publisher Tutorial](./standalone-publisher-tutorial.md) 4. [Publish Graph Node Authoring](./publish-graph-node-authoring.md) 5. [Custom Tool API Guide](./custom-tool-api-guide.md) 6. [Publishing API Reference](./publishing-api-reference.md)

Common Use Cases

I want to publish files from a standalone Python script

Start with the standalone publisher tutorial. It shows how to authenticate, load a Blueprint publish graph, choose a container and version, and run the publish.

I want to write code inside a Blueprint publish node

Read the graph node authoring guide. It explains build nodes, pre-publish checks, file publishers, status reporting, and register_publish.

I want to build a custom loader, dashboard, or automation

Read the custom tool API guide. It covers AuthManager, BlueprintStudioClient, the record helpers, and direct HTTP calls.

I want to support another DCC or creative application

Read the DCC application integration guide. It explains how to create an application setup node, startup tool, handler, and hooks using MayaHandler as the reference.

I want field names and endpoint names

Use the publishing API reference.

Runtime Modes

Blueprint can run in demo mode or studio/server mode. Most public Python helpers automatically choose the right backend:

  • In demo mode, records are read from the local demo store.
  • In studio mode, records are read and written through the Blueprint Studio API.

For custom tools, prefer the public quanta imports where possible. They hide the storage backend and keep your tool portable.

DOC / 05

Publish Graph Node Authoring

Create reusable graph nodes for validation, publishing, metadata, and studio logic.

Node Typescontainerbuildpre_publish_checkfile_publisherBuild Node API

Blueprint publish graphs are written as node chains. Each node stores Python code. At runtime, Blueprint executes the node code and passes the correct object into run(...).

This guide is for pipeline TDs writing build nodes, checks, and file publishers.

Node Types

container

The container node declares the graph's project and path template.

Required properties:

project_name: DemoProject
template_name: maya_publish

Useful properties:

default_container_name: master
container_list:
  - master
  - anim
  - camera

build

The build node creates publish items. It receives the current ContainerGraph as session.

def run(self, session):
    session.add_publish_item(
        name="Maya Scene",
        publisher_name="Publish Maya Scene",
        component_name="scene",
        component_type="maya_scene",
        user_data={"source": session.name},
        metadata={"department": "animation"},
    )

The publisher_name must match the name of a file publisher node in one of the publish branches.

pre_publish_check

A pre-publish check receives the current PublishItem as item. It must set a status.

def run(self, item):
    source_path = item.get_source_path()
    if not source_path:
        item.set_failed("No source file was provided.")
        return

item.set_success("Source file found.") ```

Available status helpers:

item.set_success("message")
item.set_warning("message")
item.set_failed("message")

If the check raises an exception, Blueprint records an error state.

file_publisher

A file publisher receives the current PublishItem as pub_item. It must write or locate the output file, then register it.

def run(self, pub_item):
    output_path = pub_item.get_publish_path({
        "VERSION": pub_item.graph.context.get_version().name,
        "COMPONENT": pub_item.component_name,
        "EXT": "ma",
    })

pub_item.add_metadata("format", "mayaAscii") pub_item.add_user_data("note", "Published by Blueprint") pub_item.register_publish(self, output_path) ```

register_publish creates the container/version/component records if needed and then saves the publish file record.

Build Node API

Use session.add_publish_item(...) to declare outputs:

session.add_publish_item(
    name="Display name in UI",
    publisher_name="Publish Maya Scene",
    component_name="scene",
    component_type="maya_scene",
    user_data={"key": "value"},
    metadata={"department": "animation"},
)

Arguments:

  • `name`: label shown to the user.
  • `publisher_name`: file publisher node name that will handle this output.
  • `component_name`: component record name.
  • `component_type`: component type for grouping and loaders.
  • `user_data`: custom data saved with the publish record.
  • `metadata`: searchable/inspectable publish metadata.

Publish Item API

Inside checks and publishers, the PublishItem provides:

pub_item.get_source_path()
pub_item.get_publish_path(keys)
pub_item.register_publish(publisher_node, file_path)
pub_item.add_metadata(key, value)
pub_item.get_metadata(key, default=None)
pub_item.add_user_data(key, value)
pub_item.get_user_data(key, default=None)
pub_item.set_temp_data(key, value)
pub_item.get_temp_data(key, default=None)
pub_item.delete_temp_data(key)

Use temporary data when one check or action needs to pass runtime-only information to a later action. Temporary data is not saved to the publish record.

Path Resolution

pub_item.get_publish_path(keys) merges graph context values with the keys you provide and resolves the graph's path template.

Typical keys supplied by a publisher:

{
    "VERSION": pub_item.graph.context.get_version().name,
    "COMPONENT": pub_item.component_name,
    "EXT": "abc",
}

If a required token is missing, get_publish_path raises ValueError.

Registering a Publish

A successful file publisher must end with:

pub_item.register_publish(self, output_path)

During registration, Blueprint collects checkpoint results up to the current publisher, reads selected container and version from graph context, finds or creates the component, computes a short SHA256 file hash, and saves a DBPublishItem record.

Checkpoint Design

Good checks should be small and explicit. Prefer messages that tell the user what happened and what to fix.

def run(self, item):
    frame_range = item.get_user_data("frame_range")
    if not frame_range:
        item.set_warning("Frame range was not provided; using scene range.")
        return

item.set_success("Frame range is available.") ```

Use set_failed for blockers. Use set_warning for publishable but suspicious states.

Multiple Outputs

A build node can add several publish items that target different publisher nodes.

def run(self, session):
    session.add_publish_item(
        name="Maya Scene",
        publisher_name="Publish Maya Scene",
        component_name="scene",
        component_type="maya_scene",
    )
    session.add_publish_item(
        name="Alembic Cache",
        publisher_name="Publish Alembic",
        component_name="main_cache",
        component_type="abc_cache",
    )

Each publish item receives a copy of the publish chain that contains its referenced publisher.

Best Practices

  • Always call `register_publish` exactly once per output file.
  • Use stable component types; loaders group publishes by component type.
  • Store pipeline-facing data in `metadata`.
  • Store custom tool data in `user_data`.
  • Use `temp_data` only for runtime handoff between nodes.
  • Keep check nodes side-effect light.
  • Raise exceptions only for unexpected technical failures.
  • Use `set_failed` for expected validation failures.

Example File Publisher

def run(self, pub_item):
    import shutil
    from pathlib import Path

source = Path(pub_item.get_source_path()) version = pub_item.graph.context.get_version()

output = Path(pub_item.get_publish_path({ "VERSION": version.name, "COMPONENT": pub_item.component_name, "EXT": source.suffix.lstrip("."), })) output.parent.mkdir(parents=True, exist_ok=True) shutil.copy2(source, output)

pub_item.add_metadata("source_file", source.as_posix()) pub_item.add_metadata("file_ext", source.suffix.lstrip(".")) pub_item.register_publish(self, output.as_posix()) ```

DOC / 06

Custom Tool Api Guide

Connect custom tools to Blueprint services and package data.

Choose the Right LayerUse Quanta Python helpers when possibleUse the HTTP client for direct server callsUse raw HTTP for non-Python toolsAuthenticationPython Login

This guide is for developers building custom loaders, dashboards, sync tools, web services, DCC panels, or automation around Blueprint.

Choose the Right Layer

Use Quanta Python helpers when possible

Use this layer for workstation tools, DCC tools, and Python applications running with the Blueprint runtime.

from quanta import (
    AuthManager,
    get_all_publish_items,
    get_all_containers,
    get_all_versions,
    get_all_components,
)

These helpers work in demo mode and studio mode.

Use the HTTP client for direct server calls

Use this layer when you want explicit endpoint control.

from quanta._internal.runtime_service.studio_client import BlueprintStudioClient

client = BlueprintStudioClient( base_url="http://127.0.0.1:8000/api/v1", token=access_token, ) ```

Use raw HTTP for non-Python tools

Any language can talk to the Blueprint server with HTTP and bearer tokens.

Authentication

Python Login

from quanta import AuthManager

auth = AuthManager() if not auth.login("artist@example.com", "password", remember_me=True): raise RuntimeError(auth.last_error or "Login failed")

print(auth.get_current_user_info()) ```

In studio mode, login calls POST /auth/login, POST /license/validate, and GET /auth/me. Blueprint validates the core quanta license during login.

Raw HTTP Login

curl.exe -X POST "http://127.0.0.1:8000/api/v1/auth/login" `
  -H "Content-Type: application/json" `
  -d "{\"email\":\"artist@example.com\",\"password\":\"password\"}"

The response includes access_token. Pass it as a bearer token:

curl.exe "http://127.0.0.1:8000/api/v1/auth/me" `
  -H "Authorization: Bearer ACCESS_TOKEN"

License Validation

Python:

allowed, reason, payload = auth.validate_license(["quanta", "my_custom_plugin"])
if not allowed:
    raise RuntimeError(reason or "License denied")

HTTP:

curl.exe -X POST "http://127.0.0.1:8000/api/v1/license/validate" `
  -H "Authorization: Bearer ACCESS_TOKEN" `
  -H "Content-Type: application/json" `
  -d "{\"plugin_keys\":[\"quanta\",\"my_custom_plugin\"]}"

Release plugin leases when your tool closes:

auth.release_license(["my_custom_plugin"])

Reading Publishes

List publish items

from quanta import get_all_publish_items

items = get_all_publish_items(project_id="PROJECT_ID") for item in items: print(item.component.name, item.version.name, item.filepath) ```

Common filters:

get_all_publish_items(project_id="...")
get_all_publish_items(shot_id="...", tasktype_id="...")
get_all_publish_items(asset_id="...", tasktype_id="...")
get_all_publish_items(container_id="...")
get_all_publish_items(version_id="...")

The exact accepted filters depend on the server implementation, but the built-in tools commonly use project, episode, sequence, shot, asset, asset category, task type, task, container, and version ids.

Find publish items for multiple assets

from quanta import get_publish_items_by_ids

items = get_publish_items_by_ids( asset_ids=["ASSET_ID_A", "ASSET_ID_B"], tasktype_id="RIGGING_TASKTYPE_ID", ) ```

This is used by builder workflows to collect asset inventory for a shot.

Lookup one publish item

from quanta import get_publish_item_by_column

item = get_publish_item_by_column("filepath", "D:/show/publish/file.abc") ```

Supported lookup columns are server-defined. The demo store supports common publish columns such as id, filepath, publisher name, and file hash.

Reading Containers, Versions, and Components

from quanta import (
    get_all_containers,
    get_all_versions,
    get_all_components,
)

containers = get_all_containers(project_id="PROJECT_ID", shot_id="SHOT_ID") versions = get_all_versions(containers[0].id) components = get_all_components(containers[0].id, versions[0].id) ```

Creating Records Manually

For most publishing tools, prefer the graph workflow or Package. If you need low-level control, use the dataclasses and create helpers.

from quanta import (
    DBContainerItem,
    DBVersionItem,
    DBComponentItem,
    DBPublishItem,
    create_container,
    create_version,
    create_component,
    create_publish_item,
)

container = create_container(DBContainerItem( name="master", project_id="PROJECT_ID", shot_id="SHOT_ID", tasktype_id="TASKTYPE_ID", ))

version = create_version(DBVersionItem( name="v001", version_number=1, container_id=container.id, ))

component = create_component(DBComponentItem( name="cache", type="abc_cache", version_id=version.id, ))

publish = create_publish_item(DBPublishItem( container=container, version=version, component=component, publisher_name="Custom Alembic Exporter", filepath="D:/show/publish/cache_v001.abc", path_template_name="maya_publish", metadata={"frame_start": 1001, "frame_end": 1100}, user_data={"tool": "custom_cache_exporter"}, )) ```

Direct HTTP Client

from quanta._internal.runtime_service.studio_client import BlueprintStudioClient

client = BlueprintStudioClient( base_url="http://127.0.0.1:8000/api/v1", token="ACCESS_TOKEN", )

publishes = client.get("publish/items", params={"project_id": "PROJECT_ID"}) ```

The client disables proxy environment inheritance with trust_env=False. This avoids Windows or studio proxy settings accidentally hijacking local Blueprint API calls.

Custom Loader Pattern

A loader usually needs to resolve the current DCC context, query publish items for that context, group records into components, let the user choose a version, and import, reference, load, or update files in the DCC.

For DCC tools, subclass or mirror BaseDccHandler:

from quanta import BaseDccHandler, DccHandlerMode

class MyDccHandler(BaseDccHandler): def __init__(self): super().__init__(mode=DccHandlerMode.LOADER)

def paths_in_scene(self): return []

def on_update(self, items): pass ```

BaseDccHandler.get_items() already knows how to query publishes for loader, updater, browser, and builder modes.

HTTP Endpoint Summary

Common endpoints used by the Python runtime:

POST /auth/login
GET  /auth/me
POST /auth/register
POST /auth/password-reset/request
POST /auth/password-reset/confirm
GET  /auth/departments
GET  /auth/user-names

POST /license/validate POST /license/release

GET /publish/items POST /publish/items POST /publish/items/by-assets GET /publish/items/lookup GET /publish/containers POST /publish/containers GET /publish/containers/{container_id} GET /publish/containers/{container_id}/versions POST /publish/versions GET /publish/versions/{version_id} GET /publish/versions/{version_id}/components POST /publish/components GET /publish/components/{component_id} GET /publish/components/lookup/by-name ```

Error Handling

The HTTP client raises httpx.HTTPStatusError for non-2xx responses. Wrap custom API calls:

import httpx

try: payload = client.get("publish/items") except httpx.HTTPStatusError as exc: print(exc.response.status_code, exc.response.text) except httpx.RequestError as exc: print("Connection failed", exc) ```

For workstation tools, show user-friendly messages for missing API URL, expired session, license denial, missing context, missing publish records, and file path not found.

DOC / 07

Publishing Api Reference

Reference for publishing endpoints, payloads, and integration contracts.

Public Python ImportsAuthentication`AuthManager.login(email, password, remember_me=False)``AuthManager.validate_license(plugin_keys=None)``AuthManager.release_license(plugin_keys=None)``AuthManager.get_current_user_info()`

This page is a compact reference for the Blueprint publishing Python API and the matching server endpoints used by the runtime.

Public Python Imports

Most custom tools should import from quanta:

from quanta import (
    AuthManager,
    Context,
    PublishManager,
    ContainerGraph,
    PublishItem,
    PublishError,
    DBContainerItem,
    DBVersionItem,
    DBComponentItem,
    DBPublishItem,
    get_all_publish_items,
    get_publish_items_by_ids,
    get_publish_item_by_column,
    create_publish_item,
    get_all_containers,
    create_container,
    get_all_versions,
    create_version,
    get_all_components,
    get_component,
    create_component,
)

Authentication

`AuthManager.login(email, password, remember_me=False)`

Logs in and restores user permissions. In studio mode, also validates the core quanta license. Returns True or False. On failure, read auth.last_error.

`AuthManager.validate_license(plugin_keys=None)`

Validates the current session against one or more plugin keys.

Returns:

(allowed: bool, reason: str | None, payload: dict)

`AuthManager.release_license(plugin_keys=None)`

Releases plugin license leases for the current session.

`AuthManager.get_current_user_info()`

Returns:

{
    "user_id": "...",
    "email": "...",
    "name": "...",
}

Publish Runner

`PublishManager.add_graph(show_name, filepath, container_conf_path)`

Loads one source file into a ContainerGraph unless it is already loaded.

`PublishManager.run(publish_note="")`

Runs all checked graphs.

`ContainerGraph.load(show_name, filepath, container_config)`

Loads and validates a publish blueprint YAML, resolves context, runs pre-build nodes, and runs the build node.

`ContainerGraph.update_context(key, value)`

Sets a context value and re-evaluates graph context. Accepted keys include Project, Episode, Sequence, Shot, AssetCategory, Asset, TaskType, Task, Container, and Version.

`ContainerGraph.get_containers()`

Returns:

(containers: list[DBContainerItem], default_name: str | None)

Includes existing matching containers and new candidates from the container node.

`ContainerGraph.get_versions()`

Returns versions for the selected container. If versions already exist, the next version candidate is inserted first.

`ContainerGraph.run(publish_note="")`

Runs all checked publish items. Publishing is skipped if graph status is worse than warning.

Build Node Method

`ContainerGraph.add_publish_item(...)`

session.add_publish_item(
    name="Maya Scene",
    publisher_name="Publish Maya Scene",
    component_name="scene",
    component_type="maya_scene",
    user_data={},
    metadata={},
)

Raises ContainerError if component_name or component_type is missing, or if publisher_name does not match a file publisher node.

Publish Item Methods

`PublishItem.get_source_path()`

Returns the source file path loaded into the graph.

`PublishItem.get_publish_path(keys)`

Resolves the graph path template using graph context plus explicit keys. Raises ValueError when a required token is missing.

`PublishItem.register_publish(publisher, file_path)`

Registers an output file in Blueprint. This creates or reuses container, version, and component records, computes the file hash, and creates a publish record.

`PublishItem.add_metadata(key, value)`

Adds metadata saved with the publish record.

`PublishItem.add_user_data(key, value)`

Adds custom data saved with the publish record.

Status helpers

PublishItem.set_success(message)
PublishItem.set_warning(message)
PublishItem.set_failed(message)

These set the status for the currently running action.

Record Dataclasses

`DBContainerItem`

id, db_type, project_id, episode_id, sequence_id, shot_id, asset_id,
assetcategory_id, tasktype_id, task_id, name, metadata, container_path,
created_at, updated_at, is_new

`DBVersionItem`

id, name, container_id, version_number, files, note, created_at, updated_at, is_new

`DBComponentItem`

id, name, type, version_id, created_at, updated_at

`DBPublishItem`

id, container, version, component, is_approved, checkpoints, metadata,
publisher_name, filepath, path_template_name, user_data, dependencies,
is_new, filehash, published_at, updated_at, user_id, user_name

Record Helpers

Publish Items

get_all_publish_items(**filters) -> list[DBPublishItem]
get_publish_items_by_ids(asset_ids: list[str], tasktype_id: str) -> list[DBPublishItem]
get_publish_item_by_column(column: str, value: str) -> DBPublishItem | None
create_publish_item(publish_item: DBPublishItem) -> DBPublishItem | None

Containers

get_all_containers(**filters) -> list[DBContainerItem]
get_container_by_id(container_id: str) -> DBContainerItem | None
create_container(container: DBContainerItem) -> DBContainerItem | None

Versions

get_all_versions(container_id: str) -> list[DBVersionItem]
get_version_by_id(version_id: str) -> DBVersionItem | None
create_version(version: DBVersionItem) -> DBVersionItem | None

Components

get_all_components(container_id: str, version_id: str) -> list[DBComponentItem]
get_component(container_id: str, version_id: str, component_name: str) -> DBComponentItem | None
create_component(component: DBComponentItem) -> DBComponentItem | None

HTTP Endpoints Used by Runtime

Base URL defaults to:

http://127.0.0.1:8000/api/v1

Override with BLUEPRINT_API_URL.

Auth

POST /auth/login
GET  /auth/me
POST /auth/register
POST /auth/password-reset/request
POST /auth/password-reset/confirm
GET  /auth/departments
GET  /auth/user-names

License

POST /license/validate
POST /license/release

Publish Items

GET  /publish/items
POST /publish/items
POST /publish/items/by-assets
GET  /publish/items/lookup

Create publish item payload:

{
  "id": null,
  "container_id": "CONTAINER_ID",
  "version_id": "VERSION_ID",
  "component_id": "COMPONENT_ID",
  "user_id": "USER_ID",
  "is_approved": false,
  "checkpoints": [],
  "metadata": {},
  "publisher_name": "Publish Maya Scene",
  "filepath": "D:/show/publish/scene_v001.ma",
  "path_template_name": "maya_publish",
  "user_data": {},
  "dependencies": {},
  "filehash": "abc123"
}

Containers

GET  /publish/containers
POST /publish/containers
GET  /publish/containers/{container_id}
GET  /publish/containers/{container_id}/versions

Create container payload:

{
  "id": null,
  "db_type": "",
  "project_id": "PROJECT_ID",
  "episode_id": null,
  "sequence_id": "SEQUENCE_ID",
  "shot_id": "SHOT_ID",
  "asset_id": null,
  "assetcategory_id": null,
  "tasktype_id": "TASKTYPE_ID",
  "task_id": null,
  "name": "master",
  "metadata": {},
  "container_path": ""
}

Versions

POST /publish/versions
GET  /publish/versions/{version_id}
GET  /publish/versions/{version_id}/components

Create version payload:

{
  "id": null,
  "name": "v001",
  "container_id": "CONTAINER_ID",
  "version_number": 1,
  "files": [],
  "note": ""
}

Components

POST /publish/components
GET  /publish/components/{component_id}
GET  /publish/components/lookup/by-name

Create component payload:

{
  "id": null,
  "name": "scene",
  "type": "maya_scene",
  "version_id": "VERSION_ID"
}

Status Values

Blueprint check results use these practical states:

Pending
Success
Warning
Failed
Error
Skipped

A graph or publish item should not continue when a prior result is worse than warning.

File Hashing

Package.register_file(...) computes a SHA256 hash from the output path and stores the first 16 characters in DBPublishItem.filehash. If the file cannot be read, the hash is saved as an empty string.

DOC / 08

DCC Application Integration Guide

Integrate DCC tools such as Maya, Houdini, Nuke, Blender, Unreal, and studio plugins.

Reference FilesIntegration Layout1. Add a Show Setup Node2. Write the Startup Tool3. Create the Application Handler4. Understand BaseDccHandler Behavior

This guide explains how to add support for a new DCC or creative application in Blueprint, using the Maya integration as the reference implementation.

The Maya integration is made from three cooperating parts:

  • A show setup node that lets Blueprint store application configuration.
  • A startup tool that injects Blueprint scripts into the launched application.
  • A DCC handler, like `MayaHandler`, that knows how to load, update, build, and inspect files inside that application.

Reference Files

Use these files as the main examples:

src/plugins/node_editor/nodes/show_setup/maya/
src/startup_tools/maya/startup.py
src/startup_tools/maya/modules/blueprint.mod
src/startup_tools/maya/modules/scripts/maya_handler.py
src/startup_tools/maya/modules/scripts/maya_hooks.py
src/startup_tools/maya/modules/scripts/blueprint_menu.py
src/quanta/_internal/dcc_loader_service.py
src/quanta/ignition/

Integration Layout

A new application should normally follow this shape:

src/plugins/node_editor/nodes/show_setup/<app_name>/
    __init__.py
    <app_name>.py
    data.yaml

src/startup_tools/<app_name>/ startup.py modules/ or scripts/ <app_name>_handler.py <app_name>_hooks.py application menu/bootstrap files ```

Keep the application name lowercase where it is used as an internal key. Maya uses maya; a Houdini integration would use houdini; Nuke would use nuke.

1. Add a Show Setup Node

The node under plugins/node_editor/nodes/show_setup/<app_name> lets users configure the application in Blueprint's show setup graph.

Mirror the existing Maya, Houdini, or Nuke node class:

from qtpy.QtWidgets import QWidget

from plugins.node_editor.graph import Node, NodeMetadata, PortConfig from plugins.node_editor.widgets import PropertyWidget

class Houdini(Node): def __init__(self, metadata: NodeMetadata): super().__init__(metadata)

self.add_input( PortConfig( name="input", types=["project", "project_config", "dcc"], description="Input for Houdini node", default_value={}, ) )

self.add_output( PortConfig( is_input=False, name="output", description="Output for Houdini node", default_value={}, ) )

def get_data_editor(self, parent) -> QWidget: self._editor = PropertyWidget( node=self, config_tab=True, parameters_tab=True, show_export_button=True, export_node=True, parent=parent, ) return self._editor ```

In data.yaml, define:

  • `internal_name`: the lowercase app key, exported but not editable.
  • `is_default`: whether this app should be default for matching file types.
  • `file_extension`: extensions handled by the app.
  • `APP_LOCATION`: executable path.
  • App-specific environment variables such as `MAYA_MODULE_PATH`, `HOUDINI_PATH`, `NUKE_PATH`, or custom plugin paths.

Use export_tag: Extend for path variables that should be appended to an existing environment variable instead of replacing it.

2. Write the Startup Tool

startup_tools/<app_name>/startup.py is called when Blueprint prepares the application's environment. It should return a dictionary of environment variables and path lists.

Maya's startup tool injects a module path and optional Python runtime paths:

import os

from quanta import get_nucleus

def _split_paths(value: str) -> list[str]: return [path for path in str(value or "").split(os.pathsep) if path]

def app_start() -> dict: bp_runtime = get_nucleus() bp_path = bp_runtime.get_blueprint_paths()

startup_path = os.getenv("STARTUP_TOOL_PATH") or bp_path.startup_tool_path module_path = startup_path / "my_app" / "modules" python_paths = _split_paths(os.getenv("BLUEPRINT_MY_APP_PYTHONPATH", ""))

return { "MY_APP_MODULE_PATH": [str(module_path)], "PYTHONPATH": python_paths, } ```

The exact variables depend on the host application. The important rule is that the launched application must be able to import:

  • `quanta`
  • your `<app_name>_handler.py`
  • your `<app_name>_hooks.py`
  • any menu or UI bootstrap modules

3. Create the Application Handler

The handler is the main adapter between Blueprint and the host application. It should inherit from BaseDccHandler and implement the host-specific operations.

Import public symbols from quanta:

from typing import Optional

from quanta import ( BaseDccHandler, BucketItem, BucketMethod, ComponentItem, DccAction, DccEvent, DccHandlerMode, run_hooks, ) ```

Start with this skeleton:

class MyAppHandler(BaseDccHandler):
    def __init__(self, mode: Optional[DccHandlerMode] = None):
        super().__init__(mode=mode)
        self.is_item_checkable = True

def on_import(self, item: ComponentItem): self._add_to_bucket(item, BucketMethod.IMPORT)

def on_reference(self, item: ComponentItem): self._add_to_bucket(item, BucketMethod.REFERENCE)

def on_load(self, items: list[BucketItem]): for bucket_item in items or []: item = bucket_item.item publish_file = self._get_publish_file(item) if not publish_file: continue

action = DccAction( handler=self, mode=self.mode, item=item, bucket_item=bucket_item, publish_file=publish_file, method=bucket_item.method, ) run_hooks(DccEvent.BEFORE_LOAD, action) if action.cancelled: continue

action.loaded_items = self._load_into_host( action.publish_file, action.method, ) if action.loaded_items: run_hooks(DccEvent.AFTER_LOAD, action)

def on_update(self, items: list[ComponentItem]): for item in items or []: if not getattr(item, "checked", False): continue # Find existing host references/nodes and replace them with item.version.

def on_build(self, items: list[ComponentItem]): action = DccAction(handler=self, mode=self.mode, data={"items": items}) run_hooks(DccEvent.BEFORE_BUILD, action) if action.cancelled: return

self.on_load(self.bucket) run_hooks(DccEvent.AFTER_BUILD, action)

def paths_in_scene(self) -> list[str]: return []

def get_scene_item_ids(self) -> list[str]: return [] ```

Maya adds helper methods such as _get_publish_file, namespace generation, reference discovery, and shader assignment. For another application, replace those with the native concepts of that host:

  • Reference nodes, file layers, clips, packages, or imported assets.
  • Namespace, node path, layer name, or package identifier.
  • Native file commands for import, reference, update, and delete.

4. Understand BaseDccHandler Behavior

BaseDccHandler already provides shared Blueprint behavior:

  • `context`: builds a Blueprint context from environment variables such as `BLUEPRINT_PROJECT_NAME`, `BLUEPRINT_STEP_NAME`, `BLUEPRINT_ASSET_NAME`, `BLUEPRINT_SEQUENCE_NAME`, and `BLUEPRINT_SHOT_NAME`.
  • `get_items()`: returns publishable items for the current mode.
  • `_add_to_bucket()`: queues a `ComponentItem` for import or reference.
  • `bucket`: the pending load/build list.
  • `menus`: custom menu actions that a UI can expose.
  • `paths_in_scene()`: meant to be overridden so updater mode can compare scene paths with published paths.
  • `on_update()`: meant to be overridden for application-specific reference updates.
  • `get_scene_item_ids()`: meant to be overridden if the host stores Blueprint publish ids in the scene.

The supported modes are:

DccHandlerMode.LOADER   # all publish items for the project
DccHandlerMode.BROWSER  # normal browser mode
DccHandlerMode.BUILDER  # shot inventory from browser/builder_config.yaml
DccHandlerMode.UPDATER  # current scene references plus latest available versions

5. Write Hooks

Hooks are registered through quanta.ignition and are available from the public quanta package.

Blueprint currently defines these DCC events:

DccEvent.BEFORE_LOAD
DccEvent.AFTER_LOAD
DccEvent.BEFORE_UPDATE
DccEvent.AFTER_UPDATE
DccEvent.BEFORE_BUILD
DccEvent.AFTER_BUILD

A hook receives a DccAction object. It can inspect or modify the action, append messages, or cancel the operation.

from quanta import DccEvent, register_hook

def validate_file_before_load(action): publish_file = action.publish_file if not publish_file: action.cancel("No publish file was supplied.") return

if not str(publish_file.filepath).lower().endswith((".abc", ".usd")): action.cancel(f"Unsupported file type: {publish_file.filepath}")

def report_after_load(action): print(f"Loaded {action.publish_file.filepath}: {action.loaded_items}")

def register_default_hooks() -> None: register_hook(DccEvent.BEFORE_LOAD, validate_file_before_load) register_hook(DccEvent.AFTER_LOAD, report_after_load)

register_default_hooks() ```

Returning False from a hook also cancels the action:

def block_camera_loads(action):
    if action.component_type == "camera":
        action.messages.append("Camera loads are disabled in this context.")
        return False

Import your hooks module inside the handler or application bootstrap so registration happens once:

try:
    import my_app_hooks  # noqa: F401
except ImportError:
    from . import my_app_hooks  # noqa: F401

register_hook prevents duplicate registration for the same module and function name, so re-importing the same hooks module is safe in normal use.

6. Use DccAction Correctly

DccAction is the payload passed to hooks. Common fields are:

  • `handler`: the active application handler.
  • `event_name`: filled by `run_hooks`.
  • `mode`: the active `DccHandlerMode`.
  • `item`: the `ComponentItem` selected in the UI.
  • `bucket_item`: the queued item with method and count.
  • `publish_file`: the database publish record being loaded or updated.
  • `method`: `BucketMethod.IMPORT` or `BucketMethod.REFERENCE`.
  • `namespace`: Maya-style namespace or the equivalent host identifier.
  • `scene_path`: current path in the scene during updates.
  • `ref_node`: host reference node during updates.
  • `loaded_items`: host nodes, refs, layers, or identifiers created by the operation.
  • `data`: extra event-specific data.
  • `messages`: warnings or user-facing notes collected by hooks.
  • `cancelled`: whether the operation should stop.

Prefer action.cancel("message") instead of setting cancelled manually when you want the reason to travel with the action.

7. Add Menus or Host UI

The Maya menu is built in blueprint_menu.py. A new application should provide an equivalent host bootstrap that opens Blueprint widgets or actions from the application's UI.

For loader, builder, updater, and publisher behavior, Maya calls widget helpers from blueprint_menu_widgets:

get_loader_widget(parent).show()
get_builder_widget(parent).show()
get_updater_widget(parent).show()
get_publisher_widget(menu_label, config_path, version, parent, scene_path).show()

If the host cannot embed Qt widgets directly, create a thin command layer that launches Blueprint tools as a separate window or process, while still using the same handler and environment variables.

8. Handler Checklist

Before considering a new application supported, verify these points:

  • The show setup node exports the correct `internal_name`, executable path, extensions, and environment variables.
  • `startup.py` returns all paths required for the host to import Blueprint and the integration scripts.
  • The host application imports the handler and hooks successfully.
  • `on_import` and `on_reference` add items to the bucket.
  • `on_load` can load at least one published file and fires `BEFORE_LOAD` and `AFTER_LOAD`.
  • `paths_in_scene` returns normalized file paths for existing host references.
  • `on_update` can replace an existing loaded file and fires `BEFORE_UPDATE` and `AFTER_UPDATE`.
  • `on_build` loads the builder bucket and fires `BEFORE_BUILD` and `AFTER_BUILD`.
  • Hook cancellation stops the operation cleanly.
  • User-facing warnings come from the host UI when possible and from `action.messages` when the hook needs to report context.

9. Minimal New App Example

This example shows the smallest useful shape. Replace the pseudo host API calls with the application's real Python API.

from pathlib import Path
from typing import Optional

from quanta import ( BaseDccHandler, BucketItem, BucketMethod, ComponentItem, DccAction, DccEvent, DccHandlerMode, run_hooks, )

try: import my_app_hooks # noqa: F401 except ImportError: from . import my_app_hooks # noqa: F401

class MyAppHandler(BaseDccHandler): def __init__(self, mode: Optional[DccHandlerMode] = None): super().__init__(mode=mode) self.is_item_checkable = True

def _get_publish_file(self, item: ComponentItem): version = item.version or (item.versions[0] if item.versions else "") files = item.files.get(version, []) return files[0] if files else None

def _load_into_host(self, publish_file, method: BucketMethod) -> list[str]: path = Path(publish_file.filepath) if not path.exists(): return []

if method == BucketMethod.REFERENCE: ref_id = host_api.reference_file(path.as_posix()) return [ref_id]

node_ids = host_api.import_file(path.as_posix()) return list(node_ids or [])

def on_import(self, item: ComponentItem): self._add_to_bucket(item, BucketMethod.IMPORT)

def on_reference(self, item: ComponentItem): self._add_to_bucket(item, BucketMethod.REFERENCE)

def on_load(self, items: list[BucketItem]): for bucket_item in items or []: publish_file = self._get_publish_file(bucket_item.item) if not publish_file: continue

action = DccAction( handler=self, mode=self.mode, item=bucket_item.item, bucket_item=bucket_item, publish_file=publish_file, method=bucket_item.method, ) run_hooks(DccEvent.BEFORE_LOAD, action) if action.cancelled: continue

action.loaded_items = self._load_into_host( action.publish_file, action.method, ) if action.loaded_items: run_hooks(DccEvent.AFTER_LOAD, action)

def paths_in_scene(self) -> list[str]: return [ref.filepath for ref in host_api.list_references()] ```

Practical Notes

  • Use public `quanta` imports whenever possible. They keep the integration stable if internal module paths move.
  • Normalize paths with `Path(path).as_posix()` before comparing scene paths and publish paths.
  • Keep host API calls inside the handler. Hooks should customize policy and post-processing, not replace the handler's basic load/update implementation.
  • Keep hooks idempotent. Users may reload scripts while developing inside a DCC.
  • Prefer small app-specific helpers over large copies of `MayaHandler`. Copy the lifecycle shape, not every Maya detail.