create_order
Create a Shopify order directly with variant or custom line items. Use for importing historical orders or recording offline sales without checkout pricing.
Instructions
Create a real Shopify order directly, bypassing the draft-order flow. Each line item is either a variant reference (variantId + quantity) or a custom item (title + priceSet + quantity). Use when you need to import historical orders, record a phone/in-person sale, or create an order without involving Shopify's checkout pricing engine. For interactive carts where Shopify should compute taxes/shipping/discounts, use create_draft_order then complete_draft_order instead. Defaults: PENDING financial status, customer not notified, inventory decremented respecting each variant's oversell policy.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| lineItems | Yes | At least one line item. Each is either a variant reference (variantId + quantity) or a custom item (title + priceSet + quantity). Use draft orders (create_draft_order → complete_draft_order) when you want Shopify to handle pricing/taxes automatically; use this tool when you need to create an order directly with explicit line-item pricing. | |
| No | Customer email for the order. Recommended even when customerId is set. | ||
| customerId | No | GID of an existing customer to attach. Get one from list_customers. Optional. | |
| tags | No | Tags applied to the new order. | |
| note | No | Internal staff-only note attached to the order. | |
| financialStatus | No | Initial financial status to record. Defaults to PENDING if omitted. Use PAID when capturing payment outside Shopify (manual offline payment). | |
| sendReceipt | No | Whether to email the customer a receipt for the new order. | |
| inventoryBehaviour | No | How inventory is handled. BYPASS: don't touch inventory. DECREMENT_OBEYING_POLICY (default): decrement and respect each variant's oversell policy. DECREMENT_IGNORING_POLICY: decrement always, even past zero. |
Implementation Reference
- src/tools/orders.ts:374-427 (handler)The async handler function for the create_order tool. It constructs line items from args (variantId+quantity or title+priceSet+quantity), builds the order input and options objects, calls the Shopify OrderCreate mutation via GraphQL, handles user errors, and returns a success message with order name, ID, and total.
async (args) => { const lineItems = args.lineItems.map((li) => { if (li.variantId) { return { variantId: li.variantId, quantity: li.quantity }; } return { title: li.title!, quantity: li.quantity, priceSet: li.priceSet!, }; }); const order: Record<string, unknown> = { lineItems }; if (args.email) order.email = args.email; if (args.customerId) order.customerId = args.customerId; if (args.tags) order.tags = args.tags; if (args.note) order.note = args.note; if (args.financialStatus) order.financialStatus = args.financialStatus; const options: Record<string, unknown> = {}; if (args.sendReceipt !== undefined) options.sendReceipt = args.sendReceipt; if (args.inventoryBehaviour) options.inventoryBehaviour = args.inventoryBehaviour; const data = await client.graphql<{ orderCreate: { order: Order | null; userErrors: ShopifyUserError[]; }; }>(ORDER_CREATE_MUTATION, { order, options: Object.keys(options).length > 0 ? options : undefined, }); throwIfUserErrors(data.orderCreate.userErrors, "orderCreate"); const o = data.orderCreate.order; if (!o) { return { content: [ { type: "text" as const, text: "orderCreate returned no order." }, ], }; } const total = `${o.totalPriceSet.shopMoney.amount} ${o.totalPriceSet.shopMoney.currencyCode}`; return { content: [ { type: "text" as const, text: [ `Created order ${o.name} [${o.displayFinancialStatus ?? "?"}]`, ` ID: ${o.id}`, ` Total: ${total}`, ].join("\n"), }, ], }; }, - src/tools/orders.ts:175-214 (schema)The Zod-based input schema for create_order defining all allowed fields: lineItems (array of variant or custom items), email, customerId, tags, note, financialStatus, sendReceipt, and inventoryBehaviour.
const createOrderSchema = { lineItems: z .array(orderLineItemSchema) .min(1) .describe( "At least one line item. Each is either a variant reference (variantId + quantity) or a custom item (title + priceSet + quantity). Use draft orders (create_draft_order → complete_draft_order) when you want Shopify to handle pricing/taxes automatically; use this tool when you need to create an order directly with explicit line-item pricing.", ), email: z .string() .email() .optional() .describe("Customer email for the order. Recommended even when customerId is set."), customerId: z .string() .optional() .describe( "GID of an existing customer to attach. Get one from list_customers. Optional.", ), tags: z.array(z.string()).optional().describe("Tags applied to the new order."), note: z .string() .optional() .describe("Internal staff-only note attached to the order."), financialStatus: z .enum(["AUTHORIZED", "PAID", "PARTIALLY_PAID", "PENDING", "REFUNDED", "VOIDED"]) .optional() .describe( "Initial financial status to record. Defaults to PENDING if omitted. Use PAID when capturing payment outside Shopify (manual offline payment).", ), sendReceipt: z .boolean() .optional() .describe("Whether to email the customer a receipt for the new order."), inventoryBehaviour: z .enum(["BYPASS", "DECREMENT_IGNORING_POLICY", "DECREMENT_OBEYING_POLICY"]) .optional() .describe( "How inventory is handled. BYPASS: don't touch inventory. DECREMENT_OBEYING_POLICY (default): decrement and respect each variant's oversell policy. DECREMENT_IGNORING_POLICY: decrement always, even past zero.", ), }; - src/tools/orders.ts:370-428 (registration)Registers the create_order tool with the MCP server via server.tool('create_order', description, createOrderSchema, handler). The description explains use cases, trade-offs vs draft orders, and default behaviours.
server.tool( "create_order", "Create a real Shopify order directly, bypassing the draft-order flow. Each line item is either a variant reference (variantId + quantity) or a custom item (title + priceSet + quantity). Use when you need to import historical orders, record a phone/in-person sale, or create an order without involving Shopify's checkout pricing engine. For interactive carts where Shopify should compute taxes/shipping/discounts, use create_draft_order then complete_draft_order instead. Defaults: PENDING financial status, customer not notified, inventory decremented respecting each variant's oversell policy.", createOrderSchema, async (args) => { const lineItems = args.lineItems.map((li) => { if (li.variantId) { return { variantId: li.variantId, quantity: li.quantity }; } return { title: li.title!, quantity: li.quantity, priceSet: li.priceSet!, }; }); const order: Record<string, unknown> = { lineItems }; if (args.email) order.email = args.email; if (args.customerId) order.customerId = args.customerId; if (args.tags) order.tags = args.tags; if (args.note) order.note = args.note; if (args.financialStatus) order.financialStatus = args.financialStatus; const options: Record<string, unknown> = {}; if (args.sendReceipt !== undefined) options.sendReceipt = args.sendReceipt; if (args.inventoryBehaviour) options.inventoryBehaviour = args.inventoryBehaviour; const data = await client.graphql<{ orderCreate: { order: Order | null; userErrors: ShopifyUserError[]; }; }>(ORDER_CREATE_MUTATION, { order, options: Object.keys(options).length > 0 ? options : undefined, }); throwIfUserErrors(data.orderCreate.userErrors, "orderCreate"); const o = data.orderCreate.order; if (!o) { return { content: [ { type: "text" as const, text: "orderCreate returned no order." }, ], }; } const total = `${o.totalPriceSet.shopMoney.amount} ${o.totalPriceSet.shopMoney.currencyCode}`; return { content: [ { type: "text" as const, text: [ `Created order ${o.name} [${o.displayFinancialStatus ?? "?"}]`, ` ID: ${o.id}`, ` Total: ${total}`, ].join("\n"), }, ], }; }, ); - src/tools/orders.ts:45-72 (handler)The GraphQL mutation (ORDER_CREATE_MUTATION) executed by the create_order handler. It calls orderCreate with order input and optional options, returning the created order with id, name, displayFinancialStatus, totalPriceSet, and userErrors.
const ORDER_CREATE_MUTATION = /* GraphQL */ ` mutation OrderCreate($order: OrderCreateOrderInput!, $options: OrderCreateOptionsInput) { orderCreate(order: $order, options: $options) { order { id name displayFinancialStatus totalPriceSet { shopMoney { amount currencyCode } } } userErrors { field message } } } `; const ORDER_UPDATE_MUTATION = /* GraphQL */ ` mutation OrderUpdate($input: OrderInput!) { orderUpdate(input: $input) { order { id name email tags note } userErrors { field message } } } `; - src/tools/products.ts:333-336 (helper)The toGid helper function used by create_order (imported from ./products.js). It normalizes numeric IDs to Shopify GID format (gid://shopify/Order/123) if not already a GID.
export function toGid(id: string, type: string): string { if (id.startsWith("gid://")) return id; return `gid://shopify/${type}/${id}`; }