API Documentation
Everything you need to integrate Digital Tap AI into your infrastructure. Manage clusters, automate optimization, and track savings programmatically.
🔐 Authentication
Digital Tap AI supports two authentication methods. Use whichever fits your workflow:
Bearer Token (JWT)
Returned from /v1/auth/login. Short-lived, ideal for user sessions and interactive use.
Authorization: Bearer eyJhbG...
API Key
Also returned from /v1/auth/login. Long-lived, ideal for CI/CD pipelines and server-to-server integrations.
X-API-Key: dtap_live_abc123...
⚠️ Keep Your Keys Safe
Never commit API keys to version control. Use environment variables or a secrets manager.
All API responses follow a consistent format:
{
"success": true,
"data": { ... },
"meta": { "request_id": "req_abc123", "timestamp": "2026-03-03T21:00:00Z" }
}
Errors return:
{
"success": false,
"error": { "code": "UNAUTHORIZED", "message": "Invalid or expired token" }
}
🚀 Quick Start
Create an Account
Register via the API or at digitaltap.ai/auth
Get Your API Key
Log in to receive your JWT token and long-lived API key.
Connect a Platform
Add your Databricks credentials. Digital Tap connects to Databricks today; Amazon EMR, Azure Synapse, Google Dataproc and Kubernetes will follow as each meets our integration standard.
Start Optimizing
Run analysis, set up schedules, and watch your savings grow.
Auth Endpoints
Register a new user account and organization.
| Parameter | Type | Required | Description |
|---|---|---|---|
| name | string | required | Full name |
| string | required | Email address | |
| password | string | required | Min 8 characters |
| company | string | optional | Company name |
| org_name | string | optional | Organization name |
curl -X POST https://api.digitaltap.ai/v1/auth/register \ -H "Content-Type: application/json" \ -d '{ "name": "Jane Smith", "email": "jane@acme.com", "password": "securepass123", "company": "Acme Corp", "org_name": "acme-engineering" }'
import requests resp = requests.post( "https://api.digitaltap.ai/v1/auth/register", json={ "name": "Jane Smith", "email": "jane@acme.com", "password": "securepass123", "company": "Acme Corp", "org_name": "acme-engineering" } ) print(resp.json())
const resp = await fetch( "https://api.digitaltap.ai/v1/auth/register", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name: "Jane Smith", email: "jane@acme.com", password: "securepass123", company: "Acme Corp", org_name: "acme-engineering" }) } ); const data = await resp.json(); console.log(data);
{
"success": true,
"data": {
"id": "usr_a1b2c3",
"name": "Jane Smith",
"email": "jane@acme.com",
"org": { "slug": "acme-engineering", "role": "owner" }
}
}
| Parameter | Type | Required | Description |
|---|---|---|---|
| string | required | Email address | |
| password | string | required | Password |
curl -X POST https://api.digitaltap.ai/v1/auth/login \ -H "Content-Type: application/json" \ -d '{"email": "jane@acme.com", "password": "securepass123"}'
resp = requests.post(
"https://api.digitaltap.ai/v1/auth/login",
json={"email": "jane@acme.com", "password": "securepass123"}
)
tokens = resp.json()["data"]
print(tokens["token"], tokens["api_key"])
const { data } = await fetch( "https://api.digitaltap.ai/v1/auth/login", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ email: "jane@acme.com", password: "securepass123" }) } ).then(r => r.json()); console.log(data.token, data.api_key);
{
"success": true,
"data": {
"token": "eyJhbGciOiJIUzI1NiIs...",
"api_key": "dtap_live_k8x9m2...",
"expires_in": 3600
}
}
| Parameter | Type | Required | Description |
|---|---|---|---|
| string | required | Account email |
{
"success": true,
"data": { "message": "Password reset email sent" }
}
Account Endpoints
Returns the authenticated user's account information.
{
"success": true,
"data": {
"id": "usr_a1b2c3",
"name": "Jane Smith",
"email": "jane@acme.com",
"plan": "growth",
"clusters_managed": 12,
"created_at": "2026-01-15T10:30:00Z"
}
}
{
"success": true,
"data": {
"period": "2026-03",
"api_calls": 14523,
"clusters_optimized": 8,
"hibernations_triggered": 342
}
}
{
"success": true,
"data": {
"plan": "growth",
"monthly_cost": 499,
"invoices": [
{ "date": "2026-02-01", "amount": 499, "status": "paid" }
]
}
}
{
"success": true,
"data": {
"total_saved_usd": 28450.00,
"water_saved_gallons": 125000,
"idle_hours_eliminated": 4200,
"co2_reduced_kg": 1850
}
}
{
"success": true,
"data": [
{
"id": "alt_x1",
"type": "idle_cluster",
"message": "Cluster prod-etl idle for 45 minutes",
"severity": "warning",
"created_at": "2026-03-03T14:22:00Z"
}
]
}
Platform Endpoints
The same call the dashboard's connect form makes. Digital Tap signs in as a Databricks service principal with OAuth machine-to-machine credentials; a personal access token is not accepted. It exchanges the client ID and secret for a short-lived token and lists clusters before storing anything, and answers 422 with Databricks' reason, storing nothing, if either fails. See the Databricks setup guide for the grants.
| Parameter | Type | Required | Description |
|---|---|---|---|
| platform | string | required | databricks — the only platform that can be connected today |
| credential_type | string | required | sp_oauth |
| credentials.host | string | required | Workspace URL |
| credentials.client_id | string | required | The service principal's client ID (its application ID) |
| credentials.client_secret | string | required | An OAuth secret generated for the service principal |
| config.warehouse_id | string | optional | The SQL warehouse system tables are read through |
curl -X POST https://api.digitaltap.ai/api/user/platforms \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "platform": "databricks", "credential_type": "sp_oauth", "credentials": { "host": "https://dbc-abc123.cloud.databricks.com", "client_id": "5d4c3b2a-1f0e-4d9c-8b7a-695847362514", "client_secret": "$DATABRICKS_CLIENT_SECRET" }, "config": {"warehouse_id": "1234567890abcdef"} }'
resp = requests.post(
"https://api.digitaltap.ai/api/user/platforms",
headers={"Authorization": f"Bearer {token}"},
json={
"platform": "databricks",
"credential_type": "sp_oauth",
"credentials": {
"host": "https://dbc-abc123.cloud.databricks.com",
"client_id": "5d4c3b2a-1f0e-4d9c-8b7a-695847362514",
"client_secret": os.environ["DATABRICKS_CLIENT_SECRET"],
},
"config": {"warehouse_id": "1234567890abcdef"},
},
)
const resp = await fetch( "https://api.digitaltap.ai/api/user/platforms", { method: "POST", headers: { "Authorization": `Bearer ${token}`, "Content-Type": "application/json" }, body: JSON.stringify({ platform: "databricks", credential_type: "sp_oauth", credentials: { host: "https://dbc-abc123.cloud.databricks.com", client_id: "5d4c3b2a-1f0e-4d9c-8b7a-695847362514", client_secret: process.env.DATABRICKS_CLIENT_SECRET }, config: { warehouse_id: "1234567890abcdef" } }) } );
{
"success": true,
"connection_id": "6f1c2d3e-4b5a-4c6d-8e7f-901234567890",
"clusters_found": 7,
"warehouses_found": 1,
"scan_failed": false,
"workspace_id": "1234567890123456",
"capabilities": {
"clusters_list": {"readable": true, "reason": "7 cluster(s) visible to the principal"}
},
"warnings": []
}
capabilities also reports each system schema as readable, not readable or not checked, whether the principal can restart clusters, and job_visibility.
{
"success": true,
"data": [
{ "id": "plat_d4e5f6", "type": "databricks", "name": "Production Databricks", "status": "connected" }
]
}
| Parameter | Type | Required | Description |
|---|---|---|---|
| name | string | optional | New display name |
| credentials | object | optional | Updated credentials |
{ "success": true, "data": { "id": "plat_d4e5f6", "name": "Prod DB Updated", "status": "connected" } }{ "success": true, "data": { "message": "Platform removed" } }{
"success": true,
"data": { "reachable": true, "latency_ms": 142, "permissions": ["read", "write", "admin"] }
}
Cluster Endpoints
Returns clusters across all connected platforms.
curl https://api.digitaltap.ai/v1/clusters \
-H "X-API-Key: dtap_live_k8x9m2..."
resp = requests.get(
"https://api.digitaltap.ai/v1/clusters",
headers={"X-API-Key": "dtap_live_k8x9m2..."}
)
const resp = await fetch( "https://api.digitaltap.ai/v1/clusters", { headers: { "X-API-Key": "dtap_live_k8x9m2..." } } );
{
"success": true,
"data": [
{
"platform": "databricks",
"cluster_id": "0301-prod-etl",
"name": "Production ETL",
"state": "running",
"idle_minutes": 0,
"hourly_cost": 12.50,
"nodes": 8
}
]
}
{
"success": true,
"data": {
"platform": "databricks",
"cluster_id": "0301-prod-etl",
"name": "Production ETL",
"state": "running",
"instance_type": "i3.xlarge",
"nodes": 8,
"uptime_hours": 6.5,
"cost_today": 81.25,
"water_usage_gallons": 18.4
}
}
Hibernates a cluster with state preservation for fast resume.
{
"success": true,
"data": {
"cluster_id": "0301-prod-etl",
"previous_state": "idle",
"new_state": "hibernating",
"estimated_savings_per_hour": 12.50
}
}
{
"success": true,
"data": {
"cluster_id": "0301-prod-etl",
"new_state": "resuming",
"estimated_ready_seconds": 45
}
}
Analyzes all connected clusters and returns optimization opportunities.
{
"success": true,
"data": {
"total_clusters": 12,
"idle_clusters": 4,
"potential_savings_monthly": 3420.00,
"recommendations": 7
}
}
| Parameter | Type | Required | Description |
|---|---|---|---|
| idle_threshold_minutes | integer | optional | Minutes idle before action (default: 30) |
| dry_run | boolean | optional | Preview only, no changes (default: false) |
curl -X POST https://api.digitaltap.ai/v1/clusters/optimize \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"idle_threshold_minutes": 20, "dry_run": true}'
resp = requests.post(
"https://api.digitaltap.ai/v1/clusters/optimize",
headers={"Authorization": f"Bearer {token}"},
json={"idle_threshold_minutes": 20, "dry_run": True}
)
const resp = await fetch( "https://api.digitaltap.ai/v1/clusters/optimize", { method: "POST", headers: { "Authorization": `Bearer ${token}`, "Content-Type": "application/json" }, body: JSON.stringify({ idle_threshold_minutes: 20, dry_run: true }) } );
{
"success": true,
"data": {
"dry_run": true,
"actions": [
{ "cluster": "j-2ABC3DEF", "action": "hibernate", "reason": "Idle 47 min", "savings_per_hour": 8.20 }
],
"total_hourly_savings": 8.20
}
}
Scheduling Endpoints
| Parameter | Type | Required | Description |
|---|---|---|---|
| name | string | required | Schedule name |
| type | string | required | time | idle | cost |
| cluster_ids | string[] | required | Target clusters |
| action | string | required | hibernate | resume | resize |
| cron | string | optional | Cron expression (for time type) |
| threshold | object | optional | Threshold config (for idle/cost type) |
{
"success": true,
"data": {
"id": "sch_m1n2",
"name": "Night Hibernate",
"type": "time",
"cron": "0 22 * * 1-5",
"action": "hibernate",
"enabled": true
}
}
{ "success": true, "data": [{ "id": "sch_m1n2", "name": "Night Hibernate", "type": "time", "enabled": true }] }{ "success": true, "data": { "id": "sch_m1n2", "updated": true } }{ "success": true, "data": { "message": "Schedule deleted" } }{
"success": true,
"data": { "execution_id": "exec_p3q4", "status": "running" }
}
Warm Pool Endpoints
| Parameter | Type | Required | Description |
|---|---|---|---|
| platform_id | string | required | Platform to pool |
| min_nodes | integer | required | Minimum warm nodes |
| max_nodes | integer | required | Maximum warm nodes |
| instance_type | string | required | Instance type |
{
"success": true,
"data": { "id": "wp_r5s6", "min_nodes": 2, "max_nodes": 10, "status": "provisioning" }
}
{ "success": true, "data": [{ "id": "wp_r5s6", "min_nodes": 2, "max_nodes": 10, "status": "ready" }] }{
"success": true,
"data": {
"total_warm_nodes": 6,
"available": 4,
"in_use": 2,
"avg_claim_time_seconds": 12
}
}
Insight Endpoints
{
"success": true,
"data": {
"horizon": "24h",
"predictions": [
{ "cluster_id": "0301-prod-etl", "predicted_usage": "high", "confidence": 0.92, "peak_hour": 14 }
]
}
}
{
"success": true,
"data": [
{ "type": "downsize", "detail": "Reduce from 8 to 5 nodes during off-peak", "savings_monthly": 540.00 },
{ "type": "spot_mix", "detail": "Use 60% spot instances for worker nodes", "savings_monthly": 890.00 }
]
}
{
"success": true,
"data": {
"total_waste_monthly": 4200.00,
"optimization_score": 72,
"top_opportunity": "Hibernate 3 idle Databricks clusters to save $1,800/mo"
}
}
{
"success": true,
"data": [
{
"cluster_id": "0301-prod-etl",
"current_type": "i3.2xlarge",
"recommended_type": "i3.xlarge",
"avg_cpu_utilization": 0.23,
"savings_monthly": 620.00
}
]
}
Notification Endpoints
| Parameter | Type | Required | Description |
|---|---|---|---|
| type | string | required | slack | teams | pagerduty | email |
| config | object | required | Channel-specific config (webhook_url, email, etc.) |
| events | string[] | optional | Events to subscribe to |
{
"success": true,
"data": { "id": "nch_t7u8", "type": "slack", "status": "active" }
}
{ "success": true, "data": [{ "id": "nch_t7u8", "type": "slack", "status": "active" }] }| Parameter | Type | Required | Description |
|---|---|---|---|
| channel_id | string | required | Channel to test |
{ "success": true, "data": { "delivered": true } }Organization Endpoints
| Parameter | Type | Required | Description |
|---|---|---|---|
| name | string | required | Organization name |
| slug | string | optional | URL slug (auto-generated if omitted) |
{
"success": true,
"data": { "slug": "acme-engineering", "name": "Acme Engineering", "members": 1 }
}
{ "success": true, "data": [{ "slug": "acme-engineering", "name": "Acme Engineering", "role": "owner" }] }| Parameter | Type | Required | Description |
|---|---|---|---|
| string | required | Invitee email | |
| role | string | optional | admin | member | viewer (default: member) |
{ "success": true, "data": { "invite_id": "inv_v9w0", "email": "bob@acme.com", "status": "pending" } }{
"success": true,
"data": [
{ "id": "usr_a1b2c3", "name": "Jane Smith", "role": "owner" },
{ "id": "usr_d4e5f6", "name": "Bob Chen", "role": "member" }
]
}
Monitoring Endpoints
Returns current monitoring status including connected platforms, clusters monitored, and recent action count.
{
"enabled": true,
"platforms_connected": 3,
"clusters_monitored": 12,
"actions_last_24h": 7,
"config": {
"scan_interval_minutes": 10,
"idle_threshold_pct": 15.0,
"idle_threshold_minutes": 30,
"auto_hibernate": false,
"auto_rightsize": false,
"auto_spot_failover": false
}
}
Update monitoring preferences. All fields are optional — only provided fields are updated.
curl -X PUT https://api.digitaltap.ai/v1/monitoring/config \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"auto_hibernate": true,
"idle_threshold_minutes": 20,
"scan_interval_minutes": 5
}'
import requests
resp = requests.put(
"https://api.digitaltap.ai/v1/monitoring/config",
headers={"Authorization": "Bearer YOUR_TOKEN"},
json={
"auto_hibernate": True,
"idle_threshold_minutes": 20,
"scan_interval_minutes": 5,
},
)
print(resp.json())
const resp = await fetch("https://api.digitaltap.ai/v1/monitoring/config", {
method: "PUT",
headers: {
"Authorization": "Bearer YOUR_TOKEN",
"Content-Type": "application/json",
},
body: JSON.stringify({
auto_hibernate: true,
idle_threshold_minutes: 20,
scan_interval_minutes: 5,
}),
});
console.log(await resp.json());
| Field | Type | Description |
|---|---|---|
| scan_interval_minutes | int | How often to scan clusters (min: 1) |
| idle_threshold_pct | float | CPU % below which a cluster is considered idle |
| idle_threshold_minutes | int | Minutes idle before action is taken |
| auto_hibernate | bool | Automatically hibernate idle clusters |
| auto_rightsize | bool | Automatically apply right-sizing recommendations |
| auto_spot_failover | bool | Automatically failover to spot instances |
| exclude_clusters | string[] | Cluster IDs to exclude from automation |
Get history of automated optimization actions. Supports pagination and filtering by action type.
| Param | Type | Description |
|---|---|---|
| limit | int | Results per page (1-200, default 50) |
| offset | int | Pagination offset |
| action_type | string | Filter: hibernate, rightsize, spot_failover |
{
"actions": [
{
"id": "act_a1b2c3",
"platform_type": "databricks",
"cluster_id": "0123-456789-abc",
"action_type": "hibernate",
"status": "completed",
"reason": "Idle for 45 minutes (CPU < 5%)",
"savings_usd": 3.20,
"created_at": "2026-03-12T14:30:00Z"
}
],
"limit": 50,
"offset": 0
}
Get CPU, memory, worker count, and cost history for a specific cluster. Default: 24 hours, max: 168 hours.
| Param | Type | Description |
|---|---|---|
| hours | int | Lookback window (1-168, default 24) |
{
"cluster_id": "0123-456789-abc",
"hours": 24,
"data_points": 144,
"metrics": [
{
"timestamp": "2026-03-12T00:00:00Z",
"cpu_util": 12.5,
"memory_util": 34.2,
"workers": 4,
"idle_minutes": 0,
"cost_per_hour": 2.40,
"status": "running"
}
]
}
Emergency stop — pauses all automated monitoring and optimization for your account. No clusters will be touched until resumed.
{ "message": "Monitoring paused", "enabled": false }
Re-enable automated monitoring and optimization after a pause.
{ "message": "Monitoring resumed", "enabled": true }
⚡ Rate Limiting
API requests are rate-limited to ensure fair usage and platform stability.
| Tier | Limit | Window | Auth endpoints |
|---|---|---|---|
| Starter | 60 requests | Per minute | 10/min |
| Growth | 300 requests | Per minute | 30/min |
| Enterprise | 1,000 requests | Per minute | 100/min |
Rate limit headers are included in every response:
X-RateLimit-Limit: 300 X-RateLimit-Remaining: 287 X-RateLimit-Reset: 1710273600 X-Request-ID: a1b2c3d4
When rate-limited, you'll receive a 429 Too Many Requests response. Back off and retry after the reset time.
🚨 Error Codes
All errors follow a consistent format:
{
"error": "Human-readable error message",
"request_id": "a1b2c3d4"
}
| Code | Meaning | Common Causes |
|---|---|---|
400 | Bad Request | Invalid JSON, missing required fields, validation error |
401 | Unauthorized | Missing/invalid token, expired JWT, bad API key |
403 | Forbidden | Insufficient role/permissions for this action |
404 | Not Found | Resource doesn't exist or belongs to another account |
409 | Conflict | Email already registered, slug already taken |
429 | Too Many Requests | Rate limit exceeded — see headers for reset time |
502 | Bad Gateway | Upstream platform API error (Databricks) |
500 | Internal Server Error | Unexpected error — includes request_id for support |
Always include the X-Request-ID header value when contacting support about errors.