Part 3 of 7 · Return and RMA handler series ~6 min read

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 and label stage: issue a number, call the carrier label API, store the label, and email the customer A cleared return enters from the top left. Inside the AWS account container, four steps run left to right. First, “Issue RMA” mints a unique number per return. Second, “Call label API” requests a prepaid carrier label, using a key drawn from a Secrets Manager box below it. Third, “Store label” writes the label PDF to an S3 bucket rmar-store under a labels prefix, shown as a box below. Fourth, “Email customer” sends a single message with the RMA number, the label, and the return instructions, optionally phrased by a Bedrock box below it. An arrow runs from the email step up and out to a Customer box at the top right. A note states the stage is idempotent: a retry never issues a second label or RMA. Cleared return (Part 2) Customer AWS account Issue RMA unique per return Call label API prepaid carrier label Store label PDF + tracking no. Email customer RMA + label + steps label out Secrets Manager carrier API key S3 rmar-store labels/ prefix Bedrock (optional) customer wording Idempotent: a retry never issues a second label or a second RMA for the same return.
Fig 3. Issuing the RMA and label. Mint the number, call the carrier for a prepaid label with a key from Secrets Manager, store the PDF and tracking number in S3, and send the customer one email. Bedrock only ever touches the wording.

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.
All posts