Databricks Solutions Accelerator
A deployable Databricks project that routes tracks through MCP, A2A or the REST API with a shared audit trail.
The Spatial9 Solutions Accelerator for Databricks is a deployable Databricks Asset Bundle that turns a music catalog stored in Unity Catalog into immersive audio on a schedule. Every render is governed, idempotent and audited. The bundle contains six notebooks and reaches Spatial9 through three routes: the MCP server, the A2A agent and the Spatializer REST API.
This page is for data engineers and platform teams who run Databricks and want a production starting point for catalog-scale spatial audio. It describes the architecture, the tables and Volumes the accelerator creates, the deploy procedure, how to route a track, the governance controls and how to expose Spatial9 to Mosaic AI agents. The files live in the Agent Skills repository under examples/enterprise/databricks/solution-accelerator, which you can download from /agents/skills.
Architecture
New masters land in a Volume. Auto Loader records each file once in a bronze table, and a MERGE adds it to the render queue, a Delta table that is the source of truth. Each queue row carries a route that selects the integration. Finished files are written to the immersive Volume, every state change is appended to an audit table, and gold views summarize coverage, throughput and failures.
masters Volume --Auto Loader--> bronze_masters --MERGE--> render_queue (Delta, source of truth)
|
route = mcp route = a2a route = api
03 Spatial9 MCP server 04 Spatial9 A2A agent 05 Spatializer REST API
create_spatial_audio, split_stems, convert_dolby, stems -> Dolby Atmos + IAMF
get_job_status, get_downloads preview_chorus (free) (tus resumable upload)
|
immersive Volume + render_events (audit) --> 06 gold views
Choosing a route
The route column decides which notebook processes a row. All three routes write to the same Volume and the same audit table.
| Route | Use it when | What comes back |
|---|---|---|
mcp | You have a stereo master and want the full Spatial9 render | Eclipsa Audio (IAMF), YouTube-ready MP4, binaural headphone WAV |
a2a | You want to hand a whole task to the Spatial9 agent and follow it | Stems as WAV, Dolby or ADM converted to Eclipsa Audio, or a free 30-second chorus preview |
api | You already have stems and need delivery masters | Dolby Atmos master and IAMF from the Spatializer REST API |
MCP and A2A use the same s9_ key and share the plan limits: 3 renders at once and 20 per day. Failed renders are refunded automatically. Chorus previews are free and do not count toward the render limit. The REST API route uses a separate key, spatializer_api_key.
Bundle contents
| File | Purpose |
|---|---|
databricks.yml | Asset Bundle: one job, five tasks, every 15 minutes (paused by default) |
00_config.py | Widgets, table names, limits and helpers shared by every notebook |
01_setup.py | Schema, four Volumes, queue and audit tables, optional governed HTTP connection |
02_ingest_masters.py | Auto Loader picks up new masters and queues them once |
03_render_mcp.py | Full immersive renders through the MCP server |
04_delegate_a2a.py | Stems, Dolby or ADM conversion and chorus previews through the A2A agent |
05_atmos_rest_api.py | Stems to Dolby Atmos and IAMF masters with resumable uploads |
06_gold_views.sql | Coverage, daily throughput and failure reasons for dashboards or Genie |
../../shared/spatial9_mcp.py | MCP client built on requests only |
../../shared/spatial9_a2a.py | A2A client built on requests only |
Requirements
- A Databricks workspace with Unity Catalog and the Databricks CLI configured.
- A SQL warehouse, whose ID you pass as
warehouse_idfor the gold views task. - A Spatial9 personal key (
s9_...) created at /connect, used for the MCP and A2A routes. - A Spatializer REST API key, only if you use the
apiroute. - Masters in WAV, FLAC or AIFF format that you own or license.
Deploy in five steps
- Create a key at /connect and store it in a secret scope with
databricks secrets create-scope spatial9and thendatabricks secrets put-secret spatial9 api_key. For the REST API route, also storespatializer_api_key. - Download the Agent Skills repository from /agents/skills and open a terminal in the
solution-acceleratorfolder. - Deploy the bundle with
databricks bundle deploy -t dev --var warehouse_id=<your SQL warehouse id>. - Run
01_setup.pyonce from the workspace, then copy a few WAV files into themastersVolume. - Run the job with
databricks bundle run spatial9_accelerator -t dev. When the first renders look correct, unpause the schedule.
databricks secrets create-scope spatial9
databricks secrets put-secret spatial9 api_key # s9_... key (MCP and A2A)
databricks secrets put-secret spatial9 spatializer_api_key # only for the REST API route
databricks bundle validate
databricks bundle deploy -t dev --var warehouse_id=<your SQL warehouse id>
databricks bundle run spatial9_accelerator -t dev
Job definition
The bundle defines one job named Spatial9 immersive catalog. It runs every 15 minutes in UTC, starts in the PAUSED state and allows only one concurrent run, which keeps the 3-renders-at-once limit simple to enforce. The bundle syncs the shared folder so the notebooks can import both clients.
resources:
jobs:
spatial9_accelerator:
name: Spatial9 immersive catalog
max_concurrent_runs: 1
schedule:
quartz_cron_expression: "0 0/15 * * * ?"
timezone_id: UTC
pause_status: PAUSED
tasks:
- task_key: ingest
notebook_task: { notebook_path: ./02_ingest_masters.py }
- task_key: render_mcp
depends_on: [{ task_key: ingest }]
notebook_task: { notebook_path: ./03_render_mcp.py }
- task_key: delegate_a2a
depends_on: [{ task_key: render_mcp }]
notebook_task: { notebook_path: ./04_delegate_a2a.py }
- task_key: atmos_rest_api
depends_on: [{ task_key: ingest }]
notebook_task: { notebook_path: ./05_atmos_rest_api.py }
- task_key: gold
depends_on: [{ task_key: delegate_a2a }, { task_key: atmos_rest_api }]
notebook_task:
notebook_path: ./06_gold_views.sql
warehouse_id: ${var.warehouse_id}
The bundle exposes four variables. Change them with --var at deploy time or through the notebook widgets.
| Variable | Default | Meaning |
|---|---|---|
catalog | main | Unity Catalog catalog |
schema | spatial9_accelerator | Schema for tables and Volumes |
secret_scope | spatial9 | Secret scope holding the keys |
warehouse_id | none | SQL warehouse for the gold views |
The bundle has two targets: dev (development mode, the default) and prod (production mode).
Storage layout
01_setup.py creates the schema and four Volumes. 00_config.py validates the catalog and schema names against a plain identifier pattern before they are used in SQL.
| Volume | Path | Contents |
|---|---|---|
masters | /Volumes/<catalog>/<schema>/masters | WAV, FLAC or AIFF masters to render |
stems | /Volumes/<catalog>/<schema>/stems | One folder per track for the REST API route |
immersive | /Volumes/<catalog>/<schema>/immersive | Finished files |
checkpoints | /Volumes/<catalog>/<schema>/checkpoints | Auto Loader checkpoints |
The render queue
render_queue holds one row per track, version and route. It is clustered by status and route, and two constraints restrict route to mcp, a2a or api and status to pending, running, completed or failed.
| Column | Type | Notes |
|---|---|---|
track_id | STRING | Required; derived from the file name on ingest |
version | INT | Required; part of the idempotency key |
route | STRING | mcp, a2a or api |
skill | STRING | For example spatial_audio, split_stems, convert_dolby, preview_chorus, atmos_master |
title | STRING | Required |
source_path | STRING | File or stems folder in a Volume |
style | STRING | live_concert, club, cinema or intimate_studio |
instruments | STRING | REST API route only: JSON of instrument UUID to stem file names |
status | STRING | Required |
job_id | STRING | Spatial9 job, A2A task or music ID |
stage | STRING | Latest reported stage |
outputs | STRING | JSON of file ID to Volume path |
error | STRING | Plain-language reason from Spatial9 |
updated_at | TIMESTAMP | Last change |
The audit table
render_events is an append-only audit trail with the columns track_id, version, route, job_id, event, detail and at. The log() helper writes one row per state change, such as started, preview, completed or failed.
Notebook behavior
02 Ingest
Auto Loader reads the masters Volume as binaryFile, filtered to *.{wav,flac,aif,aiff}, and drops the file content so only metadata is stored. It uses trigger(availableNow=True), so each run processes new files and then stops. A MERGE then inserts version 1 of every new track as a pending mcp row with skill spatial_audio and style live_concert. Re-running the cell never duplicates rows.
03 Render with MCP
The notebook first follows running renders with get_job_status. Completed jobs are downloaded through get_downloads, keeping binaural, iamf and youtube_video, and saved to the immersive Volume. Failed or cancelled jobs are marked failed and logged. It then starts new renders, up to the number returned by free_slots(), by creating an upload link with create_upload_link, uploading the file and calling create_spatial_audio with include_binaural set to true. If Spatial9 reports a limit, the loop stops and waits for the next scheduled run.
job = s9.call("create_spatial_audio", {
"upload_id": upload(row.source_path),
"media_type": "music",
"title": row.title,
"style": row.style or "live_concert",
"include_binaural": True,
"idempotency_key": f"databricks-{row.track_id}-v{row.version}",
})
04 Delegate with A2A
Rows with route = 'a2a' are sent to the Spatial9 A2A agent with message/send. The messageId is the idempotency key and follows the pattern databricks-<skill>-<track>-v<version>. Supported skills are split_stems, convert_dolby and preview_chorus. The notebook uses the MCP client only to create private upload links with the same key. Running tasks are followed with tasks/get. The input-required state is recorded as a failure with the message that Spatial9 needs a file, so check source_path.
05 Dolby Atmos and IAMF with the REST API
For labels that already have stems, each api row points source_path at a folder in the stems Volume and sets instruments to a JSON map of instrument UUID to stem file names. Run the second cell to list instrument IDs from GET /instruments. The notebook creates a music with POST /musics?formats=atmos,iamf, uploads each stem (and an optional file whose name starts with stereo) with tus resumable uploads in 32 MB chunks, then polls GET /musics/{id}/download?format=atmos and format=iamf. A master is ready when the download answers 200. The base URL is https://api.spatializer.spatial9.ai/api/v0.0.1 and the key is sent as-is in the Authorization header. See REST API.
06 Gold views
The SQL notebook creates three views for an AI/BI dashboard or a Genie space.
| View | Shows |
|---|---|
gold_catalog_coverage | Tracks per route and skill, counts per status and pct_done |
gold_daily_throughput | Renders started and completed per day, median minutes, remaining daily capacity |
gold_failure_reasons | Plain-language failure reasons, most common first |
Sending a track to a different route
Insert a queue row with the route and skill you want. The examples below request a free chorus preview through A2A and Dolby Atmos plus IAMF masters from stems through the REST API.
INSERT INTO main.spatial9_accelerator.render_queue (track_id, version, route, skill, title, source_path, status, updated_at)
VALUES ('night_drive', 1, 'a2a', 'preview_chorus', 'Night Drive', '/Volumes/main/spatial9_accelerator/masters/night_drive.wav', 'pending', current_timestamp());
INSERT INTO main.spatial9_accelerator.render_queue (track_id, version, route, skill, title, source_path, instruments, status, updated_at)
VALUES ('night_drive', 1, 'api', 'atmos_master', 'Night Drive', '/Volumes/main/spatial9_accelerator/stems/night_drive',
'{"<bass uuid>": ["BASS.wav"], "<drums uuid>": ["DRUMS.wav"], "<vocals uuid>": ["LEAD VOCALS.wav"]}', 'pending', current_timestamp());
To render a new version of an existing track, insert a row with a higher version. The new version produces a new idempotency key and therefore a new render.
Governance controls
- The key lives only in a secret scope and Databricks redacts it in notebook output.
- Every start uses an idempotency key (
databricks-<track>-v<version>), so retries never render or charge twice. free_slots()respects 3 renders at once and 20 per day before anything starts; chorus previews are excluded from the count.render_eventsrecords every start, completion and failure for audit.- Queue updates use parameterized SQL, so titles or errors containing quotes never break a statement.
- Only render music you own or license.
Agents on Databricks
01_setup.py also creates an optional governed HTTP connection named spatial9_mcp that points at https://spatial9.ai on port 443 with base path /mcp and reads the bearer token from the secret scope. Agents and SQL users can then reach Spatial9 without seeing the key.
CREATE CONNECTION IF NOT EXISTS spatial9_mcp TYPE HTTP
OPTIONS (host 'https://spatial9.ai', port '443', base_path '/mcp', bearer_token secret('spatial9', 'api_key'));
GRANT USE CONNECTION ON CONNECTION spatial9_mcp TO `catalog-engineers`;
Register the connection as an external MCP server in Unity AI Gateway (MCP Services). Mosaic AI agents can then call Spatial9 tools under their own identity, governed by USE CONNECTION grants.
Troubleshooting
- The job runs but nothing starts: the schedule is paused by default, or
free_slots()returned 0 because 3 renders are running or 20 have started in the last 24 hours. - A row stays pending with a limit error: the daily limit was reached; the next scheduled run retries it.
- A2A row fails with "Spatial9 needs a file": the task entered
input-required; check thatsource_pathpoints to a readable file. - API row never completes: the download endpoint has not answered 200 for both
atmosandiamf; thestagecolumn shows the latest status JSON. - Spatial9 rejected the key: the secret is wrong or revoked; create a new key at /connect and update the secret scope.
Next steps
- Enterprise data platforms: compare the Databricks recipe with Snowflake, Vertex AI and Bedrock.
- MCP server: the tools the
mcproute calls. - A2A agent: task states, skills and push notifications for the
a2aroute. - Limits and quotas: how the concurrency and daily limits apply.