Skip to main content

API Reference


Module: Tuner Component: REST API Version: 1.0.0-RELEASE Last Updated: October 26, 2025 Document Type: API Documentation (Developer Reference)


Table of Contents

  1. API Overview
  2. Authentication
  3. Recommendation APIs
  4. Account Management APIs
  5. Scheduler APIs
  6. Error Handling
  7. Rate Limiting

API Overview

Base URL

Production: https://api.cloudkeeper.com/tuner/v1
Staging: https://api-staging.cloudkeeper.com/tuner/v1

Common Headers

Authorization: Bearer <JWT_TOKEN>
Content-Type: application/json
Accept: application/json

Response Format

All API responses follow this standard format:

{
"success": true,
"data": { ... },
"error": null,
"timestamp": "2025-10-26T12:00:00Z"
}

Error Response:

{
"success": false,
"data": null,
"error": {
"code": "INVALID_REQUEST",
"message": "Account ID is required",
"details": { "field": "accountId" }
},
"timestamp": "2025-10-26T12:00:00Z"
}

Authentication

JWT Token

Include JWT token in Authorization header:

GET /api/recommendations?accountId=123456789012
Host: api.cloudkeeper.com
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...

Obtaining Token

POST /api/auth/login
Content-Type: application/json

{
"email": "user@company.com",
"password": "********"
}

Response:

{
"success": true,
"data": {
"accessToken": "eyJhbGciOiJSUzI1NiIs...",
"refreshToken": "eyJhbGciOiJSUzI1NiIs...",
"expiresIn": 86400,
"tokenType": "Bearer"
}
}

Recommendation APIs

GET /api/recommendations

Get all recommendations for an account.

Parameters:

NameTypeRequiredDescription
accountIdstringYesAWS Account ID (12 digits)
regionstringNoFilter by AWS region
categorystringNoFilter by category: Cleaner, OverProvisioned, Modernization, PotentialBenefits
statusstringNoFilter by status: GENERATED, SNOOZED, IMPLEMENTED, DISMISSED
minSavingsnumberNoMinimum monthly savings ($)
pagenumberNoPage number (default: 1)
limitnumberNoItems per page (default: 50, max: 200)

Example Request:

GET /api/recommendations?accountId=123456789012&category=Cleaner&minSavings=100
Authorization: Bearer <token>

Response:

{
"success": true,
"data": {
"recommendations": [
{
"id": "rec_abcd1234",
"accountId": "123456789012",
"resourceId": "snap-0abcd1234",
"resourceName": "backup-2024-01-15",
"service": "EC2",
"category": "Cleaner",
"description": "EBS snapshot is 180 days old and the volume has been deleted",
"action": "Delete EBS snapshot snap-0abcd1234",
"region": "us-east-1",
"currentCost": 15.36,
"recommendedCost": 0,
"monthlySavings": 15.36,
"annualSavings": 184.32,
"status": "GENERATED",
"riskLevel": "LOW",
"metadata": {
"size": 100,
"created": "2024-01-15T10:30:00Z",
"volumeId": "vol-deleted"
},
"createdAt": "2025-10-26T10:00:00Z",
"updatedAt": "2025-10-26T10:00:00Z"
}
],
"pagination": {
"currentPage": 1,
"totalPages": 5,
"totalItems": 234,
"itemsPerPage": 50
},
"summary": {
"totalRecommendations": 234,
"totalMonthlySavings": 48645.00,
"totalAnnualSavings": 583740.00,
"byCategory": {
"Cleaner": { "count": 102, "savings": 48645.00 },
"OverProvisioned": { "count": 87, "savings": 28087.00 },
"Modernization": { "count": 32, "savings": 21687.00 },
"PotentialBenefits": { "count": 13, "savings": 45280.00 }
}
}
}
}

GET /api/recommendations/{id}

Get a single recommendation by ID.

Parameters:

NameTypeRequiredDescription
idstringYesRecommendation ID

Example Request:

GET /api/recommendations/rec_abcd1234
Authorization: Bearer <token>

Response: (Same structure as single recommendation above)


POST /api/recommendations/{id}/implement

Mark recommendation as implemented.

Parameters:

NameTypeRequiredDescription
idstringYesRecommendation ID

Request Body:

{
"implementedBy": "john.doe@company.com",
"implementedAt": "2025-10-26T14:30:00Z",
"notes": "Deleted snapshot via AWS Console"
}

Response:

{
"success": true,
"data": {
"id": "rec_abcd1234",
"status": "IMPLEMENTED",
"implementedBy": "john.doe@company.com",
"implementedAt": "2025-10-26T14:30:00Z"
}
}

POST /api/recommendations/{id}/snooze

Snooze recommendation for a specified duration.

Request Body:

{
"snoozeDuration": 30,
"reason": "Planned for next quarter"
}

Response:

{
"success": true,
"data": {
"id": "rec_abcd1234",
"status": "SNOOZED",
"snoozedUntil": "2025-11-25T12:00:00Z"
}
}

POST /api/recommendations/{id}/dismiss

Permanently dismiss recommendation.

Request Body:

{
"reason": "Resource required for backup purposes"
}

Response:

{
"success": true,
"data": {
"id": "rec_abcd1234",
"status": "DISMISSED"
}
}

POST /api/recommendations/refresh

Trigger recommendation regeneration for an account/region.

Request Body:

{
"accountId": "123456789012",
"region": "us-east-1",
"factorKeys": ["OVER_PROVISIONED_EC2", "SNAPSHOT_CLEANUP"]
}

Response:

{
"success": true,
"data": {
"jobId": "job_xyz789",
"status": "QUEUED",
"estimatedCompletionTime": "2025-10-26T15:00:00Z"
}
}

Account Management APIs

GET /api/accounts

Get all linked AWS accounts.

Response:

{
"success": true,
"data": {
"accounts": [
{
"accountId": "123456789012",
"accountName": "Production",
"roleArn": "arn:aws:iam::123456789012:role/CloudKeeperTunerRole",
"externalId": "uuid-12345-abcde",
"status": "ACTIVE",
"regions": ["us-east-1", "us-west-2", "eu-west-1"],
"lastSyncAt": "2025-10-26T10:00:00Z",
"linkedAt": "2025-01-15T09:00:00Z"
}
]
}
}

POST /api/accounts

Link a new AWS account.

Request Body:

{
"accountId": "987654321098",
"accountName": "Development",
"roleArn": "arn:aws:iam::987654321098:role/CloudKeeperTunerRole",
"externalId": "uuid-67890-fghij",
"regions": ["us-east-1"]
}

Response:

{
"success": true,
"data": {
"accountId": "987654321098",
"status": "VERIFYING",
"verificationJobId": "verify_abc123"
}
}

POST /api/accounts/{accountId}/verify

Verify AWS account access.

Response:

{
"success": true,
"data": {
"accountId": "123456789012",
"verified": true,
"permissions": {
"ec2:DescribeInstances": true,
"rds:DescribeDBInstances": true,
"cloudwatch:GetMetricStatistics": true
},
"missingPermissions": []
}
}

DELETE /api/accounts/{accountId}

Unlink AWS account.

Response:

{
"success": true,
"data": {
"accountId": "123456789012",
"status": "DELETED"
}
}

Scheduler APIs

GET /api/scheduler/schedules

Get all schedules.

Parameters:

NameTypeRequiredDescription
accountIdstringNoFilter by account
statusstringNoFilter by status: ACTIVE, PAUSED

Response:

{
"success": true,
"data": {
"schedules": [
{
"id": "sched_abc123",
"name": "Dev Environment Schedule",
"type": "TAG_BASED",
"accountId": "123456789012",
"region": "us-east-1",
"tagRules": {
"Environment": "Dev",
"Scheduler": "Enabled"
},
"schedule": {
"startTime": "07:00",
"stopTime": "19:00",
"timezone": "America/New_York",
"weekdays": [1, 2, 3, 4, 5]
},
"resourceTypes": ["EC2", "RDS"],
"status": "ACTIVE",
"lastExecutionAt": "2025-10-26T07:00:00Z",
"projectedMonthlySavings": 3452.00,
"createdAt": "2025-01-10T10:00:00Z"
}
]
}
}

POST /api/scheduler/schedules

Create a new schedule.

Request Body:

{
"name": "Staging Environment Schedule",
"type": "TAG_BASED",
"accountId": "123456789012",
"region": "us-east-1",
"tagRules": {
"Environment": "Staging"
},
"schedule": {
"startTime": "08:00",
"stopTime": "18:00",
"timezone": "America/New_York",
"weekdays": [1, 2, 3, 4, 5]
},
"resourceTypes": ["EC2", "RDS"]
}

Response:

{
"success": true,
"data": {
"id": "sched_xyz789",
"status": "ACTIVE",
"matchedResources": 23,
"projectedMonthlySavings": 1840.00
}
}

PUT /api/scheduler/schedules/{id}

Update existing schedule.

Request Body: (Same as POST)


DELETE /api/scheduler/schedules/{id}

Delete schedule.

Response:

{
"success": true,
"data": {
"id": "sched_xyz789",
"status": "DELETED"
}
}

POST /api/scheduler/schedules/{id}/pause

Pause schedule execution.

Response:

{
"success": true,
"data": {
"id": "sched_xyz789",
"status": "PAUSED"
}
}

POST /api/scheduler/schedules/{id}/resume

Resume schedule execution.

Response:

{
"success": true,
"data": {
"id": "sched_xyz789",
"status": "ACTIVE"
}
}

Error Handling

Error Codes

CodeHTTP StatusDescription
INVALID_REQUEST400Invalid request parameters
UNAUTHORIZED401Missing or invalid authentication token
FORBIDDEN403Insufficient permissions
NOT_FOUND404Resource not found
CONFLICT409Resource already exists or state conflict
RATE_LIMIT_EXCEEDED429Too many requests
INTERNAL_ERROR500Server error
SERVICE_UNAVAILABLE503Service temporarily unavailable

Error Response Structure

{
"success": false,
"data": null,
"error": {
"code": "INVALID_REQUEST",
"message": "Account ID must be 12 digits",
"details": {
"field": "accountId",
"value": "12345",
"expected": "12-digit string"
},
"requestId": "req_abc123def456",
"timestamp": "2025-10-26T12:00:00Z"
}
}

Rate Limiting

Limits

Endpoint TypeLimitWindow
Read Operations (GET)100 requestsper minute
Write Operations (POST/PUT/DELETE)30 requestsper minute
Auth Endpoints5 requestsper minute
Bulk Operations10 requestsper minute

Rate Limit Headers

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 87
X-RateLimit-Reset: 1698336060

Rate Limit Exceeded Response

HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1698336060
Retry-After: 45

{
"success": false,
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded. Please retry after 45 seconds."
}
}

API Versioning

APIs are versioned via URL path:

/api/v1/recommendations  (Current)
/api/v2/recommendations (Future)

Deprecation Policy:

  • 6 months notice before deprecation
  • Old version supported for 12 months after new version release

Next Steps