Problem
AsyncAPI can describe the primary operation and an optional reply response, but it has no standard way to express the behaviour when message processing fails: retry policy, retry destination, or dead-letter destination. Implementations therefore rely on unvalidated vendor extensions, which limits interoperability.
Proposal
Add first-class, operation-level support for retries and dead-letter routing. A receive operation should be able to declare:
- a
retry route: channel and message references, maximum attempts, and a backoff policy;
- a
deadLetter route: channel and message references for messages that cannot be recovered.
retry and deadLetter would be sibling properties of reply. This makes the possible outcomes of an operation visible at one level, while retaining existing channel and message reference conventions.
Illustrative AsyncAPI Spec
asyncapi: 3.0.0
info:
title: Order processing API
version: 1.0.0
channels:
orders:
address: orders
messages:
order:
$ref: "#/components/messages/Order"
orderRetries:
address: orders.retry
messages:
retryOrder:
$ref: "#/components/messages/RetryOrder"
orderDeadLetters:
address: orders.dlq
messages:
deadLetterOrder:
$ref: "#/components/messages/DeadLetterOrder"
operations:
processOrder:
action: receive
channel:
$ref: "#/channels/orders"
messages:
- $ref: "#/channels/orders/messages/order"
retry:
channel:
$ref: "#/channels/orderRetries"
messages:
- $ref: "#/channels/orderRetries/messages/retryOrder"
maxAttempts: 3
strategy:
type: exponential
initialDelaySeconds: 1
multiplier: 2
maxDelaySeconds: 60
deadLetter:
channel:
$ref: "#/channels/orderDeadLetters"
messages:
- $ref: "#/channels/orderDeadLetters/messages/deadLetterOrder"
waitTimeInSeconds: 15
components:
messages:
Order:
name: OrderMessage
title: Order Event
summary: Order message
contentType: application/json
headers:
type: object
properties:
CorrelationId:
type: string
description: Correlates the order across topics
examples:
- corr-bulk-90003
required:
- CorrelationId
payload::
type: object
RetryOrder:
name: RetryMessage
title: Retry Message Event
summary: Message sent to retry topic when processing fails
contentType: application/json
headers:
type: object
properties:
CorrelationId:
type: string
description: Correlates the order across topics
examples:
- corr-retry-90001
required:
- CorrelationId
payload:
type: object
properties:
originalMessage:
type: object
description: The original message payload that failed processing
additionalProperties: true
messageKey:
type: string
description: The message key for partitioning
examples:
- ORD-RETRY-90001
retryCount:
type: integer
description: Current retry attempt number
examples:
- 1
firstAttemptTimestamp:
type: string
format: date-time
description: Timestamp of the first processing attempt
examples:
- '2026-01-19T10:00:00Z'
lastAttemptTimestamp:
type: string
format: date-time
description: Timestamp of the last processing attempt
examples:
- '2026-01-19T10:00:05Z'
errorMessage:
type: string
description: Error message from the failed attempt
examples:
- 'Simulated transformation failure for order: ORD-RETRY-90001'
errorStackTrace:
type: string
description: Stack trace of the error
examples:
- 'io.specmatic.async.transformer.MessageTransformationException: ...'
required:
- originalMessage
- messageKey
- retryCount
- firstAttemptTimestamp
- lastAttemptTimestamp
DeadLetterOrder:
name: DLQ
title: Dead Letter Queue Event
summary: Message sent to the DLQ topic after retries are exhausted
contentType: application/json
headers:
type: object
properties:
CorrelationId:
type: string
description: Correlates the order across topics
examples:
- corr-dlq-90001
required:
- CorrelationId
payload:
type: object
properties:
originalMessage:
type: object
additionalProperties: true
messageKey:
type: string
totalRetries:
type: number
firstAttemptTimestamp:
type: string
format: date-time
failedTimestamp:
type: string
format: date-time
finalErrorMessage:
type: string
finalErrorStackTrace:
type: string
required:
- originalMessage
- messageKey
- totalRetries
- firstAttemptTimestamp
- failedTimestamp
- finalErrorMessage
This example expresses the observable recovery path only. A Kafka binding might implement it with retry and DLQ topics, while an SQS binding might use a redrive policy; neither implementation detail is required by the core object.
Design considerations
- Reuse existing Operation Object conventions for channel and message references.
- Associate retries and dead-letter routing with the originating receive operation.
- Support fixed-delay, linear-backoff, and exponential-backoff retry strategies, with an optional maximum delay.
- Define the relationship between retry exhaustion and dead-letter routing.
- Keep broker-specific delivery semantics in protocol bindings.
Prior art
Specmatic already implements this behavior through operation-level extensions named x-specmatic-retry and x-specmatic-dlq. That implementation provides a concrete starting point for the object shape and contract-test scenarios.
Acceptance criteria
- The specification defines reusable schemas for retry policy and dead-letter routing for
receive operation.
- Retry and DLQ does not apply to
send or reply operations.
- An operation can reference retry and dead-letter channels and messages.
- Fixed, linear, and exponential delay strategies are representable.
- The specification states the relationship between retry exhaustion and dead-letter routing.
- Examples and schema validation cover the new fields.
Scope
This proposal describes the observable asynchronous contract. It does not mandate how brokers implement retries, delay queues, redrive policies, or message headers.
Problem
AsyncAPI can describe the primary operation and an optional reply response, but it has no standard way to express the behaviour when message processing fails: retry policy, retry destination, or dead-letter destination. Implementations therefore rely on unvalidated vendor extensions, which limits interoperability.
Proposal
Add first-class, operation-level support for retries and dead-letter routing. A
receiveoperation should be able to declare:retryroute: channel and message references, maximum attempts, and a backoff policy;deadLetterroute: channel and message references for messages that cannot be recovered.retryanddeadLetterwould be sibling properties ofreply. This makes the possible outcomes of an operation visible at one level, while retaining existing channel and message reference conventions.Illustrative AsyncAPI Spec
This example expresses the observable recovery path only. A Kafka binding might implement it with retry and DLQ topics, while an SQS binding might use a redrive policy; neither implementation detail is required by the core object.
Design considerations
Prior art
Specmatic already implements this behavior through operation-level extensions named
x-specmatic-retryandx-specmatic-dlq. That implementation provides a concrete starting point for the object shape and contract-test scenarios.Acceptance criteria
receiveoperation.sendorreplyoperations.Scope
This proposal describes the observable asynchronous contract. It does not mandate how brokers implement retries, delay queues, redrive policies, or message headers.