Enterprise data platforms
Run governed spatial audio pipelines on Databricks, Snowflake, Google Vertex AI and Amazon Bedrock.
This page describes how to run governed spatial audio pipelines from the enterprise data and AI platforms your organization already operates: Databricks, Snowflake, Google Cloud with Vertex AI, and AWS with Amazon Bedrock. Each recipe takes tracks from a governed table, renders them with Spatial9 to Eclipsa Audio (IAMF) with a binaural headphone version, and writes statuses and file locations back for audit.
It is for data engineers, platform owners and security reviewers. The recipes live in the examples/enterprise folder of the Agent Skills repository, which you can download at /agents/skills. For the complete Databricks project with MCP, A2A and REST API routes, see Databricks Solutions Accelerator.
Platform overview
Every recipe follows the same rules: the key is stored in the platform's secret store, at most 3 renders run at once, each track and version gets its own idempotency_key, and results are written back for audit.
| Platform | Pattern | Folder |
|---|---|---|
| Databricks | Unity Catalog connection, Lakeflow Job, Volumes, optional AI Gateway MCP Services | databricks |
| Databricks Solutions Accelerator | Asset Bundle with Auto Loader, MCP, A2A and REST API routes, audit table, gold views | databricks/solution-accelerator |
| Snowflake | External access integration, Python procedure, Task, Cortex Agent custom tool | snowflake |
| Google Cloud and Vertex AI | ADK agent with McpToolset, Agent Engine, Cloud Run job and Scheduler, BigQuery | vertex-ai |
| AWS and Amazon Bedrock | Strands agent, Lambda, Step Functions, S3, AgentCore Gateway | bedrock |
| Any Python runtime | MCP handshake with requests only | shared/spatial9_mcp.py |
| Any Python runtime | A2A tasks: send, follow, cancel, webhooks | shared/spatial9_a2a.py |
Shared building blocks
Tools you call
| Tool | Purpose |
|---|---|
create_upload_link | Private upload link; returns upload_id and direct_upload_url |
create_spatial_audio | Starts an immersive render from source_url or upload_id |
create_from_stems | Starts a render from stems |
split_stems | Separates a song into stems |
preview_chorus | Free 30-second headphone preview |
get_job_status | Returns status, stage and progress |
get_downloads | Returns the only valid download links |
See MCP server for arguments and responses.
The MCP handshake in plain Python
shared/spatial9_mcp.py implements the MCP Streamable HTTP transport with only the requests library, so it runs in notebooks, Lambda, Cloud Run and similar runtimes.
- Send
initializewith protocol version2025-06-18. The server may answer with anMcp-Session-Idheader. - Send the
notifications/initializednotification. - Send one
tools/callper Spatial9 tool, echoingMcp-Session-IdandMCP-Protocol-Versionon every later request.
Requests carry Authorization: Bearer s9_..., Content-Type: application/json and Accept: application/json, text/event-stream. The server may reply with plain JSON or a text/event-stream; for a stream, the last data: message is the answer. An HTTP 401 means the key was rejected. A result.isError response contains a plain-language reason in result.content[0].text that is safe to log.
from spatial9_mcp import Spatial9MCP
client = Spatial9MCP(api_key=secret_from_your_vault)
job = client.call("create_spatial_audio", {
"source_url": "https://...", "media_type": "music", "title": "Northern Lights",
"include_binaural": True, "idempotency_key": "catalog-0042-v1",
})
done = client.wait(job["job_id"]) # polls every 45 seconds, up to 60 minutes
files = client.downloads(done["job_id"]) # binaural, youtube_video, iamf
The A2A client
shared/spatial9_a2a.py is an A2A 0.3.0 client for https://spatial9.ai/a2a. Use it when you want to hand a whole task to the Spatial9 agent and follow it.
| Method | Purpose |
|---|---|
message/send | Starts a task; returns it immediately in the submitted state |
tasks/get | Poll every 30 to 60 seconds until a final state |
tasks/cancel | Stops a task; refunded if paid processing had not started |
tasks/pushNotificationConfig/set | Registers an https webhook for state changes |
Supported skills are spatial_audio, split_stems, convert_dolby, preview_chorus and upload_link. Final states are completed, failed, canceled and rejected. The messageId is the idempotency key. Webhook endpoints must check the X-A2A-Notification-Token header against the token you registered. See A2A agent.
The queue pattern
Renders take 10 to 20 minutes, so no recipe runs one long process that waits for every render. Instead, a scheduled job runs every 15 minutes (or nightly) and does two things.
- Follow rows marked
running: callget_job_status, and on completion callget_downloads, save the files and mark the rowcompleted. Markfailedorcancelledjobs asfailedwith the reason. - Start new renders from rows marked
pending, only up to the number of free slots (3 minus the running count, and within 20 per day). If Spatial9 reports a limit, stop and wait for the next run.
The queue table is the source of truth. Each row is one track and version, with columns for status, job_id, stage, output locations, error and a timestamp.
Databricks
The basic Databricks recipe renders a catalog stored in Unity Catalog Volumes, keeps the key in a secret scope and writes every result back to a Delta table.
- Store the key:
databricks secrets create-scope spatial9, thendatabricks secrets put-secret spatial9 api_key. - Run
setup.sqlonce as a metastore or catalog admin. It creates the governed HTTP connection, themedia.spatial.renders_queuetable and themastersandimmersiveVolumes. - Copy
spatial9_mcp.pyandspatial9_catalog_job.pyinto one Workspace folder. - Create a Lakeflow Job with a Python task pointing at the job file, scheduled every 15 minutes.
- Insert rows with
status = 'pending'. Finished files appear in/Volumes/media/spatial/immersive.
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`;
To let Databricks agents call Spatial9 tools directly, register the spatial9_mcp connection as an external MCP server in Unity AI Gateway (MCP Services). Access is governed by USE CONNECTION grants and audited like any other Unity Catalog object.
Snowflake
The Snowflake recipe renders tracks listed in a table, with outbound access locked to spatial9.ai and the key stored as a Snowflake secret.
- Run
setup.sql, replacings9_replace_mewith your key. It creates a network rule, a secret, an external access integration, theRENDERS_QUEUEtable and an encryptedIMMERSIVEstage. - Run
spatial9_procedure.sql. It creates theSPATIAL9_RUNPython procedure and theSPATIAL9_EVERY_15_MINTask. - Insert rows into
RENDERS_QUEUEwith a presignedSOURCE_URLfor each master. - Run
CALL SPATIAL9_RUN(3);by hand first. It returns a report of renders started and finished and any errors.
CREATE OR REPLACE NETWORK RULE spatial9_egress
MODE = EGRESS
TYPE = HOST_PORT
VALUE_LIST = ('spatial9.ai');
CREATE OR REPLACE SECRET spatial9_key
TYPE = GENERIC_STRING
SECRET_STRING = 's9_replace_me';
CREATE OR REPLACE EXTERNAL ACCESS INTEGRATION spatial9_access
ALLOWED_NETWORK_RULES = (spatial9_egress)
ALLOWED_AUTHENTICATION_SECRETS = (spatial9_key)
ENABLED = TRUE;
The procedure uses Python 3.11 with snowflake-snowpark-python and requests, runs EXECUTE AS OWNER, and inlines the MCP handshake because procedures cannot import local files. Its idempotency key is snowflake-<TRACK_ID>-v<VERSION>. The Task runs on the cron */15 * * * * UTC.
Cortex Agents
Snowflake's own MCP server feature publishes Snowflake data to outside agents; it does not call outside servers. To let a Cortex Agent start renders, add SPATIAL9_RUN, or a smaller procedure per tool, as a custom tool. The agent then works through the same network rule, secret and grants.
Google Cloud and Vertex AI
Google Cloud supports two approaches: an agent your team talks to, or a scheduled job with no agent.
An ADK agent with Spatial9 tools
spatial9_agent/agent.py builds a Google Agent Development Kit LlmAgent that connects to https://spatial9.ai/mcp through McpToolset with StreamableHTTPConnectionParams. The key is read from the Secret Manager secret spatial9-api-key; the agent's service account needs the secret accessor role. deploy_agent_engine.py deploys the agent to Vertex AI Agent Engine, and adk web runs it locally.
spatial9 = McpToolset(
connection_params=StreamableHTTPConnectionParams(
url="https://spatial9.ai/mcp",
headers={"Authorization": f"Bearer {spatial9_key()}"},
timeout=60,
sse_read_timeout=600,
),
tool_filter=[
"create_upload_link", "create_spatial_audio", "create_from_stems",
"get_job_status", "get_downloads", "preview_chorus", "split_stems",
],
)
Use tool_filter to give each agent only the tools it needs; the example leaves out publish_to_youtube for an internal catalog bot. ADK can also expose an agent over A2A, and Spatial9 has its own A2A endpoint at https://spatial9.ai/a2a if you prefer agent-to-agent tasks.
A nightly Cloud Run job
batch_job.py reads a BigQuery queue, renders with 3 workers and writes files to Cloud Storage. cloud_run_job.sh performs the setup.
- Store the key once with
gcloud secrets create spatial9-api-keyand grantroles/secretmanager.secretAccessorto the runner service account. - Deploy the job with
gcloud run jobs deploy spatial9-catalog, passing the key with--set-secrets SPATIAL9_KEY=spatial9-api-key:latest, aBUCKETenvironment variable,--task-timeout 90mand--max-retries 1. - Create a Cloud Scheduler job
spatial9-nightlywith the schedule0 2 * * *inEtc/UTCthat calls the Cloud Run job's:runendpoint.
AWS and Amazon Bedrock
A Bedrock agent with Strands
strands_agent.py builds a Strands Agents agent with a BedrockModel and connects to Spatial9 with MCPClient over Streamable HTTP. The key is stored in AWS Secrets Manager as spatial9/api-key with the JSON value {"api_key": "s9_..."}. The example filters out publish_to_youtube so public publishing stays out of the bot.
A catalog job with Lambda and Step Functions
- Store the key:
aws secretsmanager create-secret --name spatial9/api-key --secret-string '{"api_key":"s9_..."}'. - Deploy
spatial9_lambda.pywithspatial9_mcp.pyandrequestsas a function namedspatial9. Give it read access to the secret and write access to the bucket. The function supports the actionsstart,statusandsave. - Create the state machine from
state_machine.jsonand start it with an input such as{"tracks": [{"track_id": "0042", "title": "Northern Lights", "source_url": "https://..."}]}.
The state machine is a Map state with MaxConcurrency 3. Each item runs Start, then a 45-second Wait, then Status, then a Choice: completed goes to Save (files to S3), failed or cancelled ends the item, and any other status loops back to Wait. The Start task retries States.TaskFailed up to 2 times at 30-second intervals.
AgentCore and Bedrock Agents
AgentCore Runtime can host the Strands agent. AgentCore Gateway can front Spatial9 as an MCP target so many agents share one governed connection, with AgentCore Identity holding the key. Classic Bedrock Agents can call the same Lambda as an action group. Service names and console steps change often; check the current AWS documentation before you deploy.
Governance controls
Every recipe implements six controls that security reviewers typically ask about.
| Control | Implementation |
|---|---|
| Secrets, not strings | The s9_ key lives in the platform vault and is rotated at /connect; never in notebooks, prompts or logs |
| Egress allowlist | Outbound traffic goes only to spatial9.ai, plus the file host returned by get_downloads if you copy files |
| Idempotency | One key per track and version; a retried job returns the same render |
| Limits | 3 renders at once and 20 per day; the queue waits instead of failing |
| Audit trail | job_id, status, stage, file paths, errors and timestamps written back next to rights data |
| Rights and least privilege | Render only music you own or license; give each agent only the tools it needs; keep public publishing behind human approval |
Failed renders are refunded automatically. See Limits and quotas.
Other orchestrators
The shared Python client works in any runtime that can make HTTPS calls, including Prefect, Dagster, Apache Airflow and GitHub Actions. Enterprise agent platforms such as Salesforce Agentforce, ServiceNow and IBM watsonx Orchestrate can connect to https://spatial9.ai/mcp as a remote MCP server where they support one, using a service key. These features are new and often in preview, so check each vendor's current documentation. For no-code tools, see Workflow automation.
Common mistakes
- One long process that waits for every render: it times out and loses state. Use the queue pattern.
- A new idempotency key on every retry: that starts a second render. Build the key from the track ID and version only.
- Starting many renders at once: only 3 run at a time. Count running rows first.
- Building download links by hand: links come only from
get_downloads. - Logging full download URLs or the key: log the job ID and file name instead.
- Skipping the listening check: have a person or a QC step listen to the binaural WAV before release.
Next steps
- Databricks Solutions Accelerator: the deployable bundle with three routes and gold views.
- MCP server: the full tool reference used by every recipe.
- A2A agent: delegate whole tasks and receive push notifications.
- Open-source real-time pipeline: the same pattern on self-hosted open-source infrastructure.