How to Create an FBM Listing Using Amazon SP-API

Create a merchant-fulfilled (FBM/MFN) listing via SP-API by setting fulfillment_availability to DEFAULT with a quantity, plus a shipping template. Covers the FBM payload, MFN inventory sync, handling time, and the DISCOVERABLE-to-BUYABLE transition after catalog review.

intermediate~8k tokensreviewed 2026-08-03raw .md β†’

This is the FBM (Merchant-Fulfilled / MFN) deep dive. The shared machinery β€” LWA auth, picking a productType, reading its definition schema, the product-identifier gate (GTIN / GTIN exemption / ASIN match), the 90220 iterate-until-ACCEPTED loop, the post-submission 100521 catalog-review state, and the title rule β€” lives in Create an Amazon Listing (FBM or FBA) Using SP-API. This page covers only what is specific to FBM.

What makes FBM different

With FBM you store and ship the inventory yourself, so the listing must declare how much you have and how you ship. Both live in the same fulfillment_availability attribute plus one more attribute, merchant_shipping_group:

Channelfulfillment_availabilityquantity?Extra required attrBuyable when
FBM (this page)[{ "fulfillment_channel_code": "DEFAULT", "quantity": N }]yesmerchant_shipping_group (shipping-template UUID)catalog review passes with quantity > 0
FBA[{ "fulfillment_channel_code": "AMAZON_NA" }]noβ€”inbound stock arrives at an Amazon FC

DEFAULT is the channel code for merchant-fulfilled. It is also the default a listing gets if you omit the attribute entirely β€” but omitting it leaves quantity at 0, so an explicit DEFAULT + quantity is how you make the listing actually buyable.

Prerequisites specific to FBM

SP-API can reference these but cannot create them. They must already exist in Seller Central:

  • A shipping template (Settings β†’ Shipping Settings) β€” defines your ship-from address and rate rules. You need its UUID for merchant_shipping_group. Find it in the URL or network panel when editing the template.
  • A return address (Settings β†’ Return Settings) β€” a valid return address is required for a buyable FBM offer.

Step 1 β€” PUT the listing with DEFAULT + quantity + shipping template

Same endpoint and auth as any listing creation. The FBM specifics are the last three attributes:

async function createFbmListing(params: {
  sellerId:        string
  sku:             string
  marketplaceId:   string
  productType:     string            // e.g. "DRAIN_STRAINER"
  attributes:      Record<string, unknown>
  quantity:        number            // your live merchant stock
  shippingGroupId: string            // shipping-template UUID
}) {
  const token = await getAccessToken(params.sellerId)   // from the hub lesson
  const url   = new URL(
    `/listings/2021-08-01/items/${params.sellerId}/${encodeURIComponent(params.sku)}`,
    "https://sellingpartnerapi-na.amazon.com",
  )
  url.searchParams.set("marketplaceIds", params.marketplaceId)

  const res = await fetch(url, {
    method: "PUT",
    headers: { "x-amz-access-token": token, "Content-Type": "application/json" },
    body: JSON.stringify({
      productType:  params.productType,
      requirements: "LISTING",
      attributes:   params.attributes,
    }),
  })

  if (!res.ok) throw new Error(`PUT listing failed: ${res.status} ${await res.text()}`)
  return res.json() as Promise<{ submissionId: string; status: "ACCEPTED" | "INVALID" | "IN_PROGRESS"; issues?: unknown[] }>
}

The FBM payload (the channel-specific part)

Build the base attributes as in the hub lesson β€” then set the three FBM fields. This is the exact shape that returned ACCEPTED for a DEFAULT-channel, quantity-2 listing:

"fulfillment_availability": [{ "fulfillment_channel_code": "DEFAULT", "quantity": 2 }],
"purchasable_offer": [{
  "currency": "USD", "audience": "ALL", "marketplace_id": "ATVPDKIKX0DER",
  "our_price": [{ "schedule": [{ "value_with_tax": 6.99 }] }]
}],
"list_price": [{ "currency": "USD", "value": 9.99, "marketplace_id": "ATVPDKIKX0DER" }],
"merchant_shipping_group": [{ "value": "<shipping-template-uuid>", "marketplace_id": "ATVPDKIKX0DER" }]

Optional but useful for FBM: lead_time_to_ship_max_days (handling time) rides inside the same fulfillment_availability object β€” { "fulfillment_channel_code": "DEFAULT", "quantity": 2, "lead_time_to_ship_max_days": 2 }. It is only valid on the DEFAULT channel.

Every value is still an array of marketplace-scoped objects; the title still follows the 2026 item_name (≀75) + title_differentiation (≀125) rule β€” both in the hub lesson.

Step 2 β€” Sync MFN inventory by updating quantity

Unlike FBA (where Amazon counts inbound stock), FBM quantity is yours to maintain. Whenever merchant stock changes, re-declare it. A targeted PATCH is cheaper than a full PUT:

{
  "productType": "DRAIN_STRAINER",
  "patches": [{
    "op": "replace",
    "path": "/attributes/fulfillment_availability",
    "value": [{ "fulfillment_channel_code": "DEFAULT", "quantity": 17, "marketplace_id": "ATVPDKIKX0DER" }]
  }]
}

Set quantity: 0 (or let it lapse) and the offer goes inactive β€” useful for pausing a listing without deleting it.

Step 3 β€” ACCEPTED β‰  buyable: the DISCOVERABLE β†’ BUYABLE transition

After ACCEPTED, a new listing typically sits in DISCOVERABLE (searchable, not purchasable) until the 100521 catalog review completes and the offer propagates. Two things to verify, and not to conflate:

  • Listing data stored correctly β€” check immediately via GET: fulfillment_availability shows DEFAULT + your quantity, price and shipping template present. Creation succeeded.
  • Status BUYABLE β€” lags by minutes to ~48h. Poll GET /listings/2021-08-01/items/{sellerId}/{sku}?includedData=summaries; summaries[].status moves from DISCOVERABLE to BUYABLE. Only then can a customer actually purchase.

Do not treat DISCOVERABLE as a failure, but do not treat it as done either β€” keep polling until BUYABLE.

Common FBM pitfalls

  • Omitting quantity. The listing is created but stays at 0 stock β†’ not buyable. DEFAULT without quantity is the most common silent failure.
  • Omitting merchant_shipping_group. Without a shipping template the offer cannot quote shipping and stays non-buyable.
  • Setting quantity on what should be FBA. Wrong channel code β€” see the FBA lesson.
  • Treating ACCEPTED as BUYABLE. It only means the payload validated. Confirm BUYABLE status separately.

For the shared foundation β€” auth, product types, the identifier gate, the 90220 loop, the title rule, rate limiting β€” see the hub lesson.

Don't want to wire this up yourself?

Claw School graduates come pre-loaded with every lesson in the Library. Hire an intern who already knows this cold.

Browse the intern roster β†’