CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-io-swagger-core-v3--swagger-annotations-jakarta

Jakarta EE compatible OpenAPI 3.x annotations for defining REST API specifications

Pending
Overview
Eval results
Files

callbacks.mddocs/

Callbacks and Webhooks

Advanced callback operations for asynchronous API interactions and webhook definitions supporting OpenAPI 3.1 features. This system enables documentation of event-driven API interactions, asynchronous callbacks, and webhook-based communication patterns.

Capabilities

Callback Operations

Defines callback operations that describe requests initiated by the server based on specific events or conditions.

/**
 * Defines callback operation for asynchronous interactions
 * Applied to: METHOD, TYPE
 * Repeatable: Yes
 */
@Callback(
    name = "statusUpdateCallback",                      // Callback name (required)
    callbackUrlExpression = "{$request.body#/callbackUrl}", // URL expression (required)
    operation = @Operation(                             // Callback operation definition
        method = "POST",
        summary = "Status update notification",
        description = "Called when status changes",
        requestBody = @RequestBody(
            content = @Content(
                mediaType = "application/json",
                schema = @Schema(implementation = StatusUpdateEvent.class)
            )
        ),
        responses = @ApiResponse(responseCode = "200", description = "Acknowledged")
    ),
    extensions = {@Extension(...)}                      // Custom extensions
)

Callback URL Expressions:

// Request body field
callbackUrlExpression = "{$request.body#/callbackUrl}"

// Request header
callbackUrlExpression = "{$request.header.X-Callback-URL}"

// Query parameter
callbackUrlExpression = "{$request.query.callback}"

// Static URL with parameters
callbackUrlExpression = "https://api.example.com/callbacks/{$request.body#/id}"

// Complex expression
callbackUrlExpression = "{$request.body#/baseUrl}/webhooks/{$request.body#/eventType}"

Usage Examples:

// Payment processing callback
@POST
@Path("/payments")
@Operation(summary = "Process payment")
@Callback(
    name = "paymentStatusCallback",
    callbackUrlExpression = "{$request.body#/statusWebhookUrl}",
    operation = @Operation(
        method = "POST",
        summary = "Payment status update",
        description = "Called when payment status changes",
        requestBody = @RequestBody(
            description = "Payment status event",
            content = @Content(
                mediaType = "application/json",
                schema = @Schema(implementation = PaymentStatusEvent.class),
                examples = @ExampleObject(
                    name = "paymentCompleted",
                    value = """
                        {
                          "paymentId": "pay_123456",
                          "status": "completed",
                          "amount": 99.99,
                          "timestamp": "2023-12-01T10:30:00Z"
                        }
                        """
                )
            )
        ),
        responses = {
            @ApiResponse(responseCode = "200", description = "Callback acknowledged"),
            @ApiResponse(responseCode = "404", description = "Callback endpoint not found"),
            @ApiResponse(responseCode = "500", description = "Callback processing failed")
        }
    )
)
public Response processPayment(@RequestBody PaymentRequest request) {}

// Order fulfillment callbacks
@PUT
@Path("/orders/{id}/fulfill")
@Operation(summary = "Fulfill order")
@Callbacks({
    @Callback(
        name = "orderShipped",
        callbackUrlExpression = "{$request.body#/shippingWebhook}",
        operation = @Operation(
            method = "POST",
            summary = "Order shipped notification",
            requestBody = @RequestBody(
                content = @Content(schema = @Schema(implementation = ShippingEvent.class))
            )
        )
    ),
    @Callback(
        name = "orderDelivered",
        callbackUrlExpression = "{$request.body#/deliveryWebhook}",
        operation = @Operation(
            method = "POST", 
            summary = "Order delivered notification",
            requestBody = @RequestBody(
                content = @Content(schema = @Schema(implementation = DeliveryEvent.class))
            )
        )
    )
})
public Response fulfillOrder(@PathParam("id") Long orderId, @RequestBody FulfillmentRequest request) {}

// User registration callback with verification
@POST
@Path("/users/register")
@Operation(summary = "Register new user")
@Callback(
    name = "emailVerificationCallback",
    callbackUrlExpression = "{$request.body#/verificationCallbackUrl}",
    operation = @Operation(
        method = "POST",
        summary = "Email verification result",
        description = "Called after user clicks verification link",
        requestBody = @RequestBody(
            content = @Content(
                mediaType = "application/json",
                schema = @Schema(implementation = VerificationEvent.class)
            )
        ),
        responses = @ApiResponse(responseCode = "200", description = "Verification processed"),
        security = @SecurityRequirement(name = "callbackToken")
    )
)
public Response registerUser(@RequestBody UserRegistrationRequest request) {}

// File processing callback with progress updates
@POST
@Path("/files/process")
@Operation(summary = "Process uploaded file")
@Callback(
    name = "processingProgressCallback",
    callbackUrlExpression = "{$request.body#/progressUrl}",
    operation = @Operation(
        method = "POST",
        summary = "File processing progress update",
        requestBody = @RequestBody(
            content = @Content(
                schema = @Schema(implementation = ProcessingProgressEvent.class)
            )
        ),
        responses = @ApiResponse(responseCode = "200", description = "Progress update received")
    )
)
public Response processFile(@RequestBody FileProcessingRequest request) {}

Callbacks Container

Container for multiple callback definitions on a single operation.

/**
 * Container for multiple Callback annotations
 */
@Callbacks({
    @Callback(name = "success", callbackUrlExpression = "{$request.body#/successUrl}", operation = @Operation(...)),
    @Callback(name = "failure", callbackUrlExpression = "{$request.body#/failureUrl}", operation = @Operation(...)),
    @Callback(name = "progress", callbackUrlExpression = "{$request.body#/progressUrl}", operation = @Operation(...))
})

Webhook Support (OpenAPI 3.1)

Defines webhook operations for event-driven API interactions using OpenAPI 3.1 webhook specification.

/**
 * Defines webhook operation (OpenAPI 3.1)
 * Applied to: TYPE, METHOD
 * Repeatable: Yes
 */
@Webhook(
    name = "userCreated",                              // Webhook name (required)
    operation = @Operation(                            // Webhook operation definition
        method = "POST",
        summary = "User created notification",
        description = "Sent when a new user is created",
        requestBody = @RequestBody(
            description = "User creation event data",
            content = @Content(
                mediaType = "application/json",
                schema = @Schema(implementation = UserCreatedEvent.class)
            )
        ),
        responses = {
            @ApiResponse(responseCode = "200", description = "Webhook received successfully"),
            @ApiResponse(responseCode = "410", description = "Webhook endpoint no longer available")
        },
        security = @SecurityRequirement(name = "webhookSignature")
    ),
    extensions = {@Extension(...)}                     // Custom extensions
)

Usage Examples:

// Application-level webhooks
@OpenAPIDefinition(
    info = @Info(title = "E-commerce API", version = "1.0"),
    webhooks = {
        @Webhook(
            name = "orderCreated",
            operation = @Operation(
                method = "POST",
                summary = "Order created webhook",
                requestBody = @RequestBody(
                    content = @Content(schema = @Schema(implementation = OrderCreatedEvent.class))
                )
            )
        ),
        @Webhook(
            name = "paymentProcessed", 
            operation = @Operation(
                method = "POST",
                summary = "Payment processed webhook",
                requestBody = @RequestBody(
                    content = @Content(schema = @Schema(implementation = PaymentProcessedEvent.class))
                )
            )
        )
    }
)
public class ECommerceApplication {}

// Resource-specific webhooks
@Path("/subscriptions")
@Webhooks({
    @Webhook(
        name = "subscriptionActivated",
        operation = @Operation(
            method = "POST",
            summary = "Subscription activated",
            requestBody = @RequestBody(
                content = @Content(schema = @Schema(implementation = SubscriptionEvent.class))
            ),
            security = @SecurityRequirement(name = "webhookSignature")
        )
    ),
    @Webhook(
        name = "subscriptionCancelled",
        operation = @Operation(
            method = "POST",
            summary = "Subscription cancelled",
            requestBody = @RequestBody(
                content = @Content(schema = @Schema(implementation = SubscriptionEvent.class))
            )
        )
    )
})
public class SubscriptionResource {}

// User lifecycle webhooks
@Webhook(
    name = "userStatusChanged",
    operation = @Operation(
        method = "POST",
        summary = "User status change notification",
        description = "Triggered when user status changes (active, suspended, deleted)",
        requestBody = @RequestBody(
            description = "User status change event",
            content = @Content(
                mediaType = "application/json",
                schema = @Schema(implementation = UserStatusChangeEvent.class),
                examples = {
                    @ExampleObject(
                        name = "userSuspended",
                        summary = "User account suspended",
                        value = """
                            {
                              "eventType": "user.status.changed",
                              "userId": 12345,
                              "previousStatus": "active",
                              "newStatus": "suspended",
                              "reason": "Terms of service violation",
                              "timestamp": "2023-12-01T15:30:00Z"
                            }
                            """
                    ),
                    @ExampleObject(
                        name = "userDeleted",
                        summary = "User account deleted", 
                        value = """
                            {
                              "eventType": "user.status.changed",
                              "userId": 12345,
                              "previousStatus": "active",
                              "newStatus": "deleted",
                              "reason": "User requested account deletion",
                              "timestamp": "2023-12-01T16:45:00Z"
                            }
                            """
                    )
                }
            )
        ),
        responses = {
            @ApiResponse(
                responseCode = "200",
                description = "Webhook processed successfully"
            ),
            @ApiResponse(
                responseCode = "401",
                description = "Invalid webhook signature"
            ),
            @ApiResponse(
                responseCode = "410",
                description = "Webhook endpoint no longer exists"
            )
        },
        security = @SecurityRequirement(name = "webhookSignature"),
        extensions = {
            @Extension(
                name = "x-webhook-metadata",
                properties = {
                    @ExtensionProperty(name = "retry-policy", value = "exponential-backoff"),
                    @ExtensionProperty(name = "max-retries", value = "5"),
                    @ExtensionProperty(name = "timeout", value = "30s")
                }
            )
        }
    )
)

Webhooks Container

Container for multiple webhook definitions.

/**
 * Container for multiple Webhook annotations
 */
@Webhooks({
    @Webhook(name = "created", operation = @Operation(...)),
    @Webhook(name = "updated", operation = @Operation(...)),
    @Webhook(name = "deleted", operation = @Operation(...))
})

Advanced Patterns

Conditional Callbacks

@POST
@Path("/orders")
@Operation(summary = "Create order with conditional callbacks")
@Callbacks({
    @Callback(
        name = "fulfillmentCallback",
        callbackUrlExpression = "{$request.body#/fulfillmentWebhook}",  
        operation = @Operation(
            method = "POST",
            summary = "Order fulfillment update",
            description = "Called only for physical products requiring fulfillment"
        )
    ),
    @Callback(
        name = "digitalDeliveryCallback",
        callbackUrlExpression = "{$request.body#/deliveryWebhook}",
        operation = @Operation(
            method = "POST",
            summary = "Digital product delivery",
            description = "Called only for digital products"
        )
    )
})
public Response createOrder(@RequestBody OrderRequest request) {}

Webhook Security Patterns

@Webhook(
    name = "secureWebhook",
    operation = @Operation(
        method = "POST",
        summary = "Secure webhook with signature verification",
        security = {
            @SecurityRequirement(name = "webhookSignature"),
            @SecurityRequirement(name = "bearerToken")
        },
        extensions = {
            @Extension(
                name = "x-webhook-security",
                properties = {
                    @ExtensionProperty(name = "signature-header", value = "X-Webhook-Signature"),
                    @ExtensionProperty(name = "signature-algorithm", value = "HMAC-SHA256"),
                    @ExtensionProperty(name = "timestamp-header", value = "X-Webhook-Timestamp"),
                    @ExtensionProperty(name = "timestamp-tolerance", value = "300")
                }
            )
        }
    )
)

Error Handling in Callbacks

@Callback(
    name = "robustCallback",
    callbackUrlExpression = "{$request.body#/callbackUrl}",
    operation = @Operation(
        method = "POST",
        summary = "Callback with comprehensive error handling",
        responses = {
            @ApiResponse(responseCode = "200", description = "Success"),
            @ApiResponse(responseCode = "400", description = "Invalid callback data"),
            @ApiResponse(responseCode = "401", description = "Unauthorized callback"),
            @ApiResponse(responseCode = "404", description = "Callback endpoint not found"),
            @ApiResponse(responseCode = "408", description = "Callback timeout"),
            @ApiResponse(responseCode = "410", description = "Callback endpoint permanently unavailable"),
            @ApiResponse(responseCode = "429", description = "Too many callback attempts"),
            @ApiResponse(responseCode = "500", description = "Callback endpoint error")
        },
        extensions = {
            @Extension(
                name = "x-callback-retry",
                properties = {
                    @ExtensionProperty(name = "max-attempts", value = "5"),
                    @ExtensionProperty(name = "backoff-strategy", value = "exponential"),
                    @ExtensionProperty(name = "initial-delay", value = "1s"),
                    @ExtensionProperty(name = "max-delay", value = "300s")
                }
            )
        }
    )
)

Event Schema Examples

Callback Event Schemas

@Schema(description = "Payment status update event")
public class PaymentStatusEvent {
    @Schema(description = "Payment identifier", example = "pay_123456")
    private String paymentId;
    
    @Schema(description = "Payment status", allowableValues = {"pending", "completed", "failed", "cancelled"})
    private String status;
    
    @Schema(description = "Payment amount", example = "99.99")
    private BigDecimal amount;
    
    @Schema(description = "Status change timestamp", format = "date-time")
    private Instant timestamp;
    
    @Schema(description = "Optional failure reason")
    private String failureReason;
}

@Schema(description = "User status change event")
public class UserStatusChangeEvent {
    @Schema(description = "Event type", example = "user.status.changed")
    private String eventType;
    
    @Schema(description = "User ID", example = "12345")
    private Long userId;
    
    @Schema(description = "Previous status", example = "active")
    private String previousStatus;
    
    @Schema(description = "New status", example = "suspended")
    private String newStatus;
    
    @Schema(description = "Reason for status change")
    private String reason;
    
    @Schema(description = "Change timestamp", format = "date-time")
    private Instant timestamp;
}

Install with Tessl CLI

npx tessl i tessl/maven-io-swagger-core-v3--swagger-annotations-jakarta

docs

callbacks.md

core-annotations.md

enums.md

index.md

links.md

responses.md

schema-media.md

security.md

server-info.md

tile.json