An SQS consumer of an Amazon SNS topic finds each payload wrapped in a JSON object with Type, MessageId, TopicArn and Message fields instead of the JSON the publisher sent. What is happening, and how do you change it?
answer
- the wrapper is a notification envelope
- payload is a string inside Message
- one subscription attribute controls it
- per subscription, not per topic
- envelope carries the signature for HTTPS
basics
~20 sThat wrapper is the standard SNS notification envelope, delivered because the subscription has raw message delivery turned off. Set the subscription attribute RawMessageDelivery to true and consumers receive the published payload itself, with no envelope.
solid answer
~40 sBy default an SNS subscription delivers a JSON envelope: the publisher's payload lives as a string inside the `Message` field, alongside metadata like `Type`, `MessageId`, `TopicArn`, `Timestamp`, `Subject` and the signature fields. A consumer that parses the delivery as its own event sees the wrong shape, or double-parses. Setting `RawMessageDelivery` to `true` on an SQS or HTTPS subscription makes SNS deliver the published body verbatim, which is what you normally want when the same consumer must work whether messages arrive from SNS or straight from a queue. Message attributes are still carried across as SQS message attributes, so attribute-based filtering keeps working. The tradeoff is that you lose the envelope metadata, including `Subject` and the signature — and note that Lambda subscriptions always receive the SNS record structure regardless of this setting.
code
bash · 5 linesaws sns subscribe \
--topic-arn arn:aws:sns:eu-west-1:111122223333:orders \
--protocol sqs \
--notification-endpoint arn:aws:sqs:eu-west-1:111122223333:orders-billing \
--attributes RawMessageDelivery=truego deeper
Recognise the SNS envelope on sight and know that RawMessageDelivery on the subscription removes it. Being able to say the payload lives as a string in the Message field is the key recall.
Explain what is lost with raw delivery — Subject, timestamps and the signature fields — that message attributes still pass through to SQS, and that Lambda always sees the SNS record structure regardless.
Show that you plan the switchover: consumers tolerating both shapes while the backlog drains, discriminators moved out of Subject into attributes, and a deliberate decision about signature verification for any public HTTPS subscriber.
Set the house rule so this never becomes per-team folklore: raw delivery on by default for queue subscribers, routing metadata standardised as message attributes, and the payload contract defined independently of the transport that carried it.
## The envelope When you publish to an Amazon SNS topic, what a subscriber receives by default is not your payload — it is a notification document that *contains* your payload: ```json { "Type": "Notification", "MessageId": "22b80b92-fdea-4c2c-8f9d-bdfb0c7bf324", "TopicArn": "arn:aws:sns:eu-west-1:111122223333:orders", "Subject": "OrderPlaced", "Message": "{\"orderId\":\"A-1001\",\"totalCents\":12900}", "Timestamp": "2025-03-04T09:12:44.111Z", "SignatureVersion": "1", "Signature": "...", "SigningCertURL": "https://sns.eu-west-1.amazonaws.com/SimpleNotificationService-....pem", "UnsubscribeURL": "https://sns.eu-west-1.amazonaws.com/?Action=Unsubscribe&..." } ``` Note that `Message` is a **string**, not a nested object — your JSON has been serialised into it. That is why the classic symptom is a consumer that either fails to find its own fields or has to call the JSON parser twice. ## Turning it off The subscription attribute `RawMessageDelivery` controls this. Set it to `true` and SNS delivers exactly the bytes that were published: ```bash aws sns set-subscription-attributes \ --subscription-arn arn:aws:sns:eu-west-1:111122223333:orders:8f1c2d3e-4a5b-6c7d-8e9f-0a1b2c3d4e5f \ --attribute-name RawMessageDelivery \ --attribute-value true ``` You can also pass it inline when subscribing, via `--attributes RawMessageDelivery=true`. ## Why teams usually enable it 1. **Consumer portability.** A worker that reads from an SQS queue should not care whether the message arrived from a topic or was written directly by another service. Raw delivery keeps the payload contract identical in both cases. 2. **Less parsing, fewer bugs.** No double `JSON.parse`, no string-inside-JSON escaping, no per-consumer unwrapping helper duplicated in five languages. 3. **Cleaner schema validation.** Contract tests and schema registries validate the actual event, not a wrapper. ## What you give up - **Envelope metadata.** `Subject`, `Timestamp`, `TopicArn` and `MessageId` are no longer visible in the body. If a consumer relied on `Subject` as a poor-man's event type, moving to raw delivery breaks it — put the discriminator in a message attribute or in the payload instead. - **The SNS signature.** For HTTPS subscribers, the envelope carries `Signature` and `SigningCertURL`, which is how a public endpoint verifies that a POST genuinely came from SNS. If your HTTPS endpoint is internet-facing and you rely on signature verification, keep the envelope. This concern does not apply to SQS delivery, where the queue policy already restricts who may write. - **Subscription confirmation flow visibility.** HTTPS subscriptions receive `SubscriptionConfirmation` and `UnsubscribeConfirmation` message types in the envelope shape; an endpoint still needs to handle those. ## Details that trip people up - **Message attributes survive.** With raw delivery to SQS, attributes published on the message arrive as SQS message attributes rather than being embedded in an envelope, so subscription filter policies scoped to message attributes continue to work. - **Lambda is different.** A Lambda function subscribed to a topic always receives the SNS record structure — the payload is reached through the event's `Records` array, at `Records[0].Sns.Message`. Raw message delivery is a concept for SQS, HTTPS and Firehose subscriptions. - **Changing it is not retroactive.** Messages already sitting in a queue keep the shape they were delivered with, so a switchover needs a consumer that tolerates both forms until the backlog drains. - **It is per subscription.** Two queues on the same topic can disagree, which is occasionally useful during a migration and otherwise a source of confusion. The short version to say out loud: the wrapper is SNS's notification envelope, it is on by default, `RawMessageDelivery` turns it off for SQS and HTTPS subscribers, and the only real reasons to keep it are `Subject`/metadata and signature verification on public HTTPS endpoints.
- Is there a case where you deliberately keep the envelope on?Yes — an internet-facing HTTPS subscriber that verifies the request really came from SNS needs the `Signature` and `SigningCertURL` fields, which only exist in the envelope. Consumers that genuinely use `Subject`, `Timestamp` or `TopicArn` are the other case, though moving those into message attributes is usually the better fix.
- If you flip raw message delivery on a live subscription, what should the consumer do during the transition?Tolerate both shapes for a while. Messages already queued keep the envelope, so the handler should detect the wrapper — for example, a top-level `Type` of `Notification` with a string `Message` — unwrap it, and otherwise parse the body directly. Remove the branch once the backlog has drained and metrics confirm only raw messages arrive.
saying these in an interview costs you the question
- Raw message delivery is a topic-level setting
- Enabling raw delivery drops message attributes too
- The Message field is a nested JSON object, not a string
- Lambda subscribers can be switched to raw delivery
- Existing queued messages change shape retroactively