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, EMR, Synapse, or Dataproc credentials.
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://digitaltap.ai/api/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://digitaltap.ai/api/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://digitaltap.ai/api/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://digitaltap.ai/api/auth/login \ -H "Content-Type: application/json" \ -d '{"email": "jane@acme.com", "password": "securepass123"}'
resp = requests.post(
"https://digitaltap.ai/api/auth/login",
json={"email": "jane@acme.com", "password": "securepass123"}
)
tokens = resp.json()["data"]
print(tokens["token"], tokens["api_key"])
const { data } = await fetch( "https://digitaltap.ai/api/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
| Parameter | Type | Required | Description |
|---|---|---|---|
| type | string | required | databricks | emr | synapse | dataproc |
| name | string | required | Display name |
| credentials | object | required | Platform-specific credentials |
| region | string | optional | Cloud region |
curl -X POST https://digitaltap.ai/api/platforms \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "type": "databricks", "name": "Production Databricks", "credentials": { "workspace_url": "https://dbc-abc123.cloud.databricks.com", "token": "dapi_xyz789..." }, "region": "us-east-1" }'
resp = requests.post(
"https://digitaltap.ai/api/platforms",
headers={"Authorization": f"Bearer {token}"},
json={
"type": "databricks",
"name": "Production Databricks",
"credentials": {
"workspace_url": "https://dbc-abc123.cloud.databricks.com",
"token": "dapi_xyz789..."
}
}
)
const resp = await fetch( "https://digitaltap.ai/api/platforms", { method: "POST", headers: { "Authorization": `Bearer ${token}`, "Content-Type": "application/json" }, body: JSON.stringify({ type: "databricks", name: "Production Databricks", credentials: { workspace_url: "https://dbc-abc123.cloud.databricks.com", token: "dapi_xyz789..." } }) } );
{
"success": true,
"data": {
"id": "plat_d4e5f6",
"type": "databricks",
"name": "Production Databricks",
"status": "connected",
"clusters_discovered": 7
}
}
{
"success": true,
"data": [
{ "id": "plat_d4e5f6", "type": "databricks", "name": "Production Databricks", "status": "connected" },
{ "id": "plat_g7h8i9", "type": "emr", "name": "AWS EMR Staging", "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://digitaltap.ai/api/clusters \
-H "X-API-Key: dtap_live_k8x9m2..."
resp = requests.get(
"https://digitaltap.ai/api/clusters",
headers={"X-API-Key": "dtap_live_k8x9m2..."}
)
const resp = await fetch( "https://digitaltap.ai/api/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
},
{
"platform": "emr",
"cluster_id": "j-2ABC3DEF",
"name": "Staging Analytics",
"state": "idle",
"idle_minutes": 47,
"hourly_cost": 8.20,
"nodes": 4
}
]
}
{
"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://digitaltap.ai/api/clusters/optimize \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"idle_threshold_minutes": 20, "dry_run": true}'
resp = requests.post(
"https://digitaltap.ai/api/clusters/optimize",
headers={"Authorization": f"Bearer {token}"},
json={"idle_threshold_minutes": 20, "dry_run": True}
)
const resp = await fetch( "https://digitaltap.ai/api/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 EMR 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://digitaltap.ai/api/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://digitaltap.ai/api/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://digitaltap.ai/api/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, EMR, etc.) |
500 | Internal Server Error | Unexpected error — includes request_id for support |
Always include the X-Request-ID header value when contacting support about errors.