# Create and publish a product Publish one Copenhagen Lamp with a black variant and verify that a buyer can purchase it in your configured store. The sequence matters: creating content, pricing it, and publishing it are separate operations. ## Before you begin Complete [Configure a store](/guides/catalog/configure-store) and retain its IDs. Use an Admin credential with the required catalog permissions. If you created the lamp in the [Admin quickstart](/getting-started/admin-quickstart), use that product ID and skip step 1. ## 1. Create the product Admin API ```graphql mutation CreateLamp($input: ProductCreateInput!) { productCreate(input: $input) { product { id name slug status } errors { __typename ... on UserError { message } } } } ``` Variables ```json { "input": { "name": "Copenhagen Lamp", "slug": "copenhagen-lamp", "status": "DRAFT", "tags": [ "lighting" ] } } ``` Save the returned product ID only after checking payload errors. After an uncertain response, inspect the catalog before repeating creation. ## 2. Create its sellable variant Admin API ```graphql mutation CreateLampVariant($input: ProductVariantsBulkCreateInput!) { productVariantsBulkCreate(input: $input) { productVariants { id name sku status } errors { __typename ... on UserError { message } } } } ``` Variables ```json { "input": { "productId": "", "variants": [ { "name": "Black", "sku": "LAMP-BLACK", "status": "ACTIVE" } ] } } ``` Keep the variant ID and SKU. The cart will use the variant ID; inventory setup will use the SKU. For several selectable finishes, use [attributes](/concepts/catalog/attributes) instead of relying only on variant labels. ## 3. Add price and stock Follow [Configure prices, inventory, and tax](/guides/catalog/configure-commerce) with this variant ID and SKU. It supplies complete inputs for the EUR 129.00 example price and ten tracked units. Return here once those writes succeed. ## 4. Activate the product Admin API ```graphql mutation ActivateLamp($input: ProductUpdateInput!) { productUpdate(input: $input) { product { id status } errors { __typename ... on UserError { message } } } } ``` Variables ```json { "input": { "id": "", "status": "ACTIVE" } } ``` The variant created above is already active. An existing draft variant needs its own status update before it can be sold. ## 5. Publish both levels Admin API ```graphql mutation PublishLamp($input: ProductPublishInput!) { productPublish(input: $input) { product { id } errors { __typename ... on UserError { message } } } } ``` Variables ```json { "input": { "productId": "", "publications": [ { "channelId": "", "publishedAt": "", "availableForPurchaseAt": "" } ] } } ``` Admin API ```graphql mutation PublishLampVariant($input: ProductVariantPublishInput!) { productVariantPublish(input: $input) { product { id } errors { __typename ... on UserError { message } } } } ``` Variables ```json { "input": { "productId": "", "variantId": "", "publications": [ { "channelId": "", "publishedAt": "", "availableForPurchaseAt": "" } ] } } ``` Replace each timestamp placeholder with an ISO 8601 UTC timestamp, such as the intended launch time. Use a time that has already arrived when verifying immediate availability. Publishing the product does not implicitly publish its variants. ## Verify through Storefront Run the product read in [Your first order](/getting-started/first-order) using the store, price channel, EUR, and DK context from setup. Confirm SKU `LAMP-BLACK`, a usable price, and `availableForPurchase: true`. If a condition fails, use [Catalog visibility](/concepts/catalog/publication) to diagnose that layer.