AWS Messaging skill for Claude Code
Queues, topics and buses that survive duplicates. Claude picks between SQS, SNS fan-out, EventBridge rules, Pipes and Scheduler based on ordering, routing and consumer count, then configures visibility timeouts, long polling and DLQs with alarms. Consumers are written idempotently with partial batch failure reporting, and the poison message, duplicate and redrive paths are tested before anything ships.
- name
aws-messaging - size221 lines · ≈2,450 tokens
- files1
- licenseMIT
- updated
What Claude sees before loading it
This description is the only part of the skill that sits in context all the time. Claude reads it to decide when to load the full playbook below.
description: Designs asynchronous messaging on AWS with SQS, SNS and EventBridge - choosing queues, topics or event buses, standard versus FIFO with message groups and deduplication, visibility timeouts, long polling, dead-letter queues and redrive, SNS fan-out with filter policies, EventBridge rules, Pipes, Scheduler, archive and replay, Lambda event source mappings with partial batch failures, and idempotent consumers. Use when adding a queue or event bus, when messages are duplicated, lost, stuck or reprocessed, when a DLQ fills up, or when deciding between SQS, SNS, EventBridge, Kinesis or Kafka. Try asking
- Should order events go through SNS and SQS or EventBridge? Design it for three consumers
- Our SQS consumer processes some messages twice. Find the cause and make it idempotent
- The DLQ has 4,000 messages after a bug. Plan a safe redrive
Use it when
- Adding a queue, topic, event bus, pipe or schedule on AWS
- Messages are duplicated, stuck, out of order or lost
- A dead-letter queue is filling or needs a redrive plan
- Wiring Lambda or ECS consumers to SQS
Skip it when
- You need a replayable high-throughput log with consumer offsets — use kafka-streaming
- The question is overall event-driven design across services — use event-driven-architecture
How to check the result
A poison message reaches the DLQ after maxReceiveCount and trips its alarm, a duplicate send causes exactly one side effect, and a redrive moves messages back without errors.
The common mistake it prevents
Setting the queue visibility timeout shorter than processing time, so messages reappear and get processed twice while it looks like the producer is sending duplicates.
A messaging design is done when every message has an owner, consumers are idempotent (at-least-once delivery is the contract), failures land in a monitored DLQ with a tested redrive path, visibility timeouts exceed processing time, and ordering is guaranteed only where the business needs it.
Ground rules
- Assume duplicates. Standard SQS, SNS, and EventBridge deliver at least once; FIFO dedup only covers a 5-minute window and producer retries. Consumers must be idempotent.
- Every queue and every EventBridge target gets a DLQ, and every DLQ gets an alarm on
ApproximateNumberOfMessagesVisible > 0. - Purging a queue, deleting a queue/topic/bus, or redriving a DLQ into production consumers requires explicit user confirmation; redrive replays side effects.
- Messages carry IDs and a schema version; large payloads (over the size limit or just big) go to S3 with a pointer in the message.
- Encrypt with SSE (SQS-managed or KMS). With a customer KMS key, producers (including
SNS/EventBridge service principals) need
kms:GenerateDataKey/kms:Decryptin the key policy.
Choose the service
| Need | Use |
|---|---|
| One producer, one consumer group, buffer and retry work | SQS standard queue |
| Strict order per entity (per account, per order) | SQS FIFO with MessageGroupId = entity ID |
| One event to several independent consumers | SNS topic -> one SQS queue per consumer (fan-out) |
| Routing by event content across many producers/consumers, SaaS or AWS service events | EventBridge bus + rules |
| Point-to-point source -> filter -> enrich -> target without glue code | EventBridge Pipes |
| Run something at a time or rate (one-off or cron, time zones) | EventBridge Scheduler |
| Replayable ordered stream, multiple readers at their own offset, high throughput | Kinesis Data Streams or MSK/Kafka (see kafka-streaming) |
| Request/response | Not messaging; use HTTP/gRPC |
Workflow
- [ ] 1. Map producers, consumers, ordering and volume
- [ ] 2. Pick services; design message envelope and schema
- [ ] 3. Configure queues/topics/rules with DLQs and alarms
- [ ] 4. Write idempotent consumers with correct batch failure handling
- [ ] 5. Set access policies and encryption
- [ ] 6. Test failure paths: poison message, timeout, duplicate, redrive
2. Envelope
{
"id": "01J9Z6Q4X8M2R7",
"type": "order.paid",
"version": 2,
"occurredAt": "2026-03-04T10:15:00Z",
"source": "checkout",
"correlationId": "req-7f3a",
"data": { "orderId": "o_123", "amountCents": 4599, "currency": "EUR" }
}
Use id (or a business key like orderId+type) as the idempotency key. Add fields;
never change the meaning of existing ones without bumping version.
3. SQS configuration
| Setting | Default to use |
|---|---|
| Visibility timeout | > max processing time; with Lambda, at least 6x the function timeout |
| ReceiveMessageWaitTimeSeconds | 20 (long polling; fewer empty receives, lower cost) |
| Message retention | 4 days default; DLQ retention 14 days (max) and longer than the source’s |
| Redrive policy | maxReceiveCount 3-5 (higher if throttling causes receives without processing) |
| Max message size | 1 MiB since August 2025 (was 256 KiB); still prefer S3 pointers for big payloads |
| Delay | DelaySeconds up to 15 min for simple backoff; longer delays via Scheduler |
aws sqs create-queue --queue-name orders-dlq --attributes MessageRetentionPeriod=1209600
aws sqs create-queue --queue-name orders --attributes '{
"VisibilityTimeout":"180","ReceiveMessageWaitTimeSeconds":"20",
"RedrivePolicy":"{\"deadLetterTargetArn\":\"arn:aws:sqs:eu-west-1:123456789012:orders-dlq\",\"maxReceiveCount\":\"5\"}",
"SqsManagedSseEnabled":"true"}'
aws sqs get-queue-attributes --queue-url $Q --attribute-names All
FIFO specifics: queue name ends in .fifo; MessageGroupId sets ordering scope (one
in-flight batch per group, so a single hot group serializes everything); dedup via
MessageDeduplicationId or content-based dedup; enable high throughput mode
(DeduplicationScope=messageGroup, FifoThroughputLimit=perMessageGroupId) for scale.
A message failing repeatedly blocks its group until it moves to the DLQ.
SNS fan-out
aws sns subscribe --topic-arn $TOPIC --protocol sqs --notification-endpoint $QUEUE_ARN \
--attributes '{"RawMessageDelivery":"true","FilterPolicyScope":"MessageBody","FilterPolicy":"{\"type\":[\"order.paid\",\"order.refunded\"]}"}'
- The queue’s access policy must allow
sns.amazonaws.comwithaws:SourceArn= the topic ARN. - Raw delivery avoids double JSON-wrapping; without it, consumers parse the SNS
envelope then
Message. - Give each subscription a DLQ (
RedrivePolicyon the subscription) for delivery failures (e.g. queue policy broken).
EventBridge
{
"source": ["acme.checkout"],
"detail-type": ["order.paid"],
"detail": { "amountCents": [{ "numeric": [">=", 10000] }], "currency": ["EUR", "USD"] }
}
- Use a custom bus per domain or per organization; keep the default bus for AWS service events.
- Each target: retry policy (default up to 24 h / 185 attempts; lower it for
time-sensitive events) and its own DLQ (
DeadLetterConfig). - Turn on an archive for replay of business events; replays re-deliver to rules, so consumers must be idempotent.
- Input transformers reshape events per target; avoid deep business logic there.
- Test patterns before deploying:
aws events test-event-pattern --event-pattern file://pattern.json --event file://event.json. - Event size limits differ from SQS; check the current EventBridge quota before sending large detail payloads, and use S3 pointers.
4. Consumers
Lambda with SQS (partial batch failure, so one bad message does not retry the batch):
from aws_lambda_powertools.utilities.batch import BatchProcessor, EventType, process_partial_response
from aws_lambda_powertools.utilities.idempotency import DynamoDBPersistenceLayer, IdempotencyConfig, idempotent_function
processor = BatchProcessor(event_type=EventType.SQS)
store = DynamoDBPersistenceLayer(table_name="idempotency")
cfg = IdempotencyConfig(event_key_jmespath="id", expires_after_seconds=7 * 24 * 3600)
@idempotent_function(data_keyword_argument="message", persistence_store=store, config=cfg)
def handle(message: dict) -> None:
... # side effects: charge, email, write
def record_handler(record):
import json
handle(message=json.loads(record.body))
def lambda_handler(event, context):
return process_partial_response(event=event, record_handler=record_handler,
processor=processor, context=context)
Event source mapping must enable FunctionResponseTypes=["ReportBatchItemFailures"];
without it the returned failures are ignored and the whole batch succeeds or fails.
Set ScalingConfig.MaximumConcurrency to protect downstream databases, and a batching
window to trade latency for fewer invocations.
Long-running workers (ECS/EC2):
- Receive with
WaitTimeSeconds=20,MaxNumberOfMessages=10. - Delete only after the side effect commits. Extend visibility
(
ChangeMessageVisibility) for long jobs, as a heartbeat. - Idempotency without Powertools: conditional write
(
PutItemwithattribute_not_exists(pk)) on the message key before the side effect, or a unique constraint in the database transaction (see idempotency-retries).
6. Failure testing and redrive
- Send a poison message; confirm it lands in the DLQ after
maxReceiveCountand the alarm fires. - Kill a consumer mid-processing; confirm the message reappears after the visibility timeout and is processed once.
- Send the same message twice; confirm one side effect.
- Redrive after fixing the bug (confirm with the user):
aws sqs start-message-move-task --source-arn arn:aws:sqs:eu-west-1:123456789012:orders-dlq \
--destination-arn arn:aws:sqs:eu-west-1:123456789012:orders --max-number-of-messages-per-second 50
aws sqs list-message-move-tasks --source-arn arn:aws:sqs:eu-west-1:123456789012:orders-dlq
Monitoring
| Metric | Alarm when |
|---|---|
DLQ ApproximateNumberOfMessagesVisible | > 0 |
ApproximateAgeOfOldestMessage | > the business SLA (e.g. 5 min) |
ApproximateNumberOfMessagesVisible (source) | Growing for 15 min (consumers behind) |
EventBridge FailedInvocations, InvocationsSentToDlq | > 0 |
SNS NumberOfNotificationsFailed | > 0 |
| Lambda ESM throttles / errors | Sustained |
Gotchas
- Visibility timeout shorter than processing time causes duplicate processing that looks like a producer bug.
maxReceiveCount=1sends messages to the DLQ on any transient error or consumer crash.- A FIFO DLQ must be FIFO; a standard queue’s DLQ must be standard.
- SQS
ApproximateNumberOf...metrics are approximate and lag; do not use for exactly once logic. - Lambda reserved concurrency below the ESM’s poller count causes throttles that count
as receives and push healthy messages to the DLQ; use
MaximumConcurrencyinstead. - SNS filter policies on
MessageAttributesignore body fields unlessFilterPolicyScope=MessageBody. - EventBridge rules match events from the same bus only; cross-account requires bus policies and a rule targeting the other bus.
Output
**Flow:** checkout -> EventBridge bus `commerce` -> rule order.paid -> SQS `fulfillment` (DLQ, 14 d) -> Lambda
**Ordering:** none required (idempotent by event id); refunds use FIFO group = orderId
**Consumers:** Powertools batch + idempotency (DynamoDB, 7 d TTL); ReportBatchItemFailures on
**Alarms:** DLQ > 0, oldest message > 5 min, FailedInvocations > 0
**Tested:** poison -> DLQ after 5; duplicate -> 1 side effect; redrive at 50 msg/s About this skill
Written and maintained by the Claude Skills editors to the same editorial standard as every skill in the directory: a selective trigger, a real workflow, known pitfalls and a verification step. First published , last updated . MIT licensed.