create_draft_order
Create an editable Shopify draft order (cart/quote) with catalog variants or custom line items. Optionally assign a customer, email, note, tags, or copy default address. Returns draft GID and an invoice URL for payment.
Instructions
Create a new draft order — Shopify's term for an editable cart/quote not yet placed as an order. Each line item is EITHER a variant reference (variantId + quantity) for catalog products, OR a custom item (title + originalUnitPrice + quantity) for one-off charges or services not in the catalog. Optionally attach a customer, email, internal note, tags, and choose whether to copy the customer's default address. Returns the new draft's GID and an invoice URL the customer can use to pay. Drafts stay OPEN until you call complete_draft_order or send the invoice.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| lineItems | Yes | At least one line item. Each item is EITHER a variant reference (just variantId + quantity) OR a custom item (title + originalUnitPrice + quantity, no variantId). Mixing both shapes in one item is rejected by the refine() validator. | |
| customerId | No | GID of an existing customer to attach to the draft. Get one from list_customers. Optional — drafts can be customer-less and converted to a guest checkout. | |
| No | Email address for the order. Useful when you don't have a customer record yet but want to email the invoice URL. | ||
| note | No | Internal note visible to staff only (not the customer). | |
| tags | No | Tags to apply to the draft for filtering/segmentation. | |
| useCustomerDefaultAddress | No | If true and customerId is set, copy the customer's default shipping address onto the draft. |
Implementation Reference
- src/tools/draft_orders.ts:377-421 (handler)The async handler function for 'create_draft_order'. It builds the input object from args, calls the Shopify GraphQL mutation (CREATE_DRAFT_ORDER_MUTATION), checks for user errors, and returns a formatted response with the draft order name, ID, total, and invoice URL.
async (args) => { const input: Record<string, unknown> = { lineItems: mapLineItemsForInput(args.lineItems), }; if (args.customerId) input.customerId = args.customerId; if (args.email) input.email = args.email; if (args.note) input.note = args.note; if (args.tags) input.tags = args.tags; if (args.useCustomerDefaultAddress !== undefined) { input.useCustomerDefaultAddress = args.useCustomerDefaultAddress; } const data = await client.graphql<{ draftOrderCreate: { draftOrder: DraftOrder | null; userErrors: ShopifyUserError[]; }; }>(CREATE_DRAFT_ORDER_MUTATION, { input }); throwIfUserErrors(data.draftOrderCreate.userErrors, "draftOrderCreate"); const d = data.draftOrderCreate.draftOrder; if (!d) { return { content: [ { type: "text" as const, text: "draftOrderCreate returned no draft order." }, ], }; } const total = `${d.totalPriceSet.shopMoney.amount} ${d.totalPriceSet.shopMoney.currencyCode}`; return { content: [ { type: "text" as const, text: [ `Created draft order ${d.name} [${d.status}]`, ` ID: ${d.id}`, ` Total: ${total}`, d.invoiceUrl ? ` Invoice: ${d.invoiceUrl}` : "", ] .filter(Boolean) .join("\n"), }, ], }; }, ); - src/tools/draft_orders.ts:189-223 (schema)The Zod-based input schema for 'create_draft_order'. Defines parameters: lineItems (array with variantId or custom item), customerId, email, note, tags, useCustomerDefaultAddress.
const createDraftOrderSchema = { lineItems: z .array(lineItemSchema) .min(1) .describe( "At least one line item. Each item is EITHER a variant reference (just variantId + quantity) OR a custom item (title + originalUnitPrice + quantity, no variantId). Mixing both shapes in one item is rejected by the refine() validator.", ), customerId: z .string() .optional() .describe( "GID of an existing customer to attach to the draft. Get one from list_customers. Optional — drafts can be customer-less and converted to a guest checkout.", ), email: z .string() .email() .optional() .describe( "Email address for the order. Useful when you don't have a customer record yet but want to email the invoice URL.", ), note: z .string() .optional() .describe("Internal note visible to staff only (not the customer)."), tags: z .array(z.string()) .optional() .describe("Tags to apply to the draft for filtering/segmentation."), useCustomerDefaultAddress: z .boolean() .optional() .describe( "If true and customerId is set, copy the customer's default shipping address onto the draft.", ), }; - src/tools/draft_orders.ts:373-421 (registration)The registration of 'create_draft_order' via server.tool() in the registerDraftOrderTools function. It binds the tool name, description, schema, and handler.
server.tool( "create_draft_order", "Create a new draft order — Shopify's term for an editable cart/quote not yet placed as an order. Each line item is EITHER a variant reference (variantId + quantity) for catalog products, OR a custom item (title + originalUnitPrice + quantity) for one-off charges or services not in the catalog. Optionally attach a customer, email, internal note, tags, and choose whether to copy the customer's default address. Returns the new draft's GID and an invoice URL the customer can use to pay. Drafts stay OPEN until you call complete_draft_order or send the invoice.", createDraftOrderSchema, async (args) => { const input: Record<string, unknown> = { lineItems: mapLineItemsForInput(args.lineItems), }; if (args.customerId) input.customerId = args.customerId; if (args.email) input.email = args.email; if (args.note) input.note = args.note; if (args.tags) input.tags = args.tags; if (args.useCustomerDefaultAddress !== undefined) { input.useCustomerDefaultAddress = args.useCustomerDefaultAddress; } const data = await client.graphql<{ draftOrderCreate: { draftOrder: DraftOrder | null; userErrors: ShopifyUserError[]; }; }>(CREATE_DRAFT_ORDER_MUTATION, { input }); throwIfUserErrors(data.draftOrderCreate.userErrors, "draftOrderCreate"); const d = data.draftOrderCreate.draftOrder; if (!d) { return { content: [ { type: "text" as const, text: "draftOrderCreate returned no draft order." }, ], }; } const total = `${d.totalPriceSet.shopMoney.amount} ${d.totalPriceSet.shopMoney.currencyCode}`; return { content: [ { type: "text" as const, text: [ `Created draft order ${d.name} [${d.status}]`, ` ID: ${d.id}`, ` Total: ${total}`, d.invoiceUrl ? ` Invoice: ${d.invoiceUrl}` : "", ] .filter(Boolean) .join("\n"), }, ], }; }, ); - src/tools/draft_orders.ts:83-96 (helper)The GraphQL mutation string used by the handler to create a draft order via Shopify's Admin API (draftOrderCreate).
const CREATE_DRAFT_ORDER_MUTATION = /* GraphQL */ ` mutation DraftOrderCreate($input: DraftOrderInput!) { draftOrderCreate(input: $input) { draftOrder { id name status invoiceUrl totalPriceSet { shopMoney { amount currencyCode } } } userErrors { field message } } } `; - src/tools/draft_orders.ts:269-290 (helper)Helper function mapLineItemsForInput that maps line items from Zod-inferred types to Shopify API input format, handling both variant references and custom items.
function mapLineItemsForInput( items: z.infer<typeof lineItemSchema>[] | undefined, ): | Array<{ variantId?: string; quantity: number; title?: string; originalUnitPrice?: string; }> | undefined { if (!items) return undefined; return items.map((li) => { if (li.variantId) { return { variantId: li.variantId, quantity: li.quantity }; } return { title: li.title!, quantity: li.quantity, originalUnitPrice: li.originalUnitPrice!, }; }); }