All findings from the inaugural LeadValidator audit resolved and confirmed. Release gate: PASS. VV_ISSUE_002 (BLOCKER): 15 OpenAPI specs verified present covering all 20 route groups (46 endpoints documented in docs/openapi/) VV_ISSUE_003 (MAJOR): Remove any types from src/db/pool.ts — replaced pool.query shim with unknown[] + Object.defineProperty, zero any types, eslint-disable suppressions removed VV_ISSUE_004 (MAJOR): Remove raw Pool from ScaffoldController and HealthDetailedController — injected AgentRepository/CredentialRepository and DbProbe interface respectively; added CredentialRepository.findActiveClientId() VV_ISSUE_005 (MAJOR): Add unit tests for 5 untested services — ComplianceStatusStore, EventPublisher, MarketplaceService, OIDCTrustPolicyService, UsageService VV_ISSUE_006 (MAJOR): Add integration tests for 7 missing route groups — analytics, billing, tiers, webhooks, marketplace, oidc-trust-policies, oidc-token-exchange VV_ISSUE_001 (MINOR): Create missing design.md and tasks.md in 4 OpenSpec archives — all archives now complete Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
356 lines
12 KiB
YAML
356 lines
12 KiB
YAML
openapi: "3.0.3"
|
|
|
|
info:
|
|
title: SentryAgent.ai — Billing & Usage Metering
|
|
version: 1.0.0
|
|
description: |
|
|
Billing and usage metering endpoints for the SentryAgent.ai AgentIdP platform.
|
|
|
|
Integrates with **Stripe** for subscription and payment management.
|
|
|
|
**Authenticated endpoints** (require Bearer JWT):
|
|
- `POST /billing/checkout` — Create a Stripe Checkout Session for plan upgrades
|
|
- `GET /billing/usage` — Retrieve today's usage summary
|
|
|
|
**Unauthenticated endpoint** (Stripe webhook receiver):
|
|
- `POST /billing/webhook` — Receives Stripe webhook events (raw body + signature verification)
|
|
|
|
**Important:** The `/billing/webhook` endpoint uses `express.raw()` middleware
|
|
to receive the raw request body as a Buffer. Do not apply `express.json()` to this route.
|
|
The `Stripe-Signature` header is required for all webhook deliveries.
|
|
|
|
servers:
|
|
- url: http://localhost:3000/api/v1
|
|
description: Local development server
|
|
- url: https://api.sentryagent.ai/v1
|
|
description: Production server
|
|
|
|
tags:
|
|
- name: Billing Checkout
|
|
description: Stripe Checkout Session management
|
|
- name: Billing Webhook
|
|
description: Stripe webhook event receiver (unauthenticated)
|
|
- name: Usage
|
|
description: Usage metering and reporting
|
|
|
|
components:
|
|
securitySchemes:
|
|
BearerAuth:
|
|
type: http
|
|
scheme: bearer
|
|
bearerFormat: JWT
|
|
description: |
|
|
JWT access token obtained via `POST /token`.
|
|
Include as `Authorization: Bearer <token>`.
|
|
|
|
schemas:
|
|
CheckoutRequest:
|
|
type: object
|
|
description: |
|
|
Optional request body for creating a Stripe Checkout Session.
|
|
When `successUrl` or `cancelUrl` are omitted, the platform generates
|
|
default redirect URLs pointing to the dashboard.
|
|
properties:
|
|
successUrl:
|
|
type: string
|
|
format: uri
|
|
description: URL to redirect to after successful payment.
|
|
example: "https://my-app.example.com/dashboard?billing=success"
|
|
cancelUrl:
|
|
type: string
|
|
format: uri
|
|
description: URL to redirect to if the user cancels checkout.
|
|
example: "https://my-app.example.com/dashboard?billing=cancel"
|
|
|
|
CheckoutResponse:
|
|
type: object
|
|
description: Stripe Checkout Session URL to redirect the user to.
|
|
required:
|
|
- checkoutUrl
|
|
properties:
|
|
checkoutUrl:
|
|
type: string
|
|
format: uri
|
|
description: |
|
|
Stripe-hosted Checkout page URL. Redirect the authenticated user
|
|
to this URL to complete payment.
|
|
example: "https://checkout.stripe.com/pay/cs_test_abcdef1234567890"
|
|
|
|
UsageSummary:
|
|
type: object
|
|
description: |
|
|
Today's usage summary for the authenticated organization.
|
|
Counters reset at UTC midnight.
|
|
required:
|
|
- organizationId
|
|
- date
|
|
- tokensIssued
|
|
- agentsRegistered
|
|
- credentialsGenerated
|
|
properties:
|
|
organizationId:
|
|
type: string
|
|
format: uuid
|
|
description: Organization the usage data belongs to.
|
|
example: "org-1234-5678-abcd-ef01"
|
|
date:
|
|
type: string
|
|
format: date
|
|
description: The calendar date (UTC) this summary covers.
|
|
example: "2026-04-07"
|
|
tokensIssued:
|
|
type: integer
|
|
description: Number of OAuth 2.0 tokens issued today.
|
|
minimum: 0
|
|
example: 4201
|
|
agentsRegistered:
|
|
type: integer
|
|
description: Number of new agents registered today.
|
|
minimum: 0
|
|
example: 3
|
|
credentialsGenerated:
|
|
type: integer
|
|
description: Number of new agent credentials generated today.
|
|
minimum: 0
|
|
example: 5
|
|
apiCallsTotal:
|
|
type: integer
|
|
description: Total API calls across all endpoints today.
|
|
minimum: 0
|
|
example: 12450
|
|
|
|
StripeWebhookResponse:
|
|
type: object
|
|
description: Acknowledgement response for a received Stripe webhook event.
|
|
required:
|
|
- received
|
|
properties:
|
|
received:
|
|
type: boolean
|
|
description: Always `true` when the webhook was processed successfully.
|
|
example: true
|
|
|
|
ErrorResponse:
|
|
type: object
|
|
description: Standard error response envelope.
|
|
required:
|
|
- code
|
|
- message
|
|
properties:
|
|
code:
|
|
type: string
|
|
example: "VALIDATION_ERROR"
|
|
message:
|
|
type: string
|
|
example: "Missing Stripe-Signature header."
|
|
details:
|
|
type: object
|
|
additionalProperties: true
|
|
|
|
responses:
|
|
Unauthorized:
|
|
description: Missing or invalid Bearer token.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
example:
|
|
code: "UNAUTHORIZED"
|
|
message: "A valid Bearer token is required to access this resource."
|
|
|
|
Forbidden:
|
|
description: Valid token but insufficient permissions.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
example:
|
|
code: "FORBIDDEN"
|
|
message: "You do not have permission to perform this action."
|
|
|
|
InternalServerError:
|
|
description: Unexpected server error.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
example:
|
|
code: "INTERNAL_SERVER_ERROR"
|
|
message: "An unexpected error occurred. Please try again later."
|
|
|
|
paths:
|
|
/billing/checkout:
|
|
post:
|
|
operationId: createBillingCheckoutSession
|
|
tags:
|
|
- Billing Checkout
|
|
summary: Create a Stripe Checkout Session
|
|
description: |
|
|
Creates a Stripe Checkout Session for the authenticated organization
|
|
to upgrade their subscription plan.
|
|
|
|
The organization ID is read from the `organization_id` claim in the
|
|
Bearer JWT — the caller does not need to provide it in the request body.
|
|
|
|
The `checkoutUrl` in the response is a Stripe-hosted checkout page.
|
|
Redirect the authenticated user to this URL to complete payment.
|
|
|
|
Requires a valid Bearer JWT. The `organization_id` claim must be present in the token.
|
|
security:
|
|
- BearerAuth: []
|
|
requestBody:
|
|
required: false
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/CheckoutRequest'
|
|
example:
|
|
successUrl: "https://my-app.example.com/dashboard?billing=success"
|
|
cancelUrl: "https://my-app.example.com/dashboard?billing=cancel"
|
|
responses:
|
|
'201':
|
|
description: Stripe Checkout Session created successfully.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/CheckoutResponse'
|
|
example:
|
|
checkoutUrl: "https://checkout.stripe.com/pay/cs_test_abcdef1234567890"
|
|
'400':
|
|
description: Validation error — organization_id missing from token.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
example:
|
|
code: "VALIDATION_ERROR"
|
|
message: "organization_id is required in token."
|
|
'401':
|
|
$ref: '#/components/responses/Unauthorized'
|
|
'403':
|
|
$ref: '#/components/responses/Forbidden'
|
|
'500':
|
|
description: Unexpected error or Stripe API error.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
example:
|
|
code: "STRIPE_ERROR"
|
|
message: "Failed to create Stripe Checkout Session. Please try again."
|
|
|
|
/billing/webhook:
|
|
post:
|
|
operationId: handleStripeWebhook
|
|
tags:
|
|
- Billing Webhook
|
|
summary: Receive Stripe webhook events
|
|
description: |
|
|
Receives webhook events from Stripe's delivery system. This endpoint is
|
|
**unauthenticated** — authentication is provided by Stripe's HMAC signature
|
|
in the `Stripe-Signature` header.
|
|
|
|
**Body format:** The request body MUST be the raw JSON payload as sent by
|
|
Stripe (not parsed JSON). The `Content-Type` is `application/json` but
|
|
the body is read as a raw `Buffer` for signature verification.
|
|
|
|
**Signature verification:** The `Stripe-Signature` header is required.
|
|
If absent or invalid, the request is rejected with `400`.
|
|
|
|
**Supported events processed:**
|
|
- `checkout.session.completed` — Activates subscription after payment
|
|
- `customer.subscription.deleted` — Downgrades plan on cancellation
|
|
- `invoice.payment_failed` — Handles failed renewals
|
|
security: []
|
|
parameters:
|
|
- name: Stripe-Signature
|
|
in: header
|
|
required: true
|
|
description: |
|
|
HMAC signature from Stripe for payload verification.
|
|
Format: `t=<timestamp>,v1=<signature>,...`
|
|
schema:
|
|
type: string
|
|
example: "t=1492774577,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a05bd412fbc2a2bzo..."
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
description: Raw Stripe event payload (read as Buffer internally).
|
|
additionalProperties: true
|
|
example:
|
|
id: "evt_1234567890"
|
|
object: "event"
|
|
type: "checkout.session.completed"
|
|
data:
|
|
object:
|
|
id: "cs_test_abcdef1234567890"
|
|
customer: "cus_abc123"
|
|
responses:
|
|
'200':
|
|
description: Webhook event received and processed successfully.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/StripeWebhookResponse'
|
|
example:
|
|
received: true
|
|
'400':
|
|
description: Missing or invalid Stripe-Signature header, or malformed payload.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
examples:
|
|
missingSignature:
|
|
summary: Missing Stripe-Signature header
|
|
value:
|
|
code: "VALIDATION_ERROR"
|
|
message: "Missing Stripe-Signature header."
|
|
invalidSignature:
|
|
summary: Signature verification failed
|
|
value:
|
|
code: "STRIPE_SIGNATURE_INVALID"
|
|
message: "Webhook signature verification failed."
|
|
'500':
|
|
$ref: '#/components/responses/InternalServerError'
|
|
|
|
/billing/usage:
|
|
get:
|
|
operationId: getBillingUsage
|
|
tags:
|
|
- Usage
|
|
summary: Get today's usage summary
|
|
description: |
|
|
Returns the usage summary for the authenticated organization
|
|
for the current calendar day (UTC).
|
|
|
|
Usage counters reset at UTC midnight.
|
|
The `organization_id` claim is read from the Bearer JWT.
|
|
|
|
Requires a valid Bearer JWT.
|
|
security:
|
|
- BearerAuth: []
|
|
responses:
|
|
'200':
|
|
description: Usage summary returned successfully.
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/UsageSummary'
|
|
example:
|
|
organizationId: "org-1234-5678-abcd-ef01"
|
|
date: "2026-04-07"
|
|
tokensIssued: 4201
|
|
agentsRegistered: 3
|
|
credentialsGenerated: 5
|
|
apiCallsTotal: 12450
|
|
'401':
|
|
$ref: '#/components/responses/Unauthorized'
|
|
'403':
|
|
$ref: '#/components/responses/Forbidden'
|
|
'500':
|
|
$ref: '#/components/responses/InternalServerError'
|