or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

callbacks.mdcore-annotations.mdenums.mdindex.mdlinks.mdresponses.mdschema-media.mdsecurity.mdserver-info.md

callbacks.mddocs/

0

# Callbacks and Webhooks

1

2

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.

3

4

## Capabilities

5

6

### Callback Operations

7

8

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

9

10

```java { .api }

11

/**

12

* Defines callback operation for asynchronous interactions

13

* Applied to: METHOD, TYPE

14

* Repeatable: Yes

15

*/

16

@Callback(

17

name = "statusUpdateCallback", // Callback name (required)

18

callbackUrlExpression = "{$request.body#/callbackUrl}", // URL expression (required)

19

operation = @Operation( // Callback operation definition

20

method = "POST",

21

summary = "Status update notification",

22

description = "Called when status changes",

23

requestBody = @RequestBody(

24

content = @Content(

25

mediaType = "application/json",

26

schema = @Schema(implementation = StatusUpdateEvent.class)

27

)

28

),

29

responses = @ApiResponse(responseCode = "200", description = "Acknowledged")

30

),

31

extensions = {@Extension(...)} // Custom extensions

32

)

33

```

34

35

**Callback URL Expressions:**

36

37

```java

38

// Request body field

39

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

40

41

// Request header

42

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

43

44

// Query parameter

45

callbackUrlExpression = "{$request.query.callback}"

46

47

// Static URL with parameters

48

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

49

50

// Complex expression

51

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

52

```

53

54

**Usage Examples:**

55

56

```java

57

// Payment processing callback

58

@POST

59

@Path("/payments")

60

@Operation(summary = "Process payment")

61

@Callback(

62

name = "paymentStatusCallback",

63

callbackUrlExpression = "{$request.body#/statusWebhookUrl}",

64

operation = @Operation(

65

method = "POST",

66

summary = "Payment status update",

67

description = "Called when payment status changes",

68

requestBody = @RequestBody(

69

description = "Payment status event",

70

content = @Content(

71

mediaType = "application/json",

72

schema = @Schema(implementation = PaymentStatusEvent.class),

73

examples = @ExampleObject(

74

name = "paymentCompleted",

75

value = """

76

{

77

"paymentId": "pay_123456",

78

"status": "completed",

79

"amount": 99.99,

80

"timestamp": "2023-12-01T10:30:00Z"

81

}

82

"""

83

)

84

)

85

),

86

responses = {

87

@ApiResponse(responseCode = "200", description = "Callback acknowledged"),

88

@ApiResponse(responseCode = "404", description = "Callback endpoint not found"),

89

@ApiResponse(responseCode = "500", description = "Callback processing failed")

90

}

91

)

92

)

93

public Response processPayment(@RequestBody PaymentRequest request) {}

94

95

// Order fulfillment callbacks

96

@PUT

97

@Path("/orders/{id}/fulfill")

98

@Operation(summary = "Fulfill order")

99

@Callbacks({

100

@Callback(

101

name = "orderShipped",

102

callbackUrlExpression = "{$request.body#/shippingWebhook}",

103

operation = @Operation(

104

method = "POST",

105

summary = "Order shipped notification",

106

requestBody = @RequestBody(

107

content = @Content(schema = @Schema(implementation = ShippingEvent.class))

108

)

109

)

110

),

111

@Callback(

112

name = "orderDelivered",

113

callbackUrlExpression = "{$request.body#/deliveryWebhook}",

114

operation = @Operation(

115

method = "POST",

116

summary = "Order delivered notification",

117

requestBody = @RequestBody(

118

content = @Content(schema = @Schema(implementation = DeliveryEvent.class))

119

)

120

)

121

)

122

})

123

public Response fulfillOrder(@PathParam("id") Long orderId, @RequestBody FulfillmentRequest request) {}

124

125

// User registration callback with verification

126

@POST

127

@Path("/users/register")

128

@Operation(summary = "Register new user")

129

@Callback(

130

name = "emailVerificationCallback",

131

callbackUrlExpression = "{$request.body#/verificationCallbackUrl}",

132

operation = @Operation(

133

method = "POST",

134

summary = "Email verification result",

135

description = "Called after user clicks verification link",

136

requestBody = @RequestBody(

137

content = @Content(

138

mediaType = "application/json",

139

schema = @Schema(implementation = VerificationEvent.class)

140

)

141

),

142

responses = @ApiResponse(responseCode = "200", description = "Verification processed"),

143

security = @SecurityRequirement(name = "callbackToken")

144

)

145

)

146

public Response registerUser(@RequestBody UserRegistrationRequest request) {}

147

148

// File processing callback with progress updates

149

@POST

150

@Path("/files/process")

151

@Operation(summary = "Process uploaded file")

152

@Callback(

153

name = "processingProgressCallback",

154

callbackUrlExpression = "{$request.body#/progressUrl}",

155

operation = @Operation(

156

method = "POST",

157

summary = "File processing progress update",

158

requestBody = @RequestBody(

159

content = @Content(

160

schema = @Schema(implementation = ProcessingProgressEvent.class)

161

)

162

),

163

responses = @ApiResponse(responseCode = "200", description = "Progress update received")

164

)

165

)

166

public Response processFile(@RequestBody FileProcessingRequest request) {}

167

```

168

169

### Callbacks Container

170

171

Container for multiple callback definitions on a single operation.

172

173

```java { .api }

174

/**

175

* Container for multiple Callback annotations

176

*/

177

@Callbacks({

178

@Callback(name = "success", callbackUrlExpression = "{$request.body#/successUrl}", operation = @Operation(...)),

179

@Callback(name = "failure", callbackUrlExpression = "{$request.body#/failureUrl}", operation = @Operation(...)),

180

@Callback(name = "progress", callbackUrlExpression = "{$request.body#/progressUrl}", operation = @Operation(...))

181

})

182

```

183

184

### Webhook Support (OpenAPI 3.1)

185

186

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

187

188

```java { .api }

189

/**

190

* Defines webhook operation (OpenAPI 3.1)

191

* Applied to: TYPE, METHOD

192

* Repeatable: Yes

193

*/

194

@Webhook(

195

name = "userCreated", // Webhook name (required)

196

operation = @Operation( // Webhook operation definition

197

method = "POST",

198

summary = "User created notification",

199

description = "Sent when a new user is created",

200

requestBody = @RequestBody(

201

description = "User creation event data",

202

content = @Content(

203

mediaType = "application/json",

204

schema = @Schema(implementation = UserCreatedEvent.class)

205

)

206

),

207

responses = {

208

@ApiResponse(responseCode = "200", description = "Webhook received successfully"),

209

@ApiResponse(responseCode = "410", description = "Webhook endpoint no longer available")

210

},

211

security = @SecurityRequirement(name = "webhookSignature")

212

),

213

extensions = {@Extension(...)} // Custom extensions

214

)

215

```

216

217

**Usage Examples:**

218

219

```java

220

// Application-level webhooks

221

@OpenAPIDefinition(

222

info = @Info(title = "E-commerce API", version = "1.0"),

223

webhooks = {

224

@Webhook(

225

name = "orderCreated",

226

operation = @Operation(

227

method = "POST",

228

summary = "Order created webhook",

229

requestBody = @RequestBody(

230

content = @Content(schema = @Schema(implementation = OrderCreatedEvent.class))

231

)

232

)

233

),

234

@Webhook(

235

name = "paymentProcessed",

236

operation = @Operation(

237

method = "POST",

238

summary = "Payment processed webhook",

239

requestBody = @RequestBody(

240

content = @Content(schema = @Schema(implementation = PaymentProcessedEvent.class))

241

)

242

)

243

)

244

}

245

)

246

public class ECommerceApplication {}

247

248

// Resource-specific webhooks

249

@Path("/subscriptions")

250

@Webhooks({

251

@Webhook(

252

name = "subscriptionActivated",

253

operation = @Operation(

254

method = "POST",

255

summary = "Subscription activated",

256

requestBody = @RequestBody(

257

content = @Content(schema = @Schema(implementation = SubscriptionEvent.class))

258

),

259

security = @SecurityRequirement(name = "webhookSignature")

260

)

261

),

262

@Webhook(

263

name = "subscriptionCancelled",

264

operation = @Operation(

265

method = "POST",

266

summary = "Subscription cancelled",

267

requestBody = @RequestBody(

268

content = @Content(schema = @Schema(implementation = SubscriptionEvent.class))

269

)

270

)

271

)

272

})

273

public class SubscriptionResource {}

274

275

// User lifecycle webhooks

276

@Webhook(

277

name = "userStatusChanged",

278

operation = @Operation(

279

method = "POST",

280

summary = "User status change notification",

281

description = "Triggered when user status changes (active, suspended, deleted)",

282

requestBody = @RequestBody(

283

description = "User status change event",

284

content = @Content(

285

mediaType = "application/json",

286

schema = @Schema(implementation = UserStatusChangeEvent.class),

287

examples = {

288

@ExampleObject(

289

name = "userSuspended",

290

summary = "User account suspended",

291

value = """

292

{

293

"eventType": "user.status.changed",

294

"userId": 12345,

295

"previousStatus": "active",

296

"newStatus": "suspended",

297

"reason": "Terms of service violation",

298

"timestamp": "2023-12-01T15:30:00Z"

299

}

300

"""

301

),

302

@ExampleObject(

303

name = "userDeleted",

304

summary = "User account deleted",

305

value = """

306

{

307

"eventType": "user.status.changed",

308

"userId": 12345,

309

"previousStatus": "active",

310

"newStatus": "deleted",

311

"reason": "User requested account deletion",

312

"timestamp": "2023-12-01T16:45:00Z"

313

}

314

"""

315

)

316

}

317

)

318

),

319

responses = {

320

@ApiResponse(

321

responseCode = "200",

322

description = "Webhook processed successfully"

323

),

324

@ApiResponse(

325

responseCode = "401",

326

description = "Invalid webhook signature"

327

),

328

@ApiResponse(

329

responseCode = "410",

330

description = "Webhook endpoint no longer exists"

331

)

332

},

333

security = @SecurityRequirement(name = "webhookSignature"),

334

extensions = {

335

@Extension(

336

name = "x-webhook-metadata",

337

properties = {

338

@ExtensionProperty(name = "retry-policy", value = "exponential-backoff"),

339

@ExtensionProperty(name = "max-retries", value = "5"),

340

@ExtensionProperty(name = "timeout", value = "30s")

341

}

342

)

343

}

344

)

345

)

346

```

347

348

### Webhooks Container

349

350

Container for multiple webhook definitions.

351

352

```java { .api }

353

/**

354

* Container for multiple Webhook annotations

355

*/

356

@Webhooks({

357

@Webhook(name = "created", operation = @Operation(...)),

358

@Webhook(name = "updated", operation = @Operation(...)),

359

@Webhook(name = "deleted", operation = @Operation(...))

360

})

361

```

362

363

## Advanced Patterns

364

365

### Conditional Callbacks

366

367

```java

368

@POST

369

@Path("/orders")

370

@Operation(summary = "Create order with conditional callbacks")

371

@Callbacks({

372

@Callback(

373

name = "fulfillmentCallback",

374

callbackUrlExpression = "{$request.body#/fulfillmentWebhook}",

375

operation = @Operation(

376

method = "POST",

377

summary = "Order fulfillment update",

378

description = "Called only for physical products requiring fulfillment"

379

)

380

),

381

@Callback(

382

name = "digitalDeliveryCallback",

383

callbackUrlExpression = "{$request.body#/deliveryWebhook}",

384

operation = @Operation(

385

method = "POST",

386

summary = "Digital product delivery",

387

description = "Called only for digital products"

388

)

389

)

390

})

391

public Response createOrder(@RequestBody OrderRequest request) {}

392

```

393

394

### Webhook Security Patterns

395

396

```java

397

@Webhook(

398

name = "secureWebhook",

399

operation = @Operation(

400

method = "POST",

401

summary = "Secure webhook with signature verification",

402

security = {

403

@SecurityRequirement(name = "webhookSignature"),

404

@SecurityRequirement(name = "bearerToken")

405

},

406

extensions = {

407

@Extension(

408

name = "x-webhook-security",

409

properties = {

410

@ExtensionProperty(name = "signature-header", value = "X-Webhook-Signature"),

411

@ExtensionProperty(name = "signature-algorithm", value = "HMAC-SHA256"),

412

@ExtensionProperty(name = "timestamp-header", value = "X-Webhook-Timestamp"),

413

@ExtensionProperty(name = "timestamp-tolerance", value = "300")

414

}

415

)

416

}

417

)

418

)

419

```

420

421

### Error Handling in Callbacks

422

423

```java

424

@Callback(

425

name = "robustCallback",

426

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

427

operation = @Operation(

428

method = "POST",

429

summary = "Callback with comprehensive error handling",

430

responses = {

431

@ApiResponse(responseCode = "200", description = "Success"),

432

@ApiResponse(responseCode = "400", description = "Invalid callback data"),

433

@ApiResponse(responseCode = "401", description = "Unauthorized callback"),

434

@ApiResponse(responseCode = "404", description = "Callback endpoint not found"),

435

@ApiResponse(responseCode = "408", description = "Callback timeout"),

436

@ApiResponse(responseCode = "410", description = "Callback endpoint permanently unavailable"),

437

@ApiResponse(responseCode = "429", description = "Too many callback attempts"),

438

@ApiResponse(responseCode = "500", description = "Callback endpoint error")

439

},

440

extensions = {

441

@Extension(

442

name = "x-callback-retry",

443

properties = {

444

@ExtensionProperty(name = "max-attempts", value = "5"),

445

@ExtensionProperty(name = "backoff-strategy", value = "exponential"),

446

@ExtensionProperty(name = "initial-delay", value = "1s"),

447

@ExtensionProperty(name = "max-delay", value = "300s")

448

}

449

)

450

}

451

)

452

)

453

```

454

455

## Event Schema Examples

456

457

### Callback Event Schemas

458

459

```java

460

@Schema(description = "Payment status update event")

461

public class PaymentStatusEvent {

462

@Schema(description = "Payment identifier", example = "pay_123456")

463

private String paymentId;

464

465

@Schema(description = "Payment status", allowableValues = {"pending", "completed", "failed", "cancelled"})

466

private String status;

467

468

@Schema(description = "Payment amount", example = "99.99")

469

private BigDecimal amount;

470

471

@Schema(description = "Status change timestamp", format = "date-time")

472

private Instant timestamp;

473

474

@Schema(description = "Optional failure reason")

475

private String failureReason;

476

}

477

478

@Schema(description = "User status change event")

479

public class UserStatusChangeEvent {

480

@Schema(description = "Event type", example = "user.status.changed")

481

private String eventType;

482

483

@Schema(description = "User ID", example = "12345")

484

private Long userId;

485

486

@Schema(description = "Previous status", example = "active")

487

private String previousStatus;

488

489

@Schema(description = "New status", example = "suspended")

490

private String newStatus;

491

492

@Schema(description = "Reason for status change")

493

private String reason;

494

495

@Schema(description = "Change timestamp", format = "date-time")

496

private Instant timestamp;

497

}

498

```