openapi: 3.2.0 info: title: DoiT PerfectScale for Commitments AWS API description: Programmatic access to DoiT Platform version: v1 servers: - url: https://api.doit.com security: - api_key: [] - tenantId: [] api_key: [] tags: - name: PerfectScale for Commitments AWS description: PerfectScale for Commitments (AWS) — commitment inventory, recommendations, and planned purchases. paths: /ps4commitments/v1/aws/organizations: get: operationId: listAwsOrganizations tags: - PerfectScale for Commitments AWS summary: List AWS organizations description: 'Returns all AWS Organizations accessible to the authenticated tenant. Each item includes metadata, trailing 30-day aggregate statistics, precomputed YTD/lifetime savings totals per SP type so customer-level savings can be aggregated client-side by summing across organizations, and estimated monthly potential savings (`monthlyPotentialSavings`) per SP type from the latest projection.' parameters: - $ref: '#/components/parameters/tenantId' - $ref: '#/components/parameters/ps4cPageToken' - $ref: '#/components/parameters/ps4cMaxResults' responses: '200': description: List of AWS Organizations. headers: Request-Id: $ref: '#/components/headers/RequestId' Content-Language: $ref: '#/components/headers/ContentLanguage' content: application/json: schema: type: object required: - items properties: items: type: array items: $ref: '#/components/schemas/AwsOrganization' pageToken: type: - string - 'null' rowCount: type: - integer - 'null' format: int64 description: Best-effort count for the filtered result set. May be null or omitted for expensive counts. '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' '503': $ref: '#/components/responses/ServiceUnavailable' /ps4commitments/v1/aws/organizations/{managementAccountId}: get: operationId: getAwsOrganization tags: - PerfectScale for Commitments AWS summary: Get an AWS organization description: 'Returns a single AWS Organization including metadata, 30-day aggregates, and the trailing-window stats that drive the customer Overview screen: - `monthlyStats` — last 6 calendar months (ESR, on-demand cost, cost with savings). - `dailyCoverage` — last 30 days of commitment coverage breakdown. - `savingsTotals` — year-to-date and lifetime savings per SP type. - `monthlyPotentialSavings` — estimated monthly additional savings per SP type from the latest projection. ' parameters: - $ref: '#/components/parameters/tenantId' - $ref: '#/components/parameters/managementAccountId' responses: '200': description: AWS Organization detail. headers: Request-Id: $ref: '#/components/headers/RequestId' Content-Language: $ref: '#/components/headers/ContentLanguage' content: application/json: schema: $ref: '#/components/schemas/AwsOrganizationDetail' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' '503': $ref: '#/components/responses/ServiceUnavailable' /ps4commitments/v1/aws/organizations/{managementAccountId}/member-accounts: get: operationId: listAwsMemberAccounts tags: - PerfectScale for Commitments AWS summary: List member accounts under an organization description: 'Returns all member AWS accounts under the specified organization that have active or historical commitment coverage. Includes 30-day statistics and estimated monthly potential savings (`monthlyPotentialSavings`) per SP type.' parameters: - $ref: '#/components/parameters/tenantId' - $ref: '#/components/parameters/managementAccountId' - $ref: '#/components/parameters/ps4cPageToken' - $ref: '#/components/parameters/ps4cMaxResults' responses: '200': description: List of member accounts. headers: Request-Id: $ref: '#/components/headers/RequestId' Content-Language: $ref: '#/components/headers/ContentLanguage' content: application/json: schema: type: object required: - items properties: items: type: array items: $ref: '#/components/schemas/AwsMemberAccount' pageToken: type: - string - 'null' rowCount: type: - integer - 'null' format: int64 description: Best-effort count for the filtered result set. May be null or omitted for expensive counts. '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' '503': $ref: '#/components/responses/ServiceUnavailable' /ps4commitments/v1/aws/organizations/{managementAccountId}/member-accounts/{memberAccountId}: get: operationId: getAwsMemberAccount tags: - PerfectScale for Commitments AWS summary: Get a member account description: "Returns a single member AWS account including 30-day aggregates and the\ntrailing-window stats that drive the member-account Overview view:\n- `monthlyStats` — last 6 calendar months per SP type.\n- `dailyCoverage` — last 30 days of commitment coverage breakdown.\n- `savingsTotals` — year-to-date and lifetime savings per SP type, clamped to\n the parent organization's onboarding start.\n- `monthlyPotentialSavings` — estimated monthly additional savings per SP type,\n attributed from the parent organization's projection.\n" parameters: - $ref: '#/components/parameters/tenantId' - $ref: '#/components/parameters/managementAccountId' - $ref: '#/components/parameters/memberAccountId' responses: '200': description: Member account detail. headers: Request-Id: $ref: '#/components/headers/RequestId' Content-Language: $ref: '#/components/headers/ContentLanguage' content: application/json: schema: $ref: '#/components/schemas/AwsMemberAccountDetail' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' '503': $ref: '#/components/responses/ServiceUnavailable' /ps4commitments/v1/aws/organizations/{managementAccountId}/savings-plans: get: operationId: listAwsSavingsPlans tags: - PerfectScale for Commitments AWS summary: List AWS Savings Plans description: 'Returns the paginated list of Savings Plans for the specified organization. Filterable by plan type and state.' parameters: - $ref: '#/components/parameters/tenantId' - $ref: '#/components/parameters/managementAccountId' - in: query name: type required: false description: Filter by Savings Plan type. schema: type: string enum: - compute - ec2_instance - sagemaker - database - in: query name: status required: false description: Filter by Savings Plan state. schema: type: string enum: - pending_return - returning - active - expired - queued - queued_returning - payment_failed - payment_pending - $ref: '#/components/parameters/ps4cPageToken' - $ref: '#/components/parameters/ps4cMaxResults' responses: '200': description: Paginated list of Savings Plans. headers: Request-Id: $ref: '#/components/headers/RequestId' Content-Language: $ref: '#/components/headers/ContentLanguage' content: application/json: schema: type: object required: - items properties: items: type: array items: $ref: '#/components/schemas/AwsSavingsPlan' pageToken: type: - string - 'null' rowCount: type: - integer - 'null' format: int64 description: Best-effort count for the filtered result set. May be null or omitted for expensive counts. '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' '503': $ref: '#/components/responses/ServiceUnavailable' /ps4commitments/v1/aws/organizations/{managementAccountId}/reserved-instances: get: operationId: listAwsReservedInstances tags: - PerfectScale for Commitments AWS summary: List AWS Reserved Instances description: 'Returns the paginated list of Reserved Instances (RIs) for the specified organization. Filterable by state, instance type/family, region, and offering class.' parameters: - $ref: '#/components/parameters/tenantId' - $ref: '#/components/parameters/managementAccountId' - in: query name: status required: false description: Filter by Reserved Instance state. schema: type: string enum: - active - retired - payment_pending - payment_failed - queued - queued_deleted - in: query name: instanceType required: false description: Filter by exact EC2 instance type (e.g. `m5.large`). Case-sensitive. schema: type: string - in: query name: instanceFamily required: false description: Filter by EC2 instance family — the leading segment of `instanceType` before the dot (e.g. `m5` matches `m5.large`, `m5.xlarge`). Case-sensitive. schema: type: string - in: query name: region required: false description: Filter by AWS region (e.g. `us-east-1`). Case-sensitive. schema: type: string - in: query name: offeringClass required: false description: Filter by RI offering class. schema: type: string enum: - standard - convertible - $ref: '#/components/parameters/ps4cPageToken' - $ref: '#/components/parameters/ps4cMaxResults' responses: '200': description: Paginated list of Reserved Instances. headers: Request-Id: $ref: '#/components/headers/RequestId' Content-Language: $ref: '#/components/headers/ContentLanguage' content: application/json: schema: type: object required: - items properties: items: type: array items: $ref: '#/components/schemas/AwsReservedInstance' pageToken: type: - string - 'null' rowCount: type: - integer - 'null' format: int64 description: Best-effort count for the filtered result set. May be null or omitted for expensive counts. '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' '503': $ref: '#/components/responses/ServiceUnavailable' /ps4commitments/v1/aws/organizations/{managementAccountId}/recommendations: get: operationId: listAwsRecommendations tags: - PerfectScale for Commitments AWS summary: List AWS recommendations description: 'Returns commitment purchase recommendations for the organization. Multiple recommendations may exist per organization (one per service).' parameters: - $ref: '#/components/parameters/tenantId' - $ref: '#/components/parameters/managementAccountId' responses: '200': description: List of recommendations. headers: Request-Id: $ref: '#/components/headers/RequestId' Content-Language: $ref: '#/components/headers/ContentLanguage' content: application/json: schema: $ref: '#/components/schemas/AwsRecommendations' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' '503': $ref: '#/components/responses/ServiceUnavailable' /ps4commitments/v1/aws/organizations/{managementAccountId}/recommendations/{serviceId}: get: operationId: getAwsRecommendation tags: - PerfectScale for Commitments AWS summary: Get an AWS recommendation description: Returns full detail for a single recommendation including analysis metrics. parameters: - $ref: '#/components/parameters/tenantId' - $ref: '#/components/parameters/managementAccountId' - $ref: '#/components/parameters/serviceId' - $ref: '#/components/parameters/eligibleSpendGranularity' responses: '200': description: Recommendation detail. headers: Request-Id: $ref: '#/components/headers/RequestId' Content-Language: $ref: '#/components/headers/ContentLanguage' content: application/json: schema: $ref: '#/components/schemas/AwsRecommendationWithEligibleSpend' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' '503': $ref: '#/components/responses/ServiceUnavailable' /ps4commitments/v1/aws/organizations/{managementAccountId}/planned-purchases: get: operationId: listAwsPlannedPurchases tags: - PerfectScale for Commitments AWS summary: List AWS planned purchases description: 'Returns the laddering projections for the organization — one item per available `purchases-projection` document (typically `compute` and/or `database`; up to four product lines as PS4C expands). With no filters, returns all existing projection documents for the organization in stable product-line order (`compute`, then `database`, then any future lines in enum order). When a filtered product line has no projection document, the response is an empty `items` array (not `404`). Partial projection documents return only the fields available in storage. `404` is returned only when the organization does not exist or the caller cannot access it. An organization that is not onboarded for PS4C still returns `200` with an empty `items` array when no projection documents exist — use `GET /ps4commitments/v1/aws/organizations` (or get-by-id) for `onboardingStatus`. **Pagination**: results are returned in stable product-line order (`compute`, then `database`, then any future lines in enum order). Use `maxResults` to limit page size (default 50, max 500). When more items remain, the response includes a non-null `pageToken`; pass it unchanged on the next request with the same query parameters (`service`, `maxResults`). `rowCount` is the number of items in this page. An invalid `pageToken` returns `400` with code `pagination_token_invalid`; an expired token returns `400` with code `pagination_token_expired`.' parameters: - $ref: '#/components/parameters/tenantId' - $ref: '#/components/parameters/managementAccountId' - $ref: '#/components/parameters/service' - $ref: '#/components/parameters/ps4cPageToken' - $ref: '#/components/parameters/ps4cMaxResults' responses: '200': description: Paginated list of planned purchase projections. headers: Request-Id: $ref: '#/components/headers/RequestId' Content-Language: $ref: '#/components/headers/ContentLanguage' content: application/json: schema: type: object required: - items - rowCount properties: items: type: array items: $ref: '#/components/schemas/AwsPlannedPurchase' pageToken: type: - string - 'null' rowCount: type: integer format: int64 description: Number of items in `items` for this response. '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' '503': $ref: '#/components/responses/ServiceUnavailable' /ps4commitments/v1/aws/settings: get: operationId: listAwsOrganizationsSettings tags: - PerfectScale for Commitments AWS summary: List organization engine settings description: 'Returns automation and recommendation engine settings for every onboarded organization, broken down by product line (`compute`, `database`). Settings are configured at the customer level and apply uniformly across all organizations. Only product lines that are activated/onboarded for a given organization are returned. The `purchaseAccountId` is the member AWS account designated to execute SP purchases on behalf of that organization.' parameters: - $ref: '#/components/parameters/tenantId' - $ref: '#/components/parameters/ps4cPageToken' - $ref: '#/components/parameters/ps4cMaxResults' responses: '200': description: List of per-organization per-product-line engine settings. headers: Request-Id: $ref: '#/components/headers/RequestId' Content-Language: $ref: '#/components/headers/ContentLanguage' content: application/json: schema: type: object required: - items properties: items: type: array items: $ref: '#/components/schemas/AwsOrganizationSettingsItem' pageToken: type: - string - 'null' description: Opaque cursor for the next page. Absent when this is the last page. rowCount: type: integer format: int64 description: Number of items returned in this page. '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' '503': $ref: '#/components/responses/ServiceUnavailable' components: responses: Unauthorized: description: Missing or invalid credentials. headers: WWW-Authenticate: description: Bearer challenge per RFC 6750. schema: type: string example: Bearer realm="api.doit.com" Request-Id: $ref: '#/components/headers/RequestId' Content-Language: $ref: '#/components/headers/ContentLanguage' content: application/problem+json: schema: $ref: '#/components/schemas/ProblemDetails' example: type: https://developer.doit.com/errors/unauthorized title: Unauthorized status: 401 detail: missing or invalid bearer token instance: https://api.doit.com/requests/req_01HX9P2KQVJ8Z3M4T5N6V7W8Y9 code: unauthorized retryable: false BadRequest: description: Syntactically malformed request, missing required parameter, or unsupported parameter value. headers: Request-Id: $ref: '#/components/headers/RequestId' Content-Language: $ref: '#/components/headers/ContentLanguage' content: application/problem+json: schema: $ref: '#/components/schemas/ProblemDetails' examples: validationFailed: summary: Missing or invalid parameter value: type: https://developer.doit.com/errors/validation_failed title: Validation failed status: 400 detail: The request is missing a required parameter or contains an invalid value. instance: https://api.doit.com/requests/req_01HX9P2KQVJ8Z3M4T5N6V7W8Y9 code: validation_failed retryable: false tenantIdRequired: summary: Tenant scope not resolvable (§15) value: type: https://developer.doit.com/errors/tenant_id_required title: Tenant ID required status: 400 detail: X-Tenant-Id header is required when the credential can access more than one tenant. instance: https://api.doit.com/requests/req_01HX9P2KQVJ8Z3M4T5N6V7W8Y9 code: tenant_id_required retryable: false tenantIdMismatch: summary: Header conflicts with the key's tenant (§15) value: type: https://developer.doit.com/errors/tenant_id_mismatch title: Tenant ID mismatch status: 400 detail: X-Tenant-Id does not match the tenant the credential is scoped to. instance: https://api.doit.com/requests/req_01HX9P2KQVJ8Z3M4T5N6V7W8Y9 code: tenant_id_mismatch retryable: false gcpServiceRequired: summary: GCP region filter without product line value: type: https://developer.doit.com/errors/gcp_service_required title: GCP service required status: 400 detail: The region query parameter requires gcp_service. instance: https://api.doit.com/requests/req_01HX9P2KQVJ8Z3M4T5N6V7W8Y9 code: gcp_service_required retryable: false paginationTokenInvalid: summary: Structurally invalid pagination cursor (§8) value: type: https://developer.doit.com/errors/pagination_token_invalid title: Invalid pagination token status: 400 detail: The pageToken cursor is structurally invalid (decode failure or tampering). instance: https://api.doit.com/requests/req_01HX9P2KQVJ8Z3M4T5N6V7W8Y9 code: pagination_token_invalid retryable: false paginationTokenExpired: summary: Expired pagination cursor (§8) value: type: https://developer.doit.com/errors/pagination_token_expired title: Expired pagination token status: 400 detail: The pageToken cursor is no longer valid; restart pagination from the beginning. instance: https://api.doit.com/requests/req_01HX9P2KQVJ8Z3M4T5N6V7W8Y9 code: pagination_token_expired retryable: false NotFound: description: The requested resource does not exist. headers: Request-Id: $ref: '#/components/headers/RequestId' Content-Language: $ref: '#/components/headers/ContentLanguage' content: application/problem+json: schema: $ref: '#/components/schemas/ProblemDetails' example: type: https://developer.doit.com/errors/not_found title: Resource not found status: 404 detail: organization not found instance: https://api.doit.com/requests/req_01HX9P2KQVJ8Z3M4T5N6V7W8Y9 code: not_found retryable: false ServiceUnavailable: description: Service temporarily unavailable; retry after the indicated delay. headers: Retry-After: description: Seconds to wait before retrying. schema: type: integer example: 30 Request-Id: $ref: '#/components/headers/RequestId' Content-Language: $ref: '#/components/headers/ContentLanguage' content: application/problem+json: schema: $ref: '#/components/schemas/ProblemDetails' example: type: https://developer.doit.com/errors/service_unavailable title: Service unavailable status: 503 detail: entitlement check temporarily unavailable instance: https://api.doit.com/requests/req_01HX9P2KQVJ8Z3M4T5N6V7W8Y9 code: service_unavailable retryable: true InternalServerError: description: Unexpected server error. headers: Request-Id: $ref: '#/components/headers/RequestId' Content-Language: $ref: '#/components/headers/ContentLanguage' content: application/problem+json: schema: $ref: '#/components/schemas/ProblemDetails' example: type: https://developer.doit.com/errors/internal_error title: Internal server error status: 500 detail: an unexpected error occurred instance: https://api.doit.com/requests/req_01HX9P2KQVJ8Z3M4T5N6V7W8Y9 code: internal_error retryable: true Forbidden: description: Authenticated but not authorised to access this customer's PS4C resources. headers: Request-Id: $ref: '#/components/headers/RequestId' Content-Language: $ref: '#/components/headers/ContentLanguage' content: application/problem+json: schema: $ref: '#/components/schemas/ProblemDetails' example: type: https://developer.doit.com/errors/forbidden title: Forbidden status: 403 detail: caller does not have permission to access this customer's commitments instance: https://api.doit.com/requests/req_01HX9P2KQVJ8Z3M4T5N6V7W8Y9 code: forbidden retryable: false schemas: AwsOnboardingStatusEntry: type: object required: - status description: Per-product-line PS4C onboarding state. properties: status: type: string description: Current onboarding lifecycle stage for this product line. enum: - not_started - onboarding - done - error onboardingStartedAt: type: - string - 'null' format: date-time description: 'When PS4C first began tracking commitments for this product line. Used to bound lifetime savings totals and to render onboarding history in the DoiT Console. ' Policy: type: string enum: - conservative - balanced - max_savings description: 'Coverage target policy. - `conservative` — lower coverage target (~65%) - `balanced` — moderate coverage target (~80%) - `max_savings` — aggressive coverage target (~90%) ' AwsMonthlyStatsEntry: type: object required: - month - esr - onDemandCost - costWithSavings description: 'One calendar-month aggregate for the organization. Money fields are wrapped objects (`{amount, currency}`). ' properties: month: type: string pattern: ^\d{4}-(0[1-9]|1[0-2])$ example: 2025-06 esr: type: number format: double description: Effective Savings Rate for the month (0–1). onDemandCost: allOf: - $ref: '#/components/schemas/Money' description: Eligible on-demand cost for the month. costWithSavings: allOf: - $ref: '#/components/schemas/Money' description: Actual cost after commitments for the month. AwsOrganization: type: object required: - managementAccountId properties: managementAccountId: type: string description: 12-digit account number of the organization's management (payer) account. example: '123456789012' displayName: type: - string - 'null' description: Human-readable account alias, if available. example: Acme Prod onboardingStatus: allOf: - $ref: '#/components/schemas/AwsOnboardingStatus' description: Per-product-line PS4C onboarding state for this organization. savingsPlansSyncTime: type: - string - 'null' format: date-time description: Timestamp of the last successful Savings Plan inventory sync. stats30d: type: object description: Trailing 30-day aggregate metrics, broken down by SP type. properties: compute: $ref: '#/components/schemas/Stats30dSummary' database: $ref: '#/components/schemas/Stats30dSummary' savingsTotals: type: object description: 'Year-to-date and lifetime savings per SP type. Same figures the detail endpoint returns — surfaced on the list item so callers can compute customer-level totals (sum across organizations) without an extra round-trip per organization. Lifetime is bounded by each organization''s PS4C onboarding start. ' properties: compute: $ref: '#/components/schemas/AwsSavingsTotals' database: $ref: '#/components/schemas/AwsSavingsTotals' monthlyPotentialSavings: allOf: - $ref: '#/components/schemas/AwsMonthlyPotentialSavings' description: Estimated monthly additional savings per SP type for this organization. AwsMemberAccount: type: object required: - memberAccountId - managementAccountId properties: memberAccountId: type: string description: 12-digit AWS member account ID. managementAccountId: type: string description: 12-digit AWS management (payer) account ID of the parent AWS Organization. displayName: type: - string - 'null' description: Human-readable account alias, if available. example: prod-web stats30d: type: object description: Trailing 30-day aggregate metrics per SP type. properties: compute: $ref: '#/components/schemas/Stats30dSummary' database: $ref: '#/components/schemas/Stats30dSummary' monthlyPotentialSavings: allOf: - $ref: '#/components/schemas/AwsMonthlyPotentialSavings' description: 'Estimated monthly additional savings per SP type for this member account, attributed from the parent organization''s projection by share of trailing-60-day SP-eligible on-demand cost. ' AwsMemberAccountDetail: description: "Single member AWS account detail. Adds the trailing-window stats that drive\nthe per-member-account view on the DoiT Console overview screen on top of the\nlist-item shape:\n- `monthlyStats` — last 6 calendar months per SP type\n- `dailyCoverage` — last 30 days per SP type\n- `savingsTotals` — YTD and lifetime savings per SP type, bounded by the parent\n organization's PS4C onboarding start\n\n`monthlyPotentialSavings` (estimated monthly additional savings per SP type) is inherited\nfrom the list-item shape (`AwsMemberAccount`).\n" allOf: - $ref: '#/components/schemas/AwsMemberAccount' - type: object properties: monthlyStats: type: object description: Trailing 6 calendar months of stats, grouped by SP type. properties: compute: type: array items: $ref: '#/components/schemas/AwsMonthlyStatsEntry' database: type: array items: $ref: '#/components/schemas/AwsMonthlyStatsEntry' dailyCoverage: type: object description: Trailing 30 days of commitment coverage, grouped by SP type. properties: compute: type: array items: $ref: '#/components/schemas/AwsDailyCoverageEntry' database: type: array items: $ref: '#/components/schemas/AwsDailyCoverageEntry' savingsTotals: type: object description: 'Year-to-date and lifetime savings, grouped by SP type. Lifetime is bounded by the parent organization''s PS4C onboarding start. ' properties: compute: $ref: '#/components/schemas/AwsSavingsTotals' database: $ref: '#/components/schemas/AwsSavingsTotals' AwsPurchasePlanStep: type: object description: A single weekly ladder step within a projection. required: - order - scheduledDate - purchaseAmount - cumulativeCommitment - estimatedSavings - isBootstrap - isFinal - requiresApproval properties: order: type: integer description: Step sequence number (1-based). scheduledDate: type: string format: date purchaseAmount: allOf: - $ref: '#/components/schemas/Money' description: Hourly commitment to purchase on this step. cumulativeCommitment: allOf: - $ref: '#/components/schemas/Money' description: Total hourly commitment after this step executes. estimatedSavings: allOf: - $ref: '#/components/schemas/Money' description: 'This step''s proportional share of the projection-level `estimatedSavings` (allocated by purchase amount). Zero when the recommendation total is unavailable.' isBootstrap: type: boolean description: '`true` if this is the initial bootstrap purchase.' isFinal: type: boolean description: '`true` if this step reaches the target commitment.' requiresApproval: type: boolean description: '`true` when this step''s `cumulativeCommitment` exceeds the customer-approved commitment ceiling (`approvedFinalCommitment`). Used for per-step status in the ladder even when the projection-level `purchaseApprovalStatus` is `approved`.' AwsRecommendation: type: object description: Recommended commitments properties: policy: type: string description: Actual configured policy currentCommitment: type: number format: double description: Active commitments currently applied to this account, measured in hourly spend. recommendedCommitment: type: number format: double description: Recommended hourly commitment level based on usage patterns and your commitment policy, designed to increase savings while managing underutilization risk. potentialAdditionalSavings: type: number format: double description: Estimated additional monthly savings from applying the recommended commitment policy. estimatedAverageCoverage: type: number format: double description: Estimated average coverage. estimatedEquivalentRecommendedCommitment: type: number format: double description: Estimated shelf-price hourly equivalent of the recommended commitment, derived from the median hourly eligible usage over the last 60 days multiplied by the estimated average coverage. AwsReservedInstanceRecurringCharge: type: object required: - amount - frequency properties: amount: type: number format: double description: The amount of the recurring charge. frequency: type: string description: The frequency of the recurring charge (e.g. `Hourly`). PaymentOption: type: string enum: - no_upfront - partial_upfront - all_upfront description: 'Payment structure for a commitment. - `no_upfront` — all charges paid monthly - `partial_upfront` — a portion paid upfront; remainder monthly - `all_upfront` — full commitment paid upfront ' AwsDailyCoverageEntry: type: object required: - date description: 'One day of commitment-coverage breakdown. Money fields are wrapped objects (`{amount, currency}`). ' properties: date: type: string format: date onDemandCost: $ref: '#/components/schemas/Money' savingsPlanCost: $ref: '#/components/schemas/Money' reservedInstCost: $ref: '#/components/schemas/Money' flexsaveCost: $ref: '#/components/schemas/Money' spotCost: $ref: '#/components/schemas/Money' Money: type: object required: - amount - currency properties: amount: type: string pattern: ^-?(0|[1-9]\d*)(\.\d+)?$ description: Decimal monetary amount at ISO 4217 minor-unit precision (string). currency: type: string pattern: ^[A-Z]{3}$ description: ISO 4217 currency code. example: USD EligibleSpendDataPoint: type: object description: One time-bucketed eligible-spend usage data point. properties: usageTime: type: - string - 'null' format: date-time totalUsage: type: number format: double description: Total usage. minUsage: type: number format: double description: Min usage. maxUsage: type: number format: double description: Max usage. medianUsage: type: number format: double description: Median usage. AwsPlannedPurchase: type: object required: - managementAccountId - service - status properties: managementAccountId: type: string description: 12-digit account number of the organization's management (payer) account. service: type: string enum: - compute - database description: PS4C product line this projection belongs to. status: type: string enum: - valid - expired description: 'Projection lifecycle state. `expired` means the planning cycle has lapsed and a new projection should be triggered by the automated scheduler. When the stored document has no `status`, the server returns `valid`.' purchaseApprovalStatus: type: string enum: - pending_approval - approved - paused description: 'Customer commitment approval state for this product line. Absent or unrecognized stored values are returned as `pending_approval`.' pauseNote: type: - string - 'null' description: Customer-visible pause reason, when purchases are paused. requiresApproval: type: boolean description: '`true` when the projection as a whole requires customer approval before the planner stores a purchase plan: the target `finalCommitment` exceeds the approved ceiling, and/or `purchaseApprovalStatus` is not `approved`. Distinct from step-level `steps[].requiresApproval`, which flags individual ladder rows that exceed the ceiling.' wowViolation: type: boolean description: '`true` when week-over-week eligible spend dropped beyond the allowed threshold.' profile: $ref: '#/components/schemas/Policy' term: $ref: '#/components/schemas/CommitmentTerm' paymentOption: type: - string - 'null' enum: - no_upfront - partial_upfront - all_upfront - null finalCommitment: allOf: - $ref: '#/components/schemas/Money' description: Target hourly commitment at the end of the laddering cycle. Currency is `USD` in v1. weeksToTarget: type: - integer - 'null' description: Number of ladder steps remaining to reach the target commitment. planningCycleStartDate: type: - string - 'null' format: date planningCycleEndDate: type: - string - 'null' format: date estimatedSavings: allOf: - $ref: '#/components/schemas/Money' description: Total estimated savings from the underlying recommendation. Currency is `USD` in v1. steps: type: array items: $ref: '#/components/schemas/AwsPurchasePlanStep' description: 'Weekly ladder steps from the projection output. Omitted when the stored document has no `output` section.' AwsRecommendations: type: object description: Recommended commitments for onboarded services properties: compute: allOf: - $ref: '#/components/schemas/AwsRecommendation' description: Recommended commitments for compute database: allOf: - $ref: '#/components/schemas/AwsRecommendation' description: Recommended commitments for database AwsOnboardingStatus: type: object description: 'PS4C onboarding state grouped by product line. A product line is omitted when it is not onboarded at all. ' properties: compute: $ref: '#/components/schemas/AwsOnboardingStatusEntry' database: $ref: '#/components/schemas/AwsOnboardingStatusEntry' CommitmentTerm: type: string enum: - one_year - three_year description: Commitment term length. Stats30dSummary: type: object description: 'Minimal 30-day aggregate. Only `esr` and `savings` are persisted at this granularity. Responses are denominated in USD. Used by both AWS organization/member-account and GCP billing-account list items. ' properties: esr: type: - number - 'null' format: double description: Effective Savings Rate over the last 30 days (0–1). example: 0.187 savings: allOf: - $ref: '#/components/schemas/Money' description: Realized savings amount over the last 30 days. AwsMonthlyPotentialSavings: type: object description: 'Estimated monthly additional savings per SP type, taken from the latest PS4C purchase projection. Unlike `savingsTotals` (realized YTD/lifetime savings), this is a forward-looking estimate of the monthly savings achievable if the recommended commitments were purchased. For member accounts, the organization figure is attributed by each member account''s share of trailing-60-day SP-eligible on-demand cost. A per-SP-type value of `0.00` means the latest projection is missing, expired, or empty. The field is omitted entirely only for accounts that have never had potential savings computed. ' properties: compute: $ref: '#/components/schemas/Money' database: $ref: '#/components/schemas/Money' AwsOrganizationSettingsItem: type: object required: - managementAccountId - purchaseAccountId - services properties: managementAccountId: type: string description: 12-digit account number of the organization's management (payer) account. example: '123456789012' purchaseAccountId: type: - string - 'null' description: Member AWS account used to execute SP purchases for this organization. Null if not configured. example: '987654321098' services: type: array items: $ref: '#/components/schemas/AwsOrganizationServiceSettings' description: Settings for each activated product line. AwsOrganizationDetail: description: 'Single AWS Organization detail. Adds the trailing-window stats that drive the DoiT Console overview screen on top of the list-item shape: - `monthlyStats` — last 6 calendar months per SP type - `dailyCoverage` — last 30 days per SP type `savingsTotals` (precomputed YTD and lifetime savings per SP type) and `monthlyPotentialSavings` (estimated monthly additional savings per SP type) are inherited from the list-item shape (`AwsOrganization`). ' allOf: - $ref: '#/components/schemas/AwsOrganization' - type: object properties: monthlyStats: type: object description: Trailing 6 calendar months of organization stats, grouped by SP type. properties: compute: type: array items: $ref: '#/components/schemas/AwsMonthlyStatsEntry' database: type: array items: $ref: '#/components/schemas/AwsMonthlyStatsEntry' dailyCoverage: type: object description: Trailing 30 days of commitment coverage, grouped by SP type. properties: compute: type: array items: $ref: '#/components/schemas/AwsDailyCoverageEntry' database: type: array items: $ref: '#/components/schemas/AwsDailyCoverageEntry' AwsReservedInstance: type: object required: - commitmentId - accountId - state - instanceType - mtdSavings description: Reserved Instance item in the external inventory response. Money fields are wrapped `{amount, currency}` objects. properties: commitmentId: type: string description: Reserved Instance ID (AWS-assigned). reservedInstanceArn: type: - string - 'null' description: Full ARN of the Reserved Instance, when known. accountId: type: string description: AWS account ID where the Reserved Instance exists. state: type: string description: 'Reserved Instance state, tracking the AWS EC2 Reserved Instance API verbatim in external snake_case form. Differs from Savings Plan states by design — for example, the terminal state is `retired` here vs `expired` on Savings Plans. ' enum: - active - retired - payment_pending - payment_failed - queued - queued_deleted instanceType: type: string description: EC2 instance type on which the RI can be used (e.g. `m5.large`). instanceCount: type: - integer - 'null' format: int64 instanceTenancy: type: string description: Tenancy of the instance (e.g. `default`, `dedicated`). availabilityZone: type: - string - 'null' region: type: - string - 'null' platform: type: - string - 'null' description: type: string description: Product platform description. scope: type: string description: Scope of the Reserved Instance (e.g. `Availability Zone`, `Region`). offeringClass: type: string enum: - standard - convertible offeringType: type: string description: RI offering type (e.g. `All Upfront`). termDuration: allOf: - $ref: '#/components/schemas/CommitmentTerm' description: Term inferred from the RI duration. Absent when it does not map cleanly to 1yr / 3yr. durationSeconds: type: - integer - 'null' format: int64 description: RI duration in seconds. startTime: type: - string - 'null' format: date-time endTime: type: - string - 'null' format: date-time currencyCode: type: string description: ISO 4217 currency code (USD in practice). fixedPrice: allOf: - $ref: '#/components/schemas/Money' description: Purchase price of the Reserved Instance. usagePrice: allOf: - $ref: '#/components/schemas/Money' description: Usage price per hour. hourlyRecurringFee: allOf: - $ref: '#/components/schemas/Money' upfrontFee: allOf: - $ref: '#/components/schemas/Money' totalAssetValue: allOf: - $ref: '#/components/schemas/Money' averageOnDemandHourlyRate: allOf: - $ref: '#/components/schemas/Money' amortizedRecurringFee: allOf: - $ref: '#/components/schemas/Money' amortizedUpfrontFee: allOf: - $ref: '#/components/schemas/Money' totalAmortizedFee: allOf: - $ref: '#/components/schemas/Money' netRiSavings: allOf: - $ref: '#/components/schemas/Money' realizedSavings: allOf: - $ref: '#/components/schemas/Money' unrealizedSavings: allOf: - $ref: '#/components/schemas/Money' totalPotentialRiSavings: allOf: - $ref: '#/components/schemas/Money' onDemandCostOfRiHoursUsed: allOf: - $ref: '#/components/schemas/Money' riCostForUnusedHours: allOf: - $ref: '#/components/schemas/Money' purchasedHours: type: - integer - 'null' format: int64 purchasedUnits: type: - integer - 'null' format: int64 totalActualHours: type: - integer - 'null' format: int64 totalNormalizedUnits: type: - integer - 'null' format: int64 unusedHours: type: - integer - 'null' format: int64 unusedNormalizedUnits: type: - integer - 'null' format: int64 utilizationPercentage: type: - number - 'null' format: double description: Cost-explorer reported utilization percentage (0–1). utilizationPercentageInUnits: type: - number - 'null' format: double mtdSavings: allOf: - $ref: '#/components/schemas/Money' description: Month-to-date realized savings. mtdUtilization: type: - number - 'null' format: double description: 'Month-to-date utilization (0–1). Null when not yet available. A stored 0% is indistinguishable from unavailable and is returned as null. ' updatedAt: type: - string - 'null' format: date-time description: Timestamp of the last successful inventory sync for this Reserved Instance. recurringCharges: type: array items: $ref: '#/components/schemas/AwsReservedInstanceRecurringCharge' AwsRecommendationWithEligibleSpend: type: object description: Service recommendation with eligible spend data properties: recommendation: allOf: - $ref: '#/components/schemas/AwsRecommendation' description: Recommended commitments eligibleUsage: type: array items: $ref: '#/components/schemas/EligibleSpendDataPoint' ProblemDetails: type: object required: - type - title - status - detail - instance - code - retryable additionalProperties: true description: RFC 9457 Problem Details — standard DoiT error envelope for all non-2xx responses. properties: type: type: string format: uri example: https://developer.doit.com/errors/not_found title: type: string example: Resource not found status: type: integer example: 404 detail: type: string example: customer not found instance: type: string format: uri example: https://api.doit.com/requests/req_01HX9P2KQVJ8Z3M4T5N6V7W8Y9 code: type: string pattern: ^([a-z][a-z0-9_]*|[a-z][a-z0-9]*(?:-[a-z0-9]+)*\.[a-z][a-z0-9_]*)$ example: not_found retryable: type: boolean example: false details: type: array items: type: object required: - issue properties: issue: type: string pattern: ^[a-z][a-z0-9_]*$ field: type: string additionalProperties: true docsUrl: type: string format: uri AwsSavingsPlan: type: object required: - commitmentId - savingsPlanType - state properties: commitmentId: type: string description: Savings Plan ID (AWS-assigned GUID). savingsPlanArn: type: - string - 'null' description: Full ARN of the Savings Plan. savingsPlanType: type: string enum: - compute - ec2_instance - sagemaker - database state: type: string enum: - pending_return - returning - active - expired - queued - queued_returning - payment_failed - payment_pending paymentOption: type: string enum: - no_upfront - partial_upfront - all_upfront termDuration: $ref: '#/components/schemas/CommitmentTerm' commitment: allOf: - $ref: '#/components/schemas/Money' description: Hourly commitment amount. upfrontPaymentAmount: allOf: - $ref: '#/components/schemas/Money' recurringPaymentAmount: allOf: - $ref: '#/components/schemas/Money' ec2InstanceFamily: type: - string - 'null' region: type: - string - 'null' startTime: type: - string - 'null' format: date-time endTime: type: - string - 'null' format: date-time lastMonthUtilization: type: - number - 'null' format: double description: Utilization percentage (0–1) in the last full calendar month. lastMonthSavings: allOf: - $ref: '#/components/schemas/Money' description: Realized savings in the last full calendar month. Null if not yet available. AwsSavingsTotals: type: object required: - ytd - lifetime description: 'Running savings figures derived server-side from the full monthly stats history (`onDemandCost - costWithSavings` per month, optionally bounded by onboarding start date and start of year). ' properties: ytd: allOf: - $ref: '#/components/schemas/Money' description: Year-to-date realized savings. lifetime: allOf: - $ref: '#/components/schemas/Money' description: Lifetime realized savings since onboarding. AwsOrganizationSettings: type: object description: Automation and recommendation engine settings for one product line. required: - purchaseMode - minimumCommitment - lastDayOfMonthForPurchase - maximumCommitment - term - paymentOption - policy properties: purchaseMode: type: string enum: - autonomous - requires_approval description: Whether purchases execute automatically or require approval. minimumCommitment: type: number format: double description: Minimum hourly commitment amount (USD) per purchase step. lastDayOfMonthForPurchase: type: integer description: Latest calendar day of the month on which a purchase may be scheduled. maximumCommitment: type: number format: double description: Maximum total hourly commitment (USD). 0 means no cap. term: allOf: - $ref: '#/components/schemas/CommitmentTerm' description: Preferred commitment term length. paymentOption: allOf: - $ref: '#/components/schemas/PaymentOption' description: Preferred payment structure for new commitments. policy: allOf: - $ref: '#/components/schemas/Policy' description: Coverage target policy. upfrontPercentage: type: number format: double description: Fraction of the hourly commitment paid upfront (0–1). Only present when paymentOption is `partial_upfront`. AwsOrganizationServiceSettings: type: object required: - service - settings properties: service: type: string enum: - compute - database description: Product line these settings apply to. settings: $ref: '#/components/schemas/AwsOrganizationSettings' parameters: serviceId: in: path name: serviceId required: true description: Service ID (compute|database). schema: type: string enum: - compute - database eligibleSpendGranularity: in: query name: granularity required: false description: Time bucket for eligible spend data points. schema: type: string enum: - hour - day - week - month default: day service: in: query name: service required: false description: Filter by PS4C product line (AWS planned purchases). schema: type: string enum: - compute - database memberAccountId: in: path name: memberAccountId required: true description: AWS member account ID (12-digit AWS account number). schema: type: string pattern: ^\d{12}$ example: '987654321098' tenantId: in: header name: X-Tenant-Id required: false description: "Tenant (customer) identifier that conveys the tenant scope for the request\n(DoiT API Design Standards §15).\n\nResolution when the header is absent:\n- A key scoped to exactly one tenant resolves to that tenant automatically; the header is\n optional.\n- A principal that can access more than one tenant (e.g. DoiT-employee keys) must supply the\n header. Without it the request is rejected with `400` and code `tenant_id_required` — the\n server does not guess across tenant scopes.\n\nWhen the header is present but conflicts with the tenant the key is scoped to, the request is\nrejected with `400` and code `tenant_id_mismatch`.\n" schema: type: string example: Kp2mN8qL4vR0sT1wX3yZ ps4cMaxResults: in: query name: maxResults required: false description: Maximum number of items to return. Server may return fewer. Defaults to 50; maximum 500. schema: type: integer minimum: 1 maximum: 500 default: 50 ps4cPageToken: in: query name: pageToken required: false description: 'Opaque cursor token returned by a previous list response. Omit to start from the beginning; an empty or absent token in a response means there are no more results. Do not parse it. A structurally invalid cursor returns `400` with code `pagination_token_invalid`; an expired cursor returns `400` with code `pagination_token_expired` — restart pagination from the beginning. ' schema: type: string managementAccountId: in: path name: managementAccountId required: true description: 12-digit AWS management (payer) account ID (the account that owns the AWS Organization) that scopes the request. schema: type: string pattern: ^\d{12}$ example: '123456789012' headers: ContentLanguage: description: Language of the response body. Always `en` for this API. schema: type: string enum: - en RequestId: description: DoiT request identifier for support and log correlation. Format `req_`. schema: type: string pattern: ^req_[0-9A-HJKMNP-TV-Z]{26}$ example: req_01HX9P2KQVJ8Z3M4T5N6V7W8Y9 securitySchemes: api_key: type: apiKey name: Authorization description: Use the "Bearer " format or sign in for autofill in: header tenantId: type: apiKey name: X-Tenant-Id description: 'Tenant (customer) ID that sets the request''s customer context. Required when the credential can access more than one tenant; omit when the credential is scoped to exactly one tenant (the server resolves that tenant automatically). If omitted for a multi-tenant credential, the request fails with `400` and code `tenant_id_required`. If the value conflicts with the credential''s tenant scope, the request fails with `400` and code `tenant_id_mismatch`. Use this header over the legacy `customerContext` query parameter, which only applies to legacy API keys and is ignored by personal and service-account API tokens. ' in: header oauth2: type: oauth2 flows: authorizationCode: authorizationUrl: https://console.doit.com/sign-in/oauth tokenUrl: https://console.doit.com/api/auth/token scopes: dci: Access All Data x-samples-languages: - curl - go - node - python x-cli-config: security: oauth2 params: client_id: cli