How an RMA and label get issued
Once a return clears the policy checks, the customer needs two things to actually send the item back: a reference number that ties the parcel to their return, and a label they can stick on the box. This post is about issuing both — cleanly, once, and with everything the customer needs in a single email.
Key takeaways
- A cleared return gets a unique RMA number that ties every later step — parcel, inspection, decision — back to it.
- The prepaid label comes from the carrier’s label API, called with a key held in Secrets Manager, never in code.
- The label PDF and the tracking number are stored in S3 and on the return, so nothing depends on the carrier’s portal later.
- The customer gets exactly one email: the RMA number, the label, and plain instructions for sending the item back.
- The stage is idempotent — a retry never issues a second label or a second RMA for the same return.
Two things the customer needs
Once a return is cleared, the customer needs two concrete things to actually send the item back: a reference that ties their parcel to their return, and a label they can print or show at a drop-off point. This stage — the Lambda rmar-label, the second task in the state machine — produces both, stores everything the rest of the system will need, and sends one clear email. It runs in a fixed order so that a retry can never leave a half-issued return behind.
The RMA number
The RMA (return merchandise authorisation) number is the thread that runs through everything that follows. It’s minted once, deterministically, from the return: something like RMA-4821-7 — the order number, then a per-order sequence so a customer returning two items from one order gets two distinct RMAs. It becomes the partition key on the returns record, it’s printed on the label and in the email, and it’s what the warehouse writes on the box when it’s checked back in. Because it’s derived rather than random, the same return always resolves to the same RMA — which is half of what makes this stage safe to retry.
The label
The prepaid label comes from the carrier’s label API — the same kind of call your shipping tool makes, just for an inbound parcel. The stage requests a return label for the item’s weight and the customer’s address, using an API key fetched from Secrets Manager at call time, never baked into the function or the policy doc. The carrier responds with two things worth keeping: the label PDF, which goes straight into the labels/ prefix of the S3 bucket, and the tracking number, which is written onto the return record. That tracking number is the hook the next stage waits on, so storing it now — rather than relying on the carrier’s portal later — matters. Label fees are a carrier cost on your shipping contract, not an AWS one; the system just asks for the label.
The one email
The customer gets a single message through SES: the RMA number, the label (attached and linked from S3 via a short-lived signed URL), and three plain instructions — what to pack, where to drop it, and by when. If Bedrock is enabled it phrases that email in your voice; if it isn’t, a template does the job just as well, because there’s nothing here a model needs to decide — the facts are all known. One email, not three: no separate “your return is approved,” “here’s your number,” and “here’s your label,” which is how customers end up confused about which message to act on.
Why issuing exactly once matters
This is the one stage that spends real money on an external service — every label API call may cost you a postage pre-pay — so it has to be safe against the thing that always eventually happens in a distributed system: a retry. If rmar-label times out after the carrier issued the label but before the state machine recorded success, a naive retry would buy a second label and confuse the customer with a second email. The stage avoids that by checking the return record first: if it already carries an RMA, a label key, and a tracking number, the work is done and the step simply returns the existing values. The derived RMA number and the recorded tracking number make the whole stage idempotent, so the state machine can retry it freely and the customer still gets exactly one label.
Design rules for the RMA and label stage
- One RMA, derived not random. The number comes from the return, so the same return always maps to the same RMA.
- Keys live in Secrets Manager. The carrier API key is fetched at call time, never in code or the policy doc.
- Store the tracking number now. The inbound wait hooks onto it; don’t depend on the carrier’s portal later.
- One email, all the facts. RMA, label, and instructions together — not three messages to piece together.
- Idempotent by design. Already has an RMA and a label? Return them. Never buy a second label on a retry.