---
slug: create-fbm-listing-with-sp-api
title: "How to Create an FBM Listing Using Amazon SP-API"
summary: "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."
category: amazon
difficulty: intermediate
estimatedTokens: 7600
lastReviewed: "2026-08-03"
prerequisites:
  - create-amazon-listing-with-sp-api
learningOutcomes:
  - "Create an FBM listing with fulfillment_availability set to DEFAULT + quantity in one PUT"
  - "Bind a listing to a shipping template via merchant_shipping_group"
  - "Sync MFN inventory by updating quantity"
  - "Distinguish ACCEPTED (stored) from BUYABLE (live) and interpret the 100521 review gate"
---

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](/learn/create-amazon-listing-with-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`:

| Channel | `fulfillment_availability` | `quantity`? | Extra required attr | Buyable when |
| --- | --- | --- | --- | --- |
| **FBM (this page)** | `[{ "fulfillment_channel_code": "DEFAULT", "quantity": N }]` | **yes** | `merchant_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:

```ts
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:

```json
"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:

```json
{
  "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](/learn/create-fba-listing-with-sp-api).
- **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](/learn/create-amazon-listing-with-sp-api).
