openapi: "3.0.3" info: title: SentryAgent.ai — Webhooks & Event Subscriptions version: 1.0.0 description: | Webhook subscription management and delivery history endpoints for the SentryAgent.ai AgentIdP platform. Webhooks deliver real-time event notifications to registered HTTP endpoints when significant platform events occur (agent lifecycle changes, credential operations, token events). **All endpoints require a valid Bearer JWT** with appropriate scope: - `webhooks:read` — required for GET operations - `webhooks:write` — required for POST, PATCH, DELETE operations **Delivery mechanism:** - Events are delivered via `POST` to the registered `url` - Each delivery carries a signed JSON envelope (`X-SentryAgent-Signature` header) - Failed deliveries are retried with exponential backoff (up to 10 attempts) - After 10 failures the subscription is marked `dead_letter` **Supported event types:** `agent.created`, `agent.updated`, `agent.suspended`, `agent.reactivated`, `agent.decommissioned`, `credential.generated`, `credential.rotated`, `credential.revoked`, `token.issued`, `token.revoked` servers: - url: http://localhost:3000/api/v1 description: Local development server - url: https://api.sentryagent.ai/v1 description: Production server tags: - name: Webhook Subscriptions description: Create and manage webhook endpoint subscriptions - name: Webhook Deliveries description: Query delivery history and attempt records components: securitySchemes: BearerAuth: type: http scheme: bearer bearerFormat: JWT description: | JWT access token obtained via `POST /token`. Include as `Authorization: Bearer `. schemas: WebhookEventType: type: string enum: - agent.created - agent.updated - agent.suspended - agent.reactivated - agent.decommissioned - credential.generated - credential.rotated - credential.revoked - token.issued - token.revoked description: Platform event type that can be subscribed to. example: agent.created WebhookDeliveryStatus: type: string enum: - pending - delivered - failed - dead_letter description: | Current status of a delivery attempt. - `pending` — queued for delivery or awaiting retry - `delivered` — successfully delivered (HTTP 2xx from target) - `failed` — delivery failed; retry scheduled - `dead_letter` — all retries exhausted; no further attempts example: delivered WebhookSubscription: type: object description: A registered webhook subscription (signing secret never included in responses). required: - id - organization_id - name - url - events - active - failure_count - created_at - updated_at properties: id: type: string format: uuid description: Immutable system-assigned UUID for this subscription. readOnly: true example: "wh-abcd-1234-5678-ef01" organization_id: type: string format: uuid description: Organization that owns this subscription. readOnly: true example: "org-1234-5678-abcd-ef01" name: type: string description: Human-readable label for this subscription. example: "Agent lifecycle events" url: type: string format: uri description: HTTPS endpoint that receives webhook payloads. example: "https://my-app.example.com/webhooks/sentryagent" events: type: array items: $ref: '#/components/schemas/WebhookEventType' description: List of event types this subscription receives. minItems: 1 example: - agent.created - agent.decommissioned active: type: boolean description: Whether the subscription is currently active. Set to false to pause delivery. example: true failure_count: type: integer description: Number of consecutive delivery failures since last success. minimum: 0 readOnly: true example: 0 created_at: type: string format: date-time readOnly: true example: "2026-03-01T08:00:00.000Z" updated_at: type: string format: date-time readOnly: true example: "2026-03-28T11:30:00.000Z" CreateWebhookRequest: type: object description: Request body for creating a new webhook subscription. required: - name - url - events properties: name: type: string description: Human-readable label for this subscription. minLength: 1 maxLength: 256 example: "Agent lifecycle events" url: type: string format: uri description: HTTPS endpoint URL. Must be HTTPS in production. example: "https://my-app.example.com/webhooks/sentryagent" events: type: array items: $ref: '#/components/schemas/WebhookEventType' minItems: 1 description: Event types to subscribe to. example: - agent.created - agent.decommissioned UpdateWebhookRequest: type: object description: | Request body for partially updating a webhook subscription. All fields are optional; only provided fields are updated. minProperties: 1 properties: name: type: string minLength: 1 maxLength: 256 example: "Agent events (updated)" url: type: string format: uri example: "https://my-app.example.com/webhooks/v2" events: type: array items: $ref: '#/components/schemas/WebhookEventType' minItems: 1 example: - agent.created - agent.updated - token.issued active: type: boolean description: Set to false to pause delivery without deleting the subscription. example: true WebhookDelivery: type: object description: A single webhook delivery attempt record. required: - id - subscription_id - event_type - payload - status - attempt_count - created_at - updated_at properties: id: type: string format: uuid readOnly: true example: "del-abcd-1234-5678-ef01" subscription_id: type: string format: uuid example: "wh-abcd-1234-5678-ef01" event_type: $ref: '#/components/schemas/WebhookEventType' payload: type: object description: The JSON payload that was sent (or attempted) to the target URL. additionalProperties: true example: id: "evt-1234-5678" event: "agent.created" timestamp: "2026-04-07T09:00:00.000Z" organization_id: "org-1234-5678-abcd-ef01" data: agentId: "a1b2c3d4-e5f6-7890-abcd-ef1234567890" status: $ref: '#/components/schemas/WebhookDeliveryStatus' http_status_code: type: integer nullable: true description: HTTP status code returned by the target endpoint (null if connection failed). example: 200 attempt_count: type: integer description: Number of delivery attempts made so far. minimum: 1 example: 1 next_retry_at: type: string format: date-time nullable: true description: Scheduled time for the next retry attempt. Null if delivered or dead-lettered. example: null delivered_at: type: string format: date-time nullable: true description: Timestamp when the delivery was successfully confirmed. example: "2026-04-07T09:00:05.000Z" created_at: type: string format: date-time readOnly: true example: "2026-04-07T09:00:00.000Z" updated_at: type: string format: date-time readOnly: true example: "2026-04-07T09:00:05.000Z" PaginatedDeliveriesResponse: type: object description: Paginated delivery history response. required: - deliveries - total - limit - offset properties: deliveries: type: array items: $ref: '#/components/schemas/WebhookDelivery' total: type: integer example: 87 limit: type: integer example: 20 offset: type: integer example: 0 ErrorResponse: type: object description: Standard error response envelope. required: - code - message properties: code: type: string example: "WEBHOOK_NOT_FOUND" message: type: string example: "Webhook subscription with the specified ID was not found." 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 scope. content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: code: "FORBIDDEN" message: "You do not have permission to perform this action." NotFound: description: Webhook subscription not found. content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: code: "WEBHOOK_NOT_FOUND" message: "Webhook subscription with the specified ID was not found." 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." security: - BearerAuth: [] paths: /webhooks: post: operationId: createWebhookSubscription tags: - Webhook Subscriptions summary: Create a webhook subscription description: | Creates a new webhook subscription for the authenticated organization. A signing secret is generated automatically and returned once in the response under a `signingSecret` field. **Store this secret securely — it cannot be retrieved again.** Requires `webhooks:write` scope. requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateWebhookRequest' example: name: "Agent lifecycle events" url: "https://my-app.example.com/webhooks/sentryagent" events: - agent.created - agent.decommissioned responses: '201': description: Webhook subscription created successfully. content: application/json: schema: allOf: - $ref: '#/components/schemas/WebhookSubscription' - type: object properties: signingSecret: type: string description: | HMAC signing secret for verifying webhook payloads. Returned only once at creation time. example: "whsec_abcdef1234567890abcdef1234567890" example: id: "wh-abcd-1234-5678-ef01" organization_id: "org-1234-5678-abcd-ef01" name: "Agent lifecycle events" url: "https://my-app.example.com/webhooks/sentryagent" events: - agent.created - agent.decommissioned active: true failure_count: 0 created_at: "2026-04-07T09:00:00.000Z" updated_at: "2026-04-07T09:00:00.000Z" signingSecret: "whsec_abcdef1234567890abcdef1234567890" '400': description: Validation error in request body. content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: code: "VALIDATION_ERROR" message: "Request validation failed." details: field: "url" reason: "Must be a valid HTTPS URL." '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' get: operationId: listWebhookSubscriptions tags: - Webhook Subscriptions summary: List webhook subscriptions description: | Returns all webhook subscriptions for the authenticated organization. Requires `webhooks:read` scope. responses: '200': description: List of webhook subscriptions returned successfully. content: application/json: schema: type: array items: $ref: '#/components/schemas/WebhookSubscription' example: - id: "wh-abcd-1234-5678-ef01" organization_id: "org-1234-5678-abcd-ef01" name: "Agent lifecycle events" url: "https://my-app.example.com/webhooks/sentryagent" events: - agent.created - agent.decommissioned active: true failure_count: 0 created_at: "2026-03-01T08:00:00.000Z" updated_at: "2026-03-01T08:00:00.000Z" '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' /webhooks/{id}: parameters: - name: id in: path required: true description: UUID of the webhook subscription. schema: type: string format: uuid example: "wh-abcd-1234-5678-ef01" get: operationId: getWebhookSubscription tags: - Webhook Subscriptions summary: Get a webhook subscription by ID description: | Returns a single webhook subscription record. Requires `webhooks:read` scope. responses: '200': description: Webhook subscription returned successfully. content: application/json: schema: $ref: '#/components/schemas/WebhookSubscription' example: id: "wh-abcd-1234-5678-ef01" organization_id: "org-1234-5678-abcd-ef01" name: "Agent lifecycle events" url: "https://my-app.example.com/webhooks/sentryagent" events: - agent.created - agent.decommissioned active: true failure_count: 0 created_at: "2026-03-01T08:00:00.000Z" updated_at: "2026-03-28T11:30:00.000Z" '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' patch: operationId: updateWebhookSubscription tags: - Webhook Subscriptions summary: Update a webhook subscription description: | Partially updates a webhook subscription. Only provided fields are updated. Set `active: false` to pause delivery without deleting the subscription. Requires `webhooks:write` scope. requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UpdateWebhookRequest' example: active: false responses: '200': description: Webhook subscription updated successfully. content: application/json: schema: $ref: '#/components/schemas/WebhookSubscription' example: id: "wh-abcd-1234-5678-ef01" organization_id: "org-1234-5678-abcd-ef01" name: "Agent lifecycle events" url: "https://my-app.example.com/webhooks/sentryagent" events: - agent.created - agent.decommissioned active: false failure_count: 0 created_at: "2026-03-01T08:00:00.000Z" updated_at: "2026-04-07T09:00:00.000Z" '400': description: Validation error in request body. content: application/json: schema: $ref: '#/components/schemas/ErrorResponse' example: code: "VALIDATION_ERROR" message: "Request validation failed." '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' delete: operationId: deleteWebhookSubscription tags: - Webhook Subscriptions summary: Delete a webhook subscription description: | Permanently deletes a webhook subscription and stops all future deliveries. Any pending deliveries in the queue are cancelled. Requires `webhooks:write` scope. responses: '204': description: Webhook subscription deleted successfully. No response body. '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError' /webhooks/{id}/deliveries: parameters: - name: id in: path required: true description: UUID of the webhook subscription. schema: type: string format: uuid example: "wh-abcd-1234-5678-ef01" get: operationId: listWebhookDeliveries tags: - Webhook Deliveries summary: List delivery history for a subscription description: | Returns the delivery history for a webhook subscription, ordered by `created_at` descending (most recent first). Use this endpoint to diagnose delivery failures and inspect payload content for historical events. Requires `webhooks:read` scope. parameters: - name: limit in: query required: false schema: type: integer minimum: 1 maximum: 100 default: 20 example: 20 - name: offset in: query required: false schema: type: integer minimum: 0 default: 0 example: 0 - name: status in: query required: false schema: $ref: '#/components/schemas/WebhookDeliveryStatus' description: Filter deliveries by status. responses: '200': description: Delivery history returned successfully. content: application/json: schema: $ref: '#/components/schemas/PaginatedDeliveriesResponse' example: deliveries: - id: "del-abcd-1234-5678-ef01" subscription_id: "wh-abcd-1234-5678-ef01" event_type: "agent.created" payload: id: "evt-1234-5678" event: "agent.created" timestamp: "2026-04-07T09:00:00.000Z" organization_id: "org-1234-5678-abcd-ef01" data: agentId: "a1b2c3d4-e5f6-7890-abcd-ef1234567890" status: "delivered" http_status_code: 200 attempt_count: 1 next_retry_at: null delivered_at: "2026-04-07T09:00:05.000Z" created_at: "2026-04-07T09:00:00.000Z" updated_at: "2026-04-07T09:00:05.000Z" total: 87 limit: 20 offset: 0 '401': $ref: '#/components/responses/Unauthorized' '403': $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': $ref: '#/components/responses/InternalServerError'