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 `. 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=,v1=,...` 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'