When to use:
- NFT drops and launches
- In-game item purchases
- Digital collectible storefronts
How it works
- Select and connect - The buyer chooses items in the Sale widget and connects their wallet.
- Quote - The widget requests pricing from Immutable’s API.
- Authorize and sign - At checkout, Immutable authorizes the order and signs the purchase transaction.
- Pay - The buyer pays. The signed transaction is submitted on-chain by their wallet for crypto, or by Transak for card payments.
- Mint and confirm - The NFT is minted to the buyer’s wallet as part of the on-chain transaction. Immutable then notifies your Confirmation webhook, if configured.
Prerequisites
- Hub project with an environment
- ERC-721 or ERC-1155 collection deployed
Setup
1
Configure in Hub
Go to Hub → Primary Sales to deploy a Primary Sales contract, configure payment currencies and payout addresses, and choose between Simplified or Advanced mode.
2
Integrate the widget
Mount the Sale widget in your frontend to handle the checkout flow.
3
Handle backend (Advanced mode only)
Implement webhook endpoints if using Advanced mode for dynamic pricing or custom logic.
Widget integration
- npm
- yarn
Parameters
Events
Each event payload has atype discriminator and a data object. Check payload.type before reading payload.data, since SUCCESS and FAILURE also fire for other steps in the flow (for example SALE_TRANSACTION_SUCCESS).
Sale modes
- Simplified
- Advanced
Create and manage products directly in Hub—no backend required.Create or update a productUpserts a single product by ID (pricing, stock, metadata, linked collection and token rules, etc.). Same URL for create and update.Use your real collection address from Hub in place of the placeholder AuthenticationListing products is public; create, update, and delete require authentication. Authentication requires either:
- Products created in Hub are automatically available for sale
- Stock management handled by the widget
- Token IDs auto-generated for ERC-721, specified for ERC-1155
PUT replaces the entire product record for that product_id. Send a JSON object with the shape below. A successful response returns the persisted product (same fields as GET /products).ProductPricing (
pricing[])Each element defines a price in one currency:Collection (
collection)Limits (
limits)Example (ERC-721)
collection_address.Delete a productRemoves a product from the catalog for that environment.Navigate to the Settings page of the project environment to create a secret API key. Do not expose secret keys in client-side code. Only manage products from your backend or Hub.Metadata for minted tokens:
- ERC-1155: Pre-define metadata for the Token ID before sales begin
- ERC-721: Set up a webhook in Hub for
imtbl_zkevm_activity_mintevents, then generate metadata and call the Metadata Refresh API
Webhooks
Primary Sales has two kinds of webhooks. You configure each one as an independent URL in Hub, each with its own optional API key, and Immutable’s backend calls them server-to-server (sending the API key in theAuthorization header). There is no shared base URL — the paths shown below are illustrative.
Dynamic sale webhooks — used only in Advanced (dynamic) sale mode:
- Quote — Immutable calls your backend for live pricing.
- Authorize — Immutable calls your backend to authorize and reserve the order.
- Confirmation — sent in both Simplified and Advanced modes once the purchase transaction executes on-chain. Use it to fulfil, mint, or record the completed order.
- Expiration — sent when an authorized order expires without payment, in Advanced mode only. In Simplified mode, Immutable releases the reserved stock automatically and sends no callback.
Quote
Quote
Advanced mode only. Called when a user requests a price quote for items.Method:
POST to your configured Quote URL.- Request/Response
- Implementation
Request:The buyer can pay in any settlement currency you’ve configured for the project in Hub, and the widget builds its currency options from that Hub configuration—not from this response. Your quote must therefore return both a per-product
recipient_address may be null when the buyer’s wallet is not yet known.Response:pricing row and a totals row for every configured currency. In the example above the project accepts both USDC and IMX, so each product is priced in both and totals carries a row for each.Each product’s pricing entries cover that single product (unit price times its quantity). Each totals entry is the sum across all products for that currency.Confirm
Confirm
Sent in both Simplified and Advanced modes after the purchase transaction executes on-chain. Mint or fulfil the order here.Method:
POST to your configured Confirmation URL.- Request/Response
- Implementation
Request:Order fields are nested under
order. tx_hash, recipient_address, and custom_data are sent when available. total_amount and each detail[].amount are numbers; deadline and created_at are Unix timestamps (seconds and milliseconds respectively).Response:The response body is ignored — only the HTTP status code matters. Return 2xx (or 204) to confirm the order; a 404 marks it unconfirmed, and 5xx triggers a retry. A simple acknowledgement is fine:Expired
Expired
Advanced mode only. Sent when an authorized order expires without payment. Release reserved inventory. In Simplified mode, Immutable restocks automatically and this webhook is not called.Method:
POST to your configured Expiration URL.- Request/Response
- Implementation
Request:The expiry request contains only the
reference. Look up the order by reference to find the products and inventory to release.Response:The response body is ignored — only the HTTP status code matters. Return 2xx (or 204) to acknowledge; 5xx triggers a retry. A simple acknowledgement is fine:Webhook security
Immutable authenticates every webhook call with the API key you configure for that webhook in Hub, sent in theAuthorization header. Each webhook (quote, authorize, confirmation, expiration) has its own key. Reject any request whose Authorization header doesn’t match:
Payment options
Crypto
Users can pay with tokens on Immutable Chain. The accepted tokens can be configured through Hub. Default currencies are USDC and IMX. Custom ERC-20 tokens must be whitelisted. If users don’t have enough tokens, the widget offers swap, bridge, or fiat onramp options.Fiat
Card payments are handled through Transak, Immutable’s fiat payment partner. When a buyer selects Debit Card or Credit Card in the widget, the purchase is completed in an embedded Transak checkout, which accepts cards as well as Apple Pay and Google Pay where the buyer’s device and region support them. Funds settle to your configured payout wallet in the sale’s settlement currency (commonly USDC, depending on your Hub configuration). KYC may be required for larger amounts.Card payments currently process one NFT per order.
Limits
Next steps
Minting API
Mint NFTs from your backend
Hub Webhooks
Configure webhooks for mint events
ERC-721 Contracts
Deploy NFT collections
Orderbook
Enable secondary trading