FBA Workflow
Create an Amazon FBA inbound transport plan and drive it through to labels
Overview
The FBA workflow creates an Amazon inbound plan from an outbound shipment, submits packing information, lets Amazon choose where the inventory is sent, confirms transportation, then requests labels. Most stages are asynchronous on Amazon's side and follow the same three-part pattern described below.
The plan object is the source of truth between calls. After every operation completes, reload the plan with GET /plans/{plan_id} and read the IDs you need from the refreshed response rather than reusing IDs from an earlier call.
The async-then-commit pattern
This is the single most important thing to understand. Almost every stage is three calls, not one:
- Dispatch — a
generate-*orconfirm-*call that sends an async request to Amazon and returns an operation id. - Poll — drive that operation to
SUCCESSwithGET /operations/{id}/update. - Commit — a
select-*ormark-*-as-confirmedcall that records the confirmation on the plan.
An operation reaching SUCCESS does not mean the step is confirmed. SUCCESS only means Amazon finished the async work for the dispatch call. The selection is not recorded until you call the matching select-* / mark-*-as-confirmed step. If you skip the commit step, the operation still succeeds, but the option stays OFFERED, is_packing_information_set stays false, and the next step rejects with errors like “No placement option has confirmed” or “Packing information submission is required.”
The mark-*-as-confirmed and select-* calls are local: they do not call Amazon, they record the confirmation (and, for transportation, release a lock). Always poll the operation, not the plan — the plan's updated_at does not move while an operation is running, which is not a sign of failure.
Dispatch, poll, commit
# 1. Dispatch — returns an operation id
POST /api/fba-transport/v2024/plans/{plan_id}/confirm-placement-option
=> { "operation_id": "019953ae-..." }
# 2. Poll the operation until it is SUCCESS (not the plan)
GET /api/fba-transport/v2024/operations/{operation_id}/update
=> { "status": "IN_PROGRESS" } # keep polling
=> { "status": "SUCCESS" } # Amazon finished the async work
# 3. Commit — record the confirmation in your plan
POST /api/fba-transport/v2024/plans/{plan_id}/mark-placement-option-as-confirmed
# Only now is the placement option actually confirmed. Reload the plan.
GET /api/fba-transport/v2024/plans/{plan_id}Two kinds of IDs
Every option, group, and box in the plan response carries two identifiers: our internal id (a UUID) and Amazon's id (for example packing_option_id, placement_option_id, or a box's amazon_box_id). Sending the wrong one is a common cause of “not found / is null” errors. Which id each endpoint expects:
| Endpoint | Send |
|---|---|
| confirm-packing-option, mark-packing-option-as-confirmed | Amazon packing_option_id |
| packing-group-items | the option's and group's internal id (the plan response id field) — not Amazon's id |
| add-carton-request-to-submission | packing_group_ids: array of each packing group's id from the plan response |
| select/confirm/mark placement | Amazon placement_option_id |
| confirm/mark transportation | shipment_transportation_ids: array of { shipment_id, transportation_option_id } |
| update-tracking-small-details | the box's amazon_box_id (the FBA… value) — not the internal box id |
Both ids appear on the plan
GET /api/fba-transport/v2024/plans/{plan_id}
{
"packing_options": [
{
"id": "0c8e3b1a-...", // <-- OUR internal id (a UUID)
"packing_option_id": "po8e61...", // <-- AMAZON's id
"status": "OFFERED",
"packing_groups": [
{ "id": "pg1d2c3b4-..." } // packing group: use this "id"
]
}
],
"placement_options": [
{ "id": "...", "placement_option_id": "pl4eb6..." }
]
}Prerequisites and preflight
You need an outbound shipment, an Amazon channel, and one or more merchant SKUs with quantities. The SKUs must exist on the Amazon channel and be eligible for inbound shipment. Check eligibility and prep details before creating the plan — the cached endpoints avoid a live Amazon fetch, while fetch-item-eligibility asks Amazon for the latest eligibility of one ASIN.
Optional preflight checks
GET /api/fba-transport/v2024/item-eligibility?channel_id={channel_id}&asins[]={asin}
GET /api/fba-transport/v2024/fetch-item-eligibility?channel_id={channel_id}&asin={asin}
GET /api/fba-transport/v2024/item-prep-details?channel_id={channel_id}&mskus[]={msku}Create the plan
Create the plan from the outbound shipment. createInboundPlan accepts only msku, quantity, prep_owner, label_owner, and expiration. Amazon does not take an FNSKU or ASIN here, so a freshly created plan item always has fnsku: null and asin: null. That is expected — see When FNSKUs get stamped below.
If creation rejects an item (for example brand approval, FBA_INB_0021), the reason is on the create operation's problems array, not on the plan items — see Reading per-item errors.
Create plan
curl -X POST https://YOUR_DOMAIN/api/fba-transport/v2024/plans \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"outbound_shipment_id": 5,
"channel_id": 1,
"destination_marketplace": "ATVPDKIKX0DER",
"items": [
{
"msku": "Mini-Porta-276810",
"quantity": 40,
"label_owner": "SELLER",
"prep_owner": "SELLER",
"expiration": null,
"manufacturing_lot_code": null
}
]
}'Choose the packing workflow
Set whether packing information is known before placement with update-packing-info-known. Use pack first for small parcel and any flow where box contents are known up front. Use pack later when placement must be confirmed before cartons are finalized, most commonly for LTL. Pack later does not support small parcel in the current workflow.
If generate-packing-options returns SUCCESS but packing_options stays empty, Amazon found no valid packing arrangement for the plan. Re-firing will not change it. This is almost always an item-level eligibility or compliance problem (or the plan errored), so check item eligibility and the create operation's problems rather than retrying.
Pack first
Pack first sequence
# Packing (operation -> SUCCESS -> commit at each confirm)
POST update-packing-info-known # is_packing_information_known = true
POST generate-packing-options # -> operation, poll to SUCCESS
GET packing-options
GET packing-group-items # stamps FNSKU/ASIN (use internal ids)
POST confirm-packing-option # -> operation, poll to SUCCESS
POST mark-packing-option-as-confirmed # COMMIT (local)
# Box contents
POST box-groups
POST box-groups/{box_group_id}/set-box-quantity
POST box-groups/{box_group_id}/items
POST set-packing-information # -> operation, poll to SUCCESS
POST add-carton-request-to-submission # COMMIT box contents (packing_group_ids)
# Placement
POST generate-placement-options # -> operation, poll to SUCCESS
GET placement-options
POST select-placement-option
POST confirm-placement-option # -> operation, poll to SUCCESS
POST mark-placement-option-as-confirmed # COMMIT (local)
GET set-boxes-ids # assign Amazon box ids to the shipmentPack later
Pack later sequence
POST update-packing-info-known # is_packing_information_known = false
POST generate-placement-options # -> operation, poll to SUCCESS
GET placement-options
POST select-placement-option
POST confirm-placement-option # -> operation, poll to SUCCESS
POST mark-placement-option-as-confirmed # COMMIT (local)
GET get-shipment
GET list-shipment-boxes
POST box-groups
POST box-groups/{box_group_id}/items
POST set-packing-information # -> operation, poll to SUCCESS
POST add-carton-request-to-submission # COMMIT box contents
GET set-boxes-idsConfirm packing option (Amazon packing_option_id)
curl -X POST https://YOUR_DOMAIN/api/fba-transport/v2024/plans/{plan}/confirm-packing-option \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"plan_id": "019953ae-7a0d-7342-956a-881dcdbe42ad",
"packing_option_id": "packing-option-1"
}'Commit: mark packing option as confirmed
curl -X POST https://YOUR_DOMAIN/api/fba-transport/v2024/plans/{plan}/mark-packing-option-as-confirmed \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"plan_id": "019953ae-7a0d-7342-956a-881dcdbe42ad",
"packing_option_id": "packing-option-1"
}'When FNSKUs get stamped
Plan items are created with fnsku: null and asin: null. Nothing back-fills them on a timer, and set-packing-information reaching SUCCESS does not stamp them. They are written onto the plan items only when you read the items back from Amazon through packing-group-items (before placement) or list-shipment-items (after placement). That same fetch is what assigns Amazon box IDs downstream, so if the FNSKU never lands, the box never gets an amazon_box_id and tracking and labels both fail with “one or more items are missing an FNSKU.”
The packing-group-items query parameters are misleadingly named. packing_option_id and packing_group_id here both expect the internal id field from the plan response, not Amazon's packing_option_id. Sending Amazon's id returns “Failed to fetch packing group for plan. Packing option is null.” and the FNSKU never stamps.
Stamp FNSKU/ASIN with the internal ids
# packing_option_id and packing_group_id here are the "id" fields
# from the plan response (our internal UUIDs), NOT Amazon's packing_option_id.
GET /api/fba-transport/v2024/plans/{plan_id}/packing-group-items \
?packing_option_id={packing_option.id} \
&packing_group_id={packing_group.id}
# Then re-read the plan; fnsku and asin are now populated on the items.
GET /api/fba-transport/v2024/plans/{plan_id}Submit box contents
A box group describes one package size and weight plus the associated Amazon packing group or shipment. Create box groups, set quantities, and add plan items, then call set-packing-information and drive its operation to SUCCESS. Commit the box contents with add-carton-request-to-submission, passing each packing group's id.
There is no mark-packing-information-as-confirmed route. add-carton-request-to-submission is the call that finalizes packing information.
Set packing information
curl -X POST https://YOUR_DOMAIN/api/fba-transport/v2024/plans/{plan}/set-packing-information \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"plan_id": "019953ae-7a0d-7342-956a-881dcdbe42ad"
}'Commit box contents (packing_group_ids = plan response ids)
curl -X POST https://YOUR_DOMAIN/api/fba-transport/v2024/plans/{plan}/add-carton-request-to-submission \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"packing_group_ids": [
"packing-group-1"
]
}'Select and confirm placement
Placement options describe how Amazon wants the plan split across fulfillment centers and what fees or discounts apply. Generate options, select one, then confirm it (dispatch, poll, commit). If you regenerate or re-pull placement options after confirming, the confirmation is cleared along with any delivery-window and transportation selections, so do not regenerate placement once you have moved past it.
After placement is confirmed, call set-boxes-ids. It requires a confirmed placement and assigns Amazon box IDs to the shipment. Until it runs, the boxes have contents on our side but are not assigned on Amazon, and confirm-transportation-option and request-box-labels reject with “Boxes are assigned incorrectly for the shipment.”
Amazon assigns the per-box amazon_box_id (the FBA… value) shortly after placement is confirmed, and set-boxes-ids is what reads it from Amazon and stamps it onto the box. Read the stamped id from get-shipment, in the boxes[].amazon_box_id field (or nested under placement_options[].shipments[].boxes[] on the plan response). Do not poll list-shipment-boxes for it: that endpoint returns box-group geometry and contents only and has no box-id field, so the id never appears there. If boxes[] is empty on get-shipment, set-boxes-ids has not run since placement was confirmed.
Select placement option
curl -X POST https://YOUR_DOMAIN/api/fba-transport/v2024/plans/{plan}/select-placement-option \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"plan_id": "019953ae-7a0d-7342-956a-881dcdbe42ad",
"placement_option_id": "placementOptionId-abc123"
}'Confirm placement option
curl -X POST https://YOUR_DOMAIN/api/fba-transport/v2024/plans/{plan}/confirm-placement-option \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"plan_id": "019953ae-7a0d-7342-956a-881dcdbe42ad",
"placement_option_id": "placementOptionId-abc123"
}'Commit: mark placement option as confirmed
curl -X POST https://YOUR_DOMAIN/api/fba-transport/v2024/plans/{plan}/mark-placement-option-as-confirmed \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"plan_id": "019953ae-7a0d-7342-956a-881dcdbe42ad",
"placement_option_id": "placementOptionId-abc123"
}'Assign Amazon box ids
curl -G https://YOUR_DOMAIN/api/fba-transport/v2024/plans/{plan}/set-boxes-ids \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json"Delivery window (non-partnered)
When a transportation option lists PRECONDITIONS: CONFIRMED_DELIVERY_WINDOW (typical for non-partnered shipments), confirm a delivery window first. Set the first availability date and transport mode, generate windows, then confirm and commit the chosen window.
The generate-delivery-window operation can sit at IN_PROGRESS even after the windows are listable — this is Amazon eventual consistency. It is safe to pick from the listed options; what matters is that the confirm-delivery-window-option operation reaches SUCCESS, then run the mark step.
Delivery window sequence
# Per shipment, for non-partnered (and where the transportation
# option lists PRECONDITIONS: CONFIRMED_DELIVERY_WINDOW)
POST set-first-availability-date
POST set-transport-mode-preference
POST generate-delivery-window-options # -> operation, poll to SUCCESS
GET delivery-window-options
POST confirm-delivery-window-option # -> operation, poll to SUCCESS
POST mark-delivery-window-option-as-confirmed # COMMIT (local)Confirm delivery window option
curl -X POST https://YOUR_DOMAIN/api/fba-transport/v2024/plans/{plan}/confirm-delivery-window-option \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"plan_id": "019953ae-7a0d-7342-956a-881dcdbe42ad",
"shipment_id": "shipmentId-abc123",
"delivery_window_option_id": "deliveryWindowOptionId-abc123"
}'Commit: mark delivery window option as confirmed
curl -X POST https://YOUR_DOMAIN/api/fba-transport/v2024/plans/{plan}/mark-delivery-window-option-as-confirmed \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"plan_id": "019953ae-7a0d-7342-956a-881dcdbe42ad",
"shipment_id": "shipmentId-abc123",
"delivery_window_option_id": "deliveryWindowOptionId-abc123"
}'Confirm transportation
Generate transportation options after placement is confirmed and boxes are assigned. Confirm one option per shipment (shipment_transportation_ids), drive the operation to SUCCESS, then call mark-transportation-option-as-confirmed. Transportation options can expire; regenerate if they are more than about 15 minutes old.
The “ConfirmTransportationOptions already in progress” lock. confirm-transportation-option takes a lock so a second confirm cannot race it. Driving the operation to SUCCESS does not release it — only mark-transportation-option-as-confirmed does. Call confirm-transportation-option once per attempt and follow it with the mark step. Re-firing confirm before marking just bounces off the lock, so the option never commits and downstream calls (including update-tracking-small-details) fail.
generate-transportation-options takes the same lock. It holds the lock until you drive the generate operation to SUCCESS with GET /operations/{id}/update (or until it expires a few minutes later). So if you call confirm-transportation-option after generating without first polling the generate operation to completion, confirm returns “already in progress” with no operation id — even though you never re-fired confirm. Always drive the generate operation to SUCCESS before confirming, and do not regenerate transportation between the confirm and mark steps.
Transportation sequence
# Placement must already be confirmed (mark-placement-option-as-confirmed)
GET set-boxes-ids # boxes must be assigned first
POST generate-transportation-options # -> operation, poll to SUCCESS
GET transportation-options
POST confirm-transportation-option # -> operation, poll to SUCCESS
POST mark-transportation-option-as-confirmed # COMMIT + releases the lockGenerate transportation options
curl -X POST https://YOUR_DOMAIN/api/fba-transport/v2024/plans/{plan}/generate-transportation-options \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"plan_id": "019953ae-7a0d-7342-956a-881dcdbe42ad",
"placement_option_id": "placementOptionId-abc123",
"shipments_dates": [
{
"shipment_id": "shipmentId-abc123",
"start_date": "2024-06-01"
}
]
}'Confirm transportation option
curl -X POST https://YOUR_DOMAIN/api/fba-transport/v2024/plans/{plan}/confirm-transportation-option \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"plan_id": "019953ae-7a0d-7342-956a-881dcdbe42ad",
"idempotency_key": "confirm-transportation:FbaTransport_ConfirmTransportationOptions_019953ae",
"shipment_transportation_ids": [
{
"shipment_id": "shipmentId-abc123",
"transportation_option_id": "transportationOptionId-abc123"
}
]
}'Commit: mark transportation option as confirmed
curl -X POST https://YOUR_DOMAIN/api/fba-transport/v2024/plans/{plan}/mark-transportation-option-as-confirmed \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"plan_id": "019953ae-7a0d-7342-956a-881dcdbe42ad",
"shipment_transportation_ids": [
{
"shipment_id": "shipmentId-abc123",
"transportation_option_id": "transportationOptionId-abc123"
}
]
}'Hazmat and non-partnered shipping
For dangerous goods, Amazon does not quote a partnered carrier, so generate-transportation-options can run for a couple of minutes and return zero partnered options. That is expected. The signal to go non-partnered is the option set coming back entirely as USE_YOUR_OWN_CARRIER with no partnered option present; confirm the own-carrier option (for example Deutsche Post DHL), then write tracking.
update-tracking-small-details requires a confirmed non-PCP (own-carrier) transportation option — otherwise Amazon returns “can only be processed for shipments that have confirmed a non-PCP transportation option.” Send the box's amazon_box_id (the FBA… value), not the internal box id. Read that id from get-shipment (boxes[].amazon_box_id). If it is empty, run set-boxes-ids (it needs a confirmed placement) to stamp it, then re-read get-shipment.
Small parcel tracking (amazon_box_id)
curl -X POST https://YOUR_DOMAIN/api/fba-transport/v2024/plans/{plan}/update-tracking-small-details \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"plan_id": "019953ae-7a0d-7342-956a-881dcdbe42ad",
"shipment_id": "shipment-1",
"box_tracking_ids": [
{
"amazon_box_id": "FBA1U000001",
"tracking_id": "1Z999AA10123456784"
}
]
}'2D barcodes and box labels
A plan can use Amazon 2D barcodes for box content: set-packing-information sends contentInformationSource: BARCODE_2D for each box. On a 2D-barcode plan the 4x6 box label is generated by PrepBusiness from the box's FNSKU and contents rather than requested from Amazon, so request-box-labels produces the label without an Amazon-assigned box id. Every item in the box must have a stamped FNSKU, or the call rejects with “one or more items are missing an FNSKU.”
set-boxes-ids is a single GET with no body and no operation to poll — the 202 only means it finished. It matches the boxes Amazon has returned to your plan boxes and stamps each amazon_box_id onto the box; it does not create boxes on Amazon. Once placement is confirmed, run set-boxes-ids, then read the stamped id from get-shipment (boxes[].amazon_box_id), not from list-shipment-boxes.
Request labels and paperwork
Request labels only after transportation is confirmed (and, for non-partnered, after tracking is saved). FNSKUs must already be stamped or box labels reject. Labels generate asynchronously, so the first poll of the labels endpoint usually returns nothing.
Label sequence
# Non-partnered: write tracking BEFORE requesting box labels
POST update-tracking-small-details # -> operation, poll to SUCCESS
POST request-box-labels
GET labels?shipment_id={shipment_id} # poll until the 4x6 box label is ready
# Partnered small parcel
POST request-package-labels
GET labels?shipment_id={shipment_id}
POST labels/{label_id}/resize-to-4x6 # optional 4x6 resize
# LTL
POST request-pallet-labels
POST request-bill-of-ladingRequest package labels
curl -X POST https://YOUR_DOMAIN/api/fba-transport/v2024/plans/{plan}/request-package-labels \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"shipment_id": "shipment-1"
}'Reading per-item errors
When a plan will not progress, the per-item reasons the dashboard shows are not on GET /plans/{plan_id}. There are two sources. Item eligibility, approval, and compliance (for example “Approval is required before this item can be sent”) come from the eligibility endpoints, keyed by ASIN, in ineligibilityReasons. Rejections from plan creation come from the create-plan operation's problems array, with the SKU or FNSKU inside the message and details.
Where per-item errors live
# 1. Item eligibility / approval / compliance (keyed by ASIN)
GET /api/fba-transport/v2024/fetch-item-eligibility?channel_id=...&asin=...
=> { "asin": "...", "isEligible": false, "ineligibilityReasons": ["FBA_INB_0021 ..."] }
# 2. Plan-creation rejections live on the create-plan operation, not the plan
GET /api/fba-transport/v2024/operations/{create_operation_id}
=> { "status": "...", "problems": [ { "code": "...", "message": "...", "details": "...", "severity": "..." } ] }Cancel a plan
Canceling is its own dispatch-poll-commit flow. Run cancel-plan-checker first for warnings (for example a confirmed partnered carrier), then cancel-plan, drive its operation to SUCCESS, then mark-plan-as-canceled. The plan stays ACTIVE until the mark step sets its status to voided locally — cancel-plan succeeding on its own does not flip is_canceled.
Cancel sequence
POST cancel-plan-checker # returns warnings (e.g. confirmed partnered carrier)
POST cancel-plan # -> operation, poll to SUCCESS
POST mark-plan-as-canceled # COMMIT: sets status = Voided locallyCheck before canceling
curl -X POST https://YOUR_DOMAIN/api/fba-transport/v2024/plans/{plan}/cancel-plan-checker \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"plan_id": "019953ae-7a0d-7342-956a-881dcdbe42ad"
}'Cancel plan
curl -X POST https://YOUR_DOMAIN/api/fba-transport/v2024/plans/{plan}/cancel-plan \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"plan_id": "019953ae-7a0d-7342-956a-881dcdbe42ad"
}'Commit: mark plan as canceled
curl -X POST https://YOUR_DOMAIN/api/fba-transport/v2024/plans/{plan}/mark-plan-as-canceled \
-H "Authorization: Bearer {token}" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{}'Troubleshooting
| Symptom | Cause and fix |
|---|---|
Operation SUCCESS but option stays OFFERED / is_packing_information_set stays false | The commit step was skipped. Call the matching select-* / mark-*-as-confirmed (or add-carton-request-to-submission for packing info). |
| “Failed to fetch packing group… Packing option is null” | Wrong id on packing-group-items. Send the internal id for the option and group, not Amazon's packing_option_id. |
fnsku/asin stay null on the plan item | Call packing-group-items (or list-shipment-items) with the correct ids; nothing back-fills on a timer. |
| “Boxes are assigned incorrectly for the shipment” | Boxes not assigned on Amazon. Confirm placement, then GET set-boxes-ids, then retry. |
No amazon_box_id on the box | Boxes only carry an amazon_box_id after set-boxes-ids runs (it needs a confirmed placement). Read it from get-shipment (boxes[].amazon_box_id), not list-shipment-boxes, which never returns the box id. |
| “ConfirmTransportationOptions already in progress” (no operation id returned) | The lock is held. Either you re-fired confirm without marking, or generate-transportation-options still holds it because its operation was not driven to SUCCESS. Drive the generate operation to SUCCESS via GET /operations/{id}/update, then confirm once and call the mark step. |
| “can only be processed for shipments that have confirmed a non-PCP transportation option” | Transportation never committed. Run confirm-transportation-option then mark-transportation-option-as-confirmed before tracking. |
| “one or more items are missing an FNSKU” | FNSKU not stamped yet. Stamp it via packing-group-items, confirm the box has an amazon_box_id, then request labels. |
generate-packing-options SUCCESS with 0 options | Amazon found no valid arrangement (usually item eligibility/compliance). Check eligibility and the create operation's problems; do not re-fire. |
Plan stays ACTIVE after cancel-plan SUCCESS | Call mark-plan-as-canceled to void it locally. |
Recovery and updates
Use POST /plans/{plan_id}/update to pull the latest Amazon state. Use reset endpoints (reset-placement-options, reset-submissions) only when intentionally backing up to an earlier stage, because later selections or submissions are cleared. If Amazon reports placement options were generated in Seller Central, regenerate them through the API before continuing.
