Overview
A client-mode connector for the Databricks REST API that writes MQTT data into the lakehouse and kicks off downstream pipelines:BASE_ADDRESS is your workspace URL, with no trailing slash. Replace YOUR_WORKSPACE.cloud.databricks.com with yours. Topics use data/databricks/<object>/<verb>, and replies go to <topic>/response.
See Get identifiers for workspace objects. One connector instance talks to one workspace.
Auth
Every call sendsAuthorization: Bearer …. The template reads the token from the MQTT payload field access_token. Keep it in a Hub secret such as GET SECRET "DATABRICKS_TOKEN" and never commit it.
- Personal access token (PAT): a
dapi…token from Settings → Developer → Access tokens. Use a dedicated service principal’s token in production, not a person’s. See Databricks personal access tokens. - OAuth M2M (service principal): mint an access token out of band with
POST https://<workspace>/oidc/v1/token,grant_type=client_credentials,scope=all-apis, and the service principal’s client id and secret. Tokens last about 1 hour, so the publisher must refresh them. See OAuth M2M. On Azure, a Microsoft Entra ID token for the Azure Databricks resource also works. - Permissions:
CAN USEon the SQL warehouse, table privileges (USE CATALOG,USE SCHEMA,SELECT,MODIFY) for SQL,CAN MANAGE RUNon the job, andREAD VOLUME/WRITE VOLUMEon the Unity Catalog volume.
401 response with Credential was not sent or was of an unsupported type for this API. means the token is missing or malformed. 403 with Invalid access token means it was rejected or has expired.
Basic Syntax
JSON request bodies come from
{value.json.body}, not {payload}. The MQTT payload carries control fields (access_token, statement_id, chunk_index, run_id, file_path, directory_path, overwrite) next to a body object, and only body is sent to Databricks. UploadFile sends {value.json.content} as the raw file body with Content-Type: application/octet-stream.Example MQTT payloads
Find a warehouse (topicdata/databricks/warehouses/list):
id of a RUNNING (or serverless, auto-starting) warehouse as warehouse_id.
Insert telemetry with SQL (topic data/databricks/statement/execute). Use named parameter markers instead of string-building SQL:
status.state: SUCCEEDED with num_affected_rows / num_inserted_rows in result.data_array. One row per call is fine for low rates. For high-rate telemetry, batch rows into one multi-row INSERT, or land files with UploadFile and load them with COPY INTO or Auto Loader in a job.
Query (same topic):
result.data_array as arrays of strings (null for NULL), with column names in manifest.schema.columns.
Poll, page, or cancel (topics data/databricks/statement/get, …/statement/chunk, …/statement/cancel):
GetStatement only needs statement_id, GetStatementResultChunk also needs chunk_index, and CancelStatement needs "body": {}.
Trigger a pipeline when a batch completes (topic data/databricks/job/run-now):
{"run_id": 455644833, "number_in_job": 455644833}. Poll data/databricks/run/get with { "access_token": "…", "run_id": 455644833 }.
Land a batch file in a volume (topic data/databricks/file/upload):
204 with an empty body. Check it with data/databricks/file/metadata (HEAD, { "access_token": "…", "file_path": "…" }), or list the folder with data/databricks/directory/list (directory_path).
Gotchas
warehouse_idis required for every statement. SQL runs on a SQL warehouse (serverless or pro recommended), not on an all-purpose cluster. A stopped classic warehouse starts on the first statement, which can take minutes, so expectPENDING.wait_timeoutis"0s"(asynchronous: returnsPENDINGand astatement_idstraight away) or"5s"to"50s"(synchronous up to that limit; default10s). Any other value returns400. If the limit passes,on_wait_timeout: CONTINUE(default) keeps it running for you to poll, andCANCELstops it. The connector’sREQUEST_TIMEOUTis 60 s, which is above the 50 s maximum.- Check
status.state, not just the HTTP code. SQL errors such as syntax errors or missing tables come back asHTTP 200withstatus.state: FAILEDandstatus.error.message. Statements stay readable for at least 12 hours after they finish, thenGetStatementreturns404. INLINEvsEXTERNAL_LINKS:INLINEsupportsJSON_ARRAYonly, up to 25 MiB per result; larger results are aborted.EXTERNAL_LINKS(up to 100 GiB;JSON_ARRAY,CSVorARROW_STREAM) returns short-lived presigned / SAS / signed URLs (≤ 15 minutes) that must be fetched without theAuthorizationheader. That second fetch is outside this connector.- Parameters use named markers (
:name); positional?is not supported. Values are strings, with an optionaltype. idempotency_token(max 64 chars) onrun-now: if a run with that token exists, Databricks returns the samerun_idinstead of starting a second run. Derive it from the batch id so MQTT redelivery doesn’t trigger duplicate pipelines.- Files API body is raw bytes. Databricks stores exactly what it receives.
{value.json.content}suits text (CSV, JSON lines); it does not decode base64, so binary formats such as Parquet need a different path. Max 5 GiB perPUT.overwritedefaults totrueon the Databricks side when omitted. - Files API is Public Preview and works on Unity Catalog volumes (
/Volumes/…), workspace files, and DBFS paths. The legacy DBFS API (/api/2.0/dbfs/put, base64 in 1 MB blocks) is not templated. - Deletes (files, directories, jobs, warehouses), job create / reset / update, and
runs/submitare left out on purpose.
Rate limits
Databricks applies per-workspace rate limits per endpoint and returns429 with Retry-After when you exceed them. Statement Execution and jobs/run-now are meant for moderate rates (batches, not one call per sensor sample). Back off on 429 and 503 TEMPORARILY_UNAVAILABLE. See Resource limits.
Testing
Status: blocked on credentials. The factory box has no Databricks workspace or token, so the connector has not been smoke-tested against Databricks. There is no credible local Databricks emulator, so no wire-format emulator smoke was run either. Unauthenticated probes of real Databricks hosts (community.cloud.databricks.com, accounts.cloud.databricks.com) returned 401 Credential was not sent or was of an unsupported type for this API. for GET /api/2.0/preview/scim/v2/Me, GET /api/2.0/sql/warehouses and POST /api/2.0/sql/statements, with and without a placeholder token. That only shows the hosts enforce auth on the same paths and error shape the template uses. lot_lint passed with no findings.
To clear the gate, use a real workspace URL, a token, a SQL warehouse id, a lab job id and a lab volume path:
GET /api/2.0/preview/scim/v2/Mereturns200.POST /api/2.0/sql/statementswith{"warehouse_id":"…","statement":"SELECT 1","wait_timeout":"30s"}returns200withstatus.state: SUCCEEDED.PUT /api/2.0/fs/files/Volumes/<c>/<s>/<v>/coreflux-smoke.csv?overwrite=truereturns204, andHEADon the same path returns200.POST /api/2.2/jobs/run-nowwith the labjob_idreturns200with arun_id.

