">

API Documentation

Everything you need to integrate Digital Tap AI into your infrastructure. Manage clusters, automate optimization, and track savings programmatically.

https://api.digitaltap.ai

🔐 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

POST /v1/auth/register Create account ▶

Register a new user account and organization.

ParameterTypeRequiredDescription
namestringrequiredFull name
emailstringrequiredEmail address
passwordstringrequiredMin 8 characters
companystringoptionalCompany name
org_namestringoptionalOrganization 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);
Response 201 Created
{
  "success": true,
  "data": {
    "id": "usr_a1b2c3",
    "name": "Jane Smith",
    "email": "jane@acme.com",
    "org": { "slug": "acme-engineering", "role": "owner" }
  }
}
POST /v1/auth/login Sign in → JWT + API key ▶
ParameterTypeRequiredDescription
emailstringrequiredEmail address
passwordstringrequiredPassword
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);
Response 200 OK
{
  "success": true,
  "data": {
    "token": "eyJhbGciOiJIUzI1NiIs...",
    "api_key": "dtap_live_k8x9m2...",
    "expires_in": 3600
  }
}
POST /v1/auth/reset-password Password reset ▶
ParameterTypeRequiredDescription
emailstringrequiredAccount email
Response 200 OK
{
  "success": true,
  "data": { "message": "Password reset email sent" }
}

Account Endpoints

GET /v1/account Account details ▶

Returns the authenticated user's account information.

Response 200 OK
{
  "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"
  }
}
GET /v1/account/usage Usage reports ▶
Response 200 OK
{
  "success": true,
  "data": {
    "period": "2026-03",
    "api_calls": 14523,
    "clusters_optimized": 8,
    "hibernations_triggered": 342
  }
}
GET /v1/account/billing Billing history ▶
Response 200 OK
{
  "success": true,
  "data": {
    "plan": "growth",
    "monthly_cost": 499,
    "invoices": [
      { "date": "2026-02-01", "amount": 499, "status": "paid" }
    ]
  }
}
GET /v1/account/savings Total savings summary ▶
Response 200 OK
{
  "success": true,
  "data": {
    "total_saved_usd": 28450.00,
    "water_saved_gallons": 125000,
    "idle_hours_eliminated": 4200,
    "co2_reduced_kg": 1850
  }
}
GET /v1/account/alerts Alert feed ▶
Response 200 OK
{
  "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

POST /api/user/platforms Connect Databricks with a service principal ▶

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.

ParameterTypeRequiredDescription
platformstringrequireddatabricks — the only platform that can be connected today
credential_typestringrequiredsp_oauth
credentials.hoststringrequiredWorkspace URL
credentials.client_idstringrequiredThe service principal's client ID (its application ID)
credentials.client_secretstringrequiredAn OAuth secret generated for the service principal
config.warehouse_idstringoptionalThe 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" }
    })
  }
);
Response 200 OK
{
  "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.

GET /v1/platforms List connected platforms ▶
Response 200 OK
{
  "success": true,
  "data": [
    { "id": "plat_d4e5f6", "type": "databricks", "name": "Production Databricks", "status": "connected" }
  ]
}
PUT /v1/platforms/{id} Update platform ▶
ParameterTypeRequiredDescription
namestringoptionalNew display name
credentialsobjectoptionalUpdated credentials
Response 200 OK
{ "success": true, "data": { "id": "plat_d4e5f6", "name": "Prod DB Updated", "status": "connected" } }
DELETE /v1/platforms/{id} Remove platform ▶
Response 200 OK
{ "success": true, "data": { "message": "Platform removed" } }
POST /v1/platforms/{id}/test Test connectivity ▶
Response 200 OK
{
  "success": true,
  "data": { "reachable": true, "latency_ms": 142, "permissions": ["read", "write", "admin"] }
}

Cluster Endpoints

GET /v1/clusters List all clusters ▶

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..." } }
);
Response 200 OK
{
  "success": true,
  "data": [
    {
      "platform": "databricks",
      "cluster_id": "0301-prod-etl",
      "name": "Production ETL",
      "state": "running",
      "idle_minutes": 0,
      "hourly_cost": 12.50,
      "nodes": 8
    }
  ]
}
GET /v1/clusters/{platform}/{cluster_id} Cluster details ▶
Response 200 OK
{
  "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
  }
}
POST /v1/clusters/{platform}/{cluster_id}/hibernate Hibernate cluster ▶

Hibernates a cluster with state preservation for fast resume.

Response 200 OK
{
  "success": true,
  "data": {
    "cluster_id": "0301-prod-etl",
    "previous_state": "idle",
    "new_state": "hibernating",
    "estimated_savings_per_hour": 12.50
  }
}
POST /v1/clusters/{platform}/{cluster_id}/resume Resume cluster ▶
Response 200 OK
{
  "success": true,
  "data": {
    "cluster_id": "0301-prod-etl",
    "new_state": "resuming",
    "estimated_ready_seconds": 45
  }
}
POST /v1/clusters/analyze Run optimization analysis ▶

Analyzes all connected clusters and returns optimization opportunities.

Response 200 OK
{
  "success": true,
  "data": {
    "total_clusters": 12,
    "idle_clusters": 4,
    "potential_savings_monthly": 3420.00,
    "recommendations": 7
  }
}
POST /v1/clusters/optimize Execute optimization ▶
ParameterTypeRequiredDescription
idle_threshold_minutesintegeroptionalMinutes idle before action (default: 30)
dry_runbooleanoptionalPreview 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 })
  }
);
Response 200 OK
{
  "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

POST /v1/schedules Create schedule rule ▶
ParameterTypeRequiredDescription
namestringrequiredSchedule name
typestringrequiredtime | idle | cost
cluster_idsstring[]requiredTarget clusters
actionstringrequiredhibernate | resume | resize
cronstringoptionalCron expression (for time type)
thresholdobjectoptionalThreshold config (for idle/cost type)
Response 201 Created
{
  "success": true,
  "data": {
    "id": "sch_m1n2",
    "name": "Night Hibernate",
    "type": "time",
    "cron": "0 22 * * 1-5",
    "action": "hibernate",
    "enabled": true
  }
}
GET /v1/schedules List schedules ▶
Response 200 OK
{ "success": true, "data": [{ "id": "sch_m1n2", "name": "Night Hibernate", "type": "time", "enabled": true }] }
PUT /v1/schedules/{id} Update schedule ▶
Response 200 OK
{ "success": true, "data": { "id": "sch_m1n2", "updated": true } }
DELETE /v1/schedules/{id} Delete schedule ▶
Response 200 OK
{ "success": true, "data": { "message": "Schedule deleted" } }
POST /v1/schedules/{id}/run Manual trigger ▶
Response 200 OK
{
  "success": true,
  "data": { "execution_id": "exec_p3q4", "status": "running" }
}

Warm Pool Endpoints

POST /v1/clusters/warm-pools Configure warm pool ▶
ParameterTypeRequiredDescription
platform_idstringrequiredPlatform to pool
min_nodesintegerrequiredMinimum warm nodes
max_nodesintegerrequiredMaximum warm nodes
instance_typestringrequiredInstance type
Response 201 Created
{
  "success": true,
  "data": { "id": "wp_r5s6", "min_nodes": 2, "max_nodes": 10, "status": "provisioning" }
}
GET /v1/clusters/warm-pools List configs ▶
Response 200 OK
{ "success": true, "data": [{ "id": "wp_r5s6", "min_nodes": 2, "max_nodes": 10, "status": "ready" }] }
GET /v1/clusters/warm-pools/status Pool status ▶
Response 200 OK
{
  "success": true,
  "data": {
    "total_warm_nodes": 6,
    "available": 4,
    "in_use": 2,
    "avg_claim_time_seconds": 12
  }
}

Insight Endpoints

GET /v1/clusters/predictions Predicted cluster needs (24h) ▶
Response 200 OK
{
  "success": true,
  "data": {
    "horizon": "24h",
    "predictions": [
      { "cluster_id": "0301-prod-etl", "predicted_usage": "high", "confidence": 0.92, "peak_hour": 14 }
    ]
  }
}
GET /v1/clusters/{platform}/{id}/recommendations Per-cluster recommendations ▶
Response 200 OK
{
  "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 }
  ]
}
GET /v1/insights Cross-platform insights ▶
Response 200 OK
{
  "success": true,
  "data": {
    "total_waste_monthly": 4200.00,
    "optimization_score": 72,
    "top_opportunity": "Hibernate 3 idle Databricks clusters to save $1,800/mo"
  }
}
GET /v1/insights/rightsizing Right-sizing recommendations ▶
Response 200 OK
{
  "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

POST /v1/notifications/channels Add notification channel ▶
ParameterTypeRequiredDescription
typestringrequiredslack | teams | pagerduty | email
configobjectrequiredChannel-specific config (webhook_url, email, etc.)
eventsstring[]optionalEvents to subscribe to
Response 201 Created
{
  "success": true,
  "data": { "id": "nch_t7u8", "type": "slack", "status": "active" }
}
GET /v1/notifications/channels List channels ▶
Response 200 OK
{ "success": true, "data": [{ "id": "nch_t7u8", "type": "slack", "status": "active" }] }
POST /v1/notifications/test Test notification ▶
ParameterTypeRequiredDescription
channel_idstringrequiredChannel to test
Response 200 OK
{ "success": true, "data": { "delivered": true } }

Organization Endpoints

POST /v1/orgs Create org ▶
ParameterTypeRequiredDescription
namestringrequiredOrganization name
slugstringoptionalURL slug (auto-generated if omitted)
Response 201 Created
{
  "success": true,
  "data": { "slug": "acme-engineering", "name": "Acme Engineering", "members": 1 }
}
GET /v1/orgs List user's orgs ▶
Response 200 OK
{ "success": true, "data": [{ "slug": "acme-engineering", "name": "Acme Engineering", "role": "owner" }] }
POST /v1/orgs/{slug}/invite Invite member ▶
ParameterTypeRequiredDescription
emailstringrequiredInvitee email
rolestringoptionaladmin | member | viewer (default: member)
Response 200 OK
{ "success": true, "data": { "invite_id": "inv_v9w0", "email": "bob@acme.com", "status": "pending" } }
GET /v1/orgs/{slug}/members List members ▶
Response 200 OK
{
  "success": true,
  "data": [
    { "id": "usr_a1b2c3", "name": "Jane Smith", "role": "owner" },
    { "id": "usr_d4e5f6", "name": "Bob Chen", "role": "member" }
  ]
}

Monitoring Endpoints

GET /v1/monitoring/status Get monitoring status ▶

Returns current monitoring status including connected platforms, clusters monitored, and recent action count.

Response 200 OK
{
  "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
  }
}
PUT /v1/monitoring/config Update monitoring config ▶

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());
FieldTypeDescription
scan_interval_minutesintHow often to scan clusters (min: 1)
idle_threshold_pctfloatCPU % below which a cluster is considered idle
idle_threshold_minutesintMinutes idle before action is taken
auto_hibernateboolAutomatically hibernate idle clusters
auto_rightsizeboolAutomatically apply right-sizing recommendations
auto_spot_failoverboolAutomatically failover to spot instances
exclude_clustersstring[]Cluster IDs to exclude from automation
GET /v1/monitoring/actions List optimization actions ▶

Get history of automated optimization actions. Supports pagination and filtering by action type.

ParamTypeDescription
limitintResults per page (1-200, default 50)
offsetintPagination offset
action_typestringFilter: hibernate, rightsize, spot_failover
Response 200 OK
{
  "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 /v1/monitoring/metrics/{cluster_id} Cluster utilization history ▶

Get CPU, memory, worker count, and cost history for a specific cluster. Default: 24 hours, max: 168 hours.

ParamTypeDescription
hoursintLookback window (1-168, default 24)
Response 200 OK
{
  "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"
    }
  ]
}
POST /v1/monitoring/pause Pause all monitoring ▶

Emergency stop — pauses all automated monitoring and optimization for your account. No clusters will be touched until resumed.

Response 200 OK
{ "message": "Monitoring paused", "enabled": false }
POST /v1/monitoring/resume Resume monitoring ▶

Re-enable automated monitoring and optimization after a pause.

Response 200 OK
{ "message": "Monitoring resumed", "enabled": true }

⚡ Rate Limiting

API requests are rate-limited to ensure fair usage and platform stability.

TierLimitWindowAuth endpoints
Starter60 requestsPer minute10/min
Growth300 requestsPer minute30/min
Enterprise1,000 requestsPer minute100/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"
}
CodeMeaningCommon Causes
400Bad RequestInvalid JSON, missing required fields, validation error
401UnauthorizedMissing/invalid token, expired JWT, bad API key
403ForbiddenInsufficient role/permissions for this action
404Not FoundResource doesn't exist or belongs to another account
409ConflictEmail already registered, slug already taken
429Too Many RequestsRate limit exceeded — see headers for reset time
502Bad GatewayUpstream platform API error (Databricks)
500Internal Server ErrorUnexpected error — includes request_id for support

Always include the X-Request-ID header value when contacting support about errors.

© 2026 Digital Tap AI. All rights reserved.

Home Terms Privacy Contact