openapi: 3.1.0
info:
  title: Open Cost Read API
  version: 0.1.0
  description: >-
    A provider-neutral, read-only profile for customer-authorized access to
    live incurred technology costs. Cost fields use FOCUS names only where
    their semantics match.
  contact:
    name: Vibe Costs provider integrations
    email: hello@vibecosts.app
    url: https://vibecosts.app/cost-api/docs
  license:
    name: Apache License 2.0
    identifier: Apache-2.0
externalDocs:
  description: Human implementation guide and conformance resources
  url: https://vibecosts.app/cost-api/docs
servers:
  - url: https://api.provider.example
    description: Replace with the provider's production API origin
tags:
  - name: Cost accounts
    description: Billing accounts explicitly included in the customer grant
  - name: Costs
    description: Provider-authoritative incurred cost before payment settlement
paths:
  /.well-known/oauth-authorization-server:
    get:
      operationId: readAuthorizationServerMetadata
      summary: Discover OAuth endpoints and supported scopes
      description: >-
        RFC 8414 authorization-server metadata. Existing provider discovery
        conventions are valid when they expose equivalent information.
      security: []
      responses:
        "200":
          description: Authorization-server metadata
          content:
            application/json:
              schema:
                type: object
                required: [issuer, authorization_endpoint, token_endpoint]
                properties:
                  issuer: { type: string, format: uri }
                  authorization_endpoint: { type: string, format: uri }
                  token_endpoint: { type: string, format: uri }
                  revocation_endpoint: { type: string, format: uri }
                  scopes_supported:
                    type: array
                    items: { type: string }
                  code_challenge_methods_supported:
                    type: array
                    items: { type: string, enum: [S256] }
        "404":
          description: Authorization-server metadata is not published at this illustrative path
  /v1/cost-accounts:
    get:
      operationId: listCostAccounts
      tags: [Cost accounts]
      summary: List consented cost accounts
      description: >-
        Returns only organizations, workspaces, or billing accounts selected by
        the customer during consent. Identifiers should be stable and opaque.
      security:
        - oauth: ["costs:read"]
        - restrictedToken: []
      responses:
        "200":
          description: Cost accounts included in the grant
          content:
            application/json:
              schema:
                type: object
                required: [accounts]
                properties:
                  accounts:
                    type: array
                    items:
                      $ref: "#/components/schemas/CostAccount"
              examples:
                oneAccount:
                  summary: One consented USD account
                  value:
                    accounts:
                      - id: acct_123
                        name: Production workspace
                        billing_currency: USD
                        time_zone: America/New_York
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "429": { $ref: "#/components/responses/RateLimited" }
        "500": { $ref: "#/components/responses/ProviderFailure" }
  /v1/costs:
    get:
      operationId: readCosts
      tags: [Costs]
      summary: Read incurred costs
      description: >-
        Reads provider-calculated customer cost for an inclusive start and
        exclusive end. Open-period values must be marked estimated. updated_at
        is provider calculation time, not request time. Top-level totals cover
        the complete query window and repeat unchanged across item pages.
      security:
        - oauth: ["costs:read"]
        - restrictedToken: []
      parameters:
        - name: cost_account_id
          in: query
          required: true
          schema: { type: string }
        - name: start
          in: query
          required: true
          description: Inclusive UTC RFC 3339 timestamp
          schema: { type: string, format: date-time }
        - name: end
          in: query
          required: true
          description: Exclusive UTC RFC 3339 timestamp
          schema: { type: string, format: date-time }
        - name: granularity
          in: query
          description: When items are returned, their charge periods reflect this bucket size
          schema: { type: string, enum: [hour, day, billing_period], default: day }
        - name: page_token
          in: query
          description: Opaque token binding the original query and provider snapshot
          schema: { type: string }
        - name: If-None-Match
          in: header
          required: false
          description: Previously returned ETag for a conditional read
          schema: { type: string }
      responses:
        "200":
          description: Authoritative incurred costs for the requested period
          headers:
            ETag:
              schema: { type: string }
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CostResponse"
              examples:
                currentPeriodTotal:
                  summary: Minimum Core response for an open billing period
                  value:
                    cost_account_id: acct_123
                    billing_currency: USD
                    period_start: "2026-08-01T00:00:00Z"
                    period_end: "2026-08-21T00:00:00Z"
                    cost_status: estimated
                    billed_cost: "37.37"
                    updated_at: "2026-08-21T13:45:00Z"
                    items: []
                detailedFinalPeriod:
                  summary: Final period with credits and service attribution
                  value:
                    cost_account_id: acct_123
                    billing_currency: USD
                    period_start: "2026-07-01T00:00:00Z"
                    period_end: "2026-08-01T00:00:00Z"
                    cost_status: final
                    credits: "-5.00"
                    billed_cost: "37.37"
                    updated_at: "2026-08-03T09:10:00Z"
                    items:
                      - charge_period_start: "2026-07-01T00:00:00Z"
                        charge_period_end: "2026-08-01T00:00:00Z"
                        service_name: Build
                        resource_id: project_abc
                        list_cost: "42.37"
                        credits: "-5.00"
                        billed_cost: "37.37"
        "304":
          description: The provider snapshot matches If-None-Match
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "429": { $ref: "#/components/responses/RateLimited" }
        "500": { $ref: "#/components/responses/ProviderFailure" }
components:
  securitySchemes:
    oauth:
      type: oauth2
      flows:
        authorizationCode:
          authorizationUrl: https://provider.example/oauth/authorize
          tokenUrl: https://provider.example/oauth/token
          scopes:
            "costs:read": Read incurred costs for accounts selected during consent
    restrictedToken:
      type: http
      scheme: bearer
      bearerFormat: Restricted cost-read token
  schemas:
    Decimal:
      type: string
      pattern: "^-?[0-9]+(\\.[0-9]+)?$"
      examples: ["37.37"]
    UtcTimestamp:
      type: string
      format: date-time
      pattern: "Z$"
      description: RFC 3339 timestamp normalized to the UTC Z designator
    CostAccount:
      type: object
      additionalProperties: false
      required: [id, name, billing_currency, time_zone]
      properties:
        id:
          type: string
          description: Stable opaque identifier; do not expose an email address
        name: { type: string, description: Customer-facing account name }
        billing_currency:
          type: string
          pattern: "^[A-Z]{3}$"
        time_zone:
          type: string
          examples: [America/New_York]
    CostItem:
      type: object
      additionalProperties: false
      required: [charge_period_start, charge_period_end, billed_cost]
      properties:
        charge_period_start: { $ref: "#/components/schemas/UtcTimestamp" }
        charge_period_end: { $ref: "#/components/schemas/UtcTimestamp" }
        service_name: { type: string }
        service_category: { type: string }
        resource_id: { type: string }
        resource_name: { type: string }
        region_id: { type: string }
        consumed_quantity: { $ref: "#/components/schemas/Decimal" }
        consumed_unit: { type: string }
        list_cost: { $ref: "#/components/schemas/Decimal" }
        contracted_cost: { $ref: "#/components/schemas/Decimal" }
        credits:
          allOf: [{ $ref: "#/components/schemas/Decimal" }]
          description: Signed credit total; a negative value reduces billed cost
        billed_cost:
          allOf: [{ $ref: "#/components/schemas/Decimal" }]
          description: Invoice-basis cost after reduced rates and discounts, including applicable tax charges
        effective_cost:
          allOf: [{ $ref: "#/components/schemas/Decimal" }]
          description: Optional FOCUS amortized cost; not a synonym for billed cost
        invoice_id: { type: string }
        tags:
          type: object
          additionalProperties: { type: string }
    CostResponse:
      type: object
      additionalProperties: false
      required:
        - cost_account_id
        - billing_currency
        - period_start
        - period_end
        - cost_status
        - billed_cost
        - updated_at
        - items
      properties:
        cost_account_id: { type: string, minLength: 1 }
        billing_currency:
          type: string
          pattern: "^[A-Z]{3}$"
        period_start:
          allOf: [{ $ref: "#/components/schemas/UtcTimestamp" }]
          description: Inclusive start of the exact requested interval
        period_end:
          allOf: [{ $ref: "#/components/schemas/UtcTimestamp" }]
          description: Exclusive end of the exact requested interval
        cost_status:
          type: string
          enum: [estimated, final]
          description: Open periods are estimated; later changes to final periods are explicit corrections
        credits:
          allOf: [{ $ref: "#/components/schemas/Decimal" }]
          description: Signed credit total; a negative value reduces billed cost
        billed_cost:
          allOf: [{ $ref: "#/components/schemas/Decimal" }]
          description: Invoice-basis cost, including applicable tax charges, for the complete query interval and every item page
        effective_cost:
          allOf: [{ $ref: "#/components/schemas/Decimal" }]
          description: Optional FOCUS amortized cost including applicable prepaid purchases
        updated_at:
          allOf: [{ $ref: "#/components/schemas/UtcTimestamp" }]
          description: UTC time when the provider last calculated this result
        items:
          type: array
          items: { $ref: "#/components/schemas/CostItem" }
        next_page_token:
          type: string
          minLength: 1
          description: Opaque token for the next item page in the same provider snapshot
  responses:
    BadRequest:
      description: Invalid period, granularity, account identifier, or page token
    Unauthorized:
      description: Invalid or expired grant
    Forbidden:
      description: Missing scope or account consent
    RateLimited:
      description: Rate limit exceeded
      headers:
        Retry-After:
          schema: { type: integer, minimum: 0 }
    ProviderFailure:
      description: Temporary provider-side failure; include Retry-After when retryable
