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.

PlatformPatternFolder
DatabricksUnity Catalog connection, Lakeflow Job, Volumes, optional AI Gateway MCP Servicesdatabricks
Databricks Solutions AcceleratorAsset Bundle with Auto Loader, MCP, A2A and REST API routes, audit table, gold viewsdatabricks/solution-accelerator
SnowflakeExternal access integration, Python procedure, Task, Cortex Agent custom toolsnowflake
Google Cloud and Vertex AIADK agent with McpToolset, Agent Engine, Cloud Run job and Scheduler, BigQueryvertex-ai
AWS and Amazon BedrockStrands agent, Lambda, Step Functions, S3, AgentCore Gatewaybedrock
Any Python runtimeMCP handshake with requests onlyshared/spatial9_mcp.py
Any Python runtimeA2A tasks: send, follow, cancel, webhooksshared/spatial9_a2a.py

Shared building blocks

Tools you call

ToolPurpose
create_upload_linkPrivate upload link; returns upload_id and direct_upload_url
create_spatial_audioStarts an immersive render from source_url or upload_id
create_from_stemsStarts a render from stems
split_stemsSeparates a song into stems
preview_chorusFree 30-second headphone preview
get_job_statusReturns status, stage and progress
get_downloadsReturns 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.

  1. Send initialize with protocol version 2025-06-18. The server may answer with an Mcp-Session-Id header.
  2. Send the notifications/initialized notification.
  3. Send one tools/call per Spatial9 tool, echoing Mcp-Session-Id and MCP-Protocol-Version on 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.

MethodPurpose
message/sendStarts a task; returns it immediately in the submitted state
tasks/getPoll every 30 to 60 seconds until a final state
tasks/cancelStops a task; refunded if paid processing had not started
tasks/pushNotificationConfig/setRegisters 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.

  1. Follow rows marked running: call get_job_status, and on completion call get_downloads, save the files and mark the row completed. Mark failed or cancelled jobs as failed with the reason.
  2. 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.

  1. Store the key: databricks secrets create-scope spatial9, then databricks secrets put-secret spatial9 api_key.
  2. Run setup.sql once as a metastore or catalog admin. It creates the governed HTTP connection, the media.spatial.renders_queue table and the masters and immersive Volumes.
  3. Copy spatial9_mcp.py and spatial9_catalog_job.py into one Workspace folder.
  4. Create a Lakeflow Job with a Python task pointing at the job file, scheduled every 15 minutes.
  5. 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.

  1. Run setup.sql, replacing s9_replace_me with your key. It creates a network rule, a secret, an external access integration, the RENDERS_QUEUE table and an encrypted IMMERSIVE stage.
  2. Run spatial9_procedure.sql. It creates the SPATIAL9_RUN Python procedure and the SPATIAL9_EVERY_15_MIN Task.
  3. Insert rows into RENDERS_QUEUE with a presigned SOURCE_URL for each master.
  4. 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.

  1. Store the key once with gcloud secrets create spatial9-api-key and grant roles/secretmanager.secretAccessor to the runner service account.
  2. Deploy the job with gcloud run jobs deploy spatial9-catalog, passing the key with --set-secrets SPATIAL9_KEY=spatial9-api-key:latest, a BUCKET environment variable, --task-timeout 90m and --max-retries 1.
  3. Create a Cloud Scheduler job spatial9-nightly with the schedule 0 2 * * * in Etc/UTC that calls the Cloud Run job's :run endpoint.

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

  1. Store the key: aws secretsmanager create-secret --name spatial9/api-key --secret-string '{"api_key":"s9_..."}'.
  2. Deploy spatial9_lambda.py with spatial9_mcp.py and requests as a function named spatial9. Give it read access to the secret and write access to the bucket. The function supports the actions start, status and save.
  3. Create the state machine from state_machine.json and 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.

ControlImplementation
Secrets, not stringsThe s9_ key lives in the platform vault and is rotated at /connect; never in notebooks, prompts or logs
Egress allowlistOutbound traffic goes only to spatial9.ai, plus the file host returned by get_downloads if you copy files
IdempotencyOne key per track and version; a retried job returns the same render
Limits3 renders at once and 20 per day; the queue waits instead of failing
Audit trailjob_id, status, stage, file paths, errors and timestamps written back next to rights data
Rights and least privilegeRender 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

Go deeper on the blog