Unable to Create Draft Order with Custom Prices Using GraphQL API

Topic summary

Main issue: Creating a draft order via Shopify Admin GraphQL fails with “invalid money” for lineItems.originalUnitPrice, despite sending JSON with amount and currencyCode. Error surfaces as invalid MoneyInput and shows Ruby-style “=>” in the message, leading the poster to suspect serialization problems.

Context: draftOrderCreate mutation with DraftOrderInput. MoneyInput is the structure for price values (amount and currency).

Latest update: Documentation indicates originalUnitPrice is deprecated and its type now expects only a number (no currency). Suggested fixes:

  • Provide originalUnitPrice as a numeric amount without currency.
  • Alternatively use originalUnitPriceWithCurrency to include currency explicitly.

Artifacts: Screenshots of the docs and a link to the 2024-10 API mutation docs are provided and central to the guidance.

Actions recommended: Update payload to current field expectations—remove currencyCode when using originalUnitPrice, or switch to originalUnitPriceWithCurrency. Verify against the 2024-10 schema.

Status: No confirmation of resolution yet; the issue appears linked to deprecated field usage and type mismatch rather than JSON formatting.

Summarized with AI on December 16. AI used: gpt-5.

Hello Shopify Community,

I’m encountering an issue while trying to create a draft order with Custom Prices using the GraphQL API. Despite following the documentation, I’m relentlessly running into an issue that I don’t understand why it’s happening.

Here is the most relevant part of terminal response:

00:29:23 │                     remix │ Line items: [
00:29:23 │                     remix │   {
00:29:23 │                     remix │     "variantId": "gid://shopify/ProductVariant/49595202273618",
00:29:23 │                     remix │     "quantity": 1,
00:29:23 │                     remix │     "originalUnitPrice": {
00:29:23 │                     remix │       "amount": "3.50",
00:29:23 │                     remix │       "currencyCode": "GBP"
00:29:23 │                     remix │     }
00:29:23 │                     remix │   },
00:29:23 │                     remix │   {
00:29:23 │                     remix │     "variantId": "gid://shopify/ProductVariant/49595202404690",
00:29:23 │                     remix │     "quantity": 1,
00:29:23 │                     remix │     "originalUnitPrice": {
00:29:23 │                     remix │       "amount": "8.00",
00:29:23 │                     remix │       "currencyCode": "GBP"
00:29:23 │                     remix │     }
00:29:23 │                     remix │   }
00:29:23 │                     remix │ ]
00:29:23 │                     remix │ GraphQL Variables: {
00:29:23 │                     remix │   "input": {
00:29:23 │                     remix │     "lineItems": [
00:29:23 │                     remix │       {
00:29:23 │                     remix │         "variantId": "gid://shopify/ProductVariant/49595202273618",
00:29:23 │                     remix │         "quantity": 1,
00:29:23 │                     remix │         "originalUnitPrice": {
00:29:23 │                     remix │           "amount": "3.50",
00:29:23 │                     remix │           "currencyCode": "GBP"
00:29:23 │                     remix │         }
00:29:23 │                     remix │       },
00:29:23 │                     remix │       {
00:29:23 │                     remix │         "variantId": "gid://shopify/ProductVariant/49595202404690",
00:29:23 │                     remix │         "quantity": 1,
00:29:23 │                     remix │         "originalUnitPrice": {
00:29:23 │                     remix │           "amount": "8.00",
00:29:23 │                     remix │           "currencyCode": "GBP"
00:29:23 │                     remix │         }
00:29:23 │                     remix │       }
00:29:23 │                     remix │     ],
00:29:23 │                     remix │     "useCustomerDefaultAddress": true
00:29:23 │                     remix │   }
00:29:23 │                     remix │ }
00:29:23 │                     remix │ Serialized Payload before sending: {"query":"\n      mutation createDraftOrder($input: DraftOrderInput!) {\n
draftOrderCreate(input: $input) {\n          draftOrder {\n            id\n            invoiceUrl\n          }\n          userErrors {\n            field\n
message\n          }\n        }\n      }\n
","variables":{"input":{"lineItems":[{"variantId":"gid://shopify/ProductVariant/49595202273618","quantity":1,"originalUnitPrice":{"amount":"3.50","currencyCode":"GBP"}},{"vari
antId":"gid://shopify/ProductVariant/49595202404690","quantity":1,"originalUnitPrice":{"amount":"8.00","currencyCode":"GBP"}}],"useCustomerDefaultAddress":true}}}
00:29:23 │                     remix │ Draft Order Response: {
00:29:23 │                     remix │   "errors": [
00:29:23 │                     remix │     {
00:29:23 │                     remix │       "message": "Variable $input of type DraftOrderInput! was provided invalid value for lineItems.0.originalUnitPrice (invalid money  
'{\"amount\" => \"3.50\", \"currencyCode\" => \"GBP\"}'), lineItems.1.originalUnitPrice (invalid money '{\"amount\" => \"8.00\", \"currencyCode\" => \"GBP\"}')",
00:29:23 │                     remix │       "locations": [
00:29:23 │                     remix │         {
00:29:23 │                     remix │           "line": 2,
00:29:23 │                     remix │           "column": 33
00:29:23 │                     remix │         }
00:29:23 │                     remix │       ],
00:29:23 │                     remix │       "extensions": {
00:29:23 │                     remix │         "value": {
00:29:23 │                     remix │           "lineItems": [
00:29:23 │                     remix │             {
00:29:23 │                     remix │               "variantId": "gid://shopify/ProductVariant/49595202273618",
00:29:23 │                     remix │               "quantity": 1,
00:29:23 │                     remix │               "originalUnitPrice": {
00:29:23 │                     remix │                 "amount": "3.50",
00:29:23 │                     remix │                 "currencyCode": "GBP"
00:29:23 │                     remix │               }
00:29:23 │                     remix │             },
00:29:23 │                     remix │             {
00:29:23 │                     remix │               "variantId": "gid://shopify/ProductVariant/49595202404690",
00:29:23 │                     remix │               "quantity": 1,
00:29:23 │                     remix │               "originalUnitPrice": {
00:29:23 │                     remix │                 "amount": "8.00",
00:29:23 │                     remix │                 "currencyCode": "GBP"
00:29:23 │                     remix │               }
00:29:23 │                     remix │             }
00:29:23 │                     remix │           ],
00:29:23 │                     remix │           "useCustomerDefaultAddress": true
00:29:23 │                     remix │         },
00:29:23 │                     remix │         "problems": [
00:29:23 │                     remix │           {
00:29:23 │                     remix │             "path": [
00:29:23 │                     remix │               "lineItems",
00:29:23 │                     remix │               0,
00:29:23 │                     remix │               "originalUnitPrice"
00:29:23 │                     remix │             ],
00:29:23 │                     remix │             "explanation": "invalid money '{\"amount\" => \"3.50\", \"currencyCode\" => \"GBP\"}'",
00:29:23 │                     remix │             "message": "invalid money '{\"amount\" => \"3.50\", \"currencyCode\" => \"GBP\"}'"
00:29:23 │                     remix │           },
00:29:23 │                     remix │           {
00:29:23 │                     remix │             "path": [
00:29:23 │                     remix │               "lineItems",
00:29:23 │                     remix │               1,
00:29:23 │                     remix │               "originalUnitPrice"
00:29:23 │                     remix │             ],
00:29:23 │                     remix │             "explanation": "invalid money '{\"amount\" => \"8.00\", \"currencyCode\" => \"GBP\"}'",
00:29:23 │                     remix │             "message": "invalid money '{\"amount\" => \"8.00\", \"currencyCode\" => \"GBP\"}'"
00:29:23 │                     remix │           }
00:29:23 │                     remix │         ]
00:29:23 │                     remix │       }
00:29:23 │                     remix │     }
00:29:23 │                     remix │   ]
00:29:23 │                     remix │ }

The issue consistently appears around how my originalUnitPrice is being processed. The error that persists is:

"message": "Variable $input of type DraftOrderInput! was provided invalid value for lineItems.0.originalUnitPrice (invalid money  
'{\\"amount\\" => \\"3.50\\", \\"currencyCode\\" => \\"GBP\\"}'), lineItems.1.originalUnitPrice (invalid money '{\\"amount\\" => \\"8.00\\", \\"currencyCode\\" => \\"GBP\\"}')"

As you can see, it says that originalUnitPrice is invalid and it clearly points to an issue with how the MoneyInput is being formatted. As far as my understanding goes, it is not being structured in a way that Shopify API expects - a correctly formatted JSON, using colons (:slight_smile: for key-value pairs. But this error message shows the => format (e.g., “{"amount" => 3.5, "currencyCode" => "GBP"}”) despite all my different efforts to serialize the JSON correctly. Clearly, the => notation is the root problem here since the incorrect format is being flagged as invalid. So, it’s something must be wrong with how the input data is being structured and sent to GraphQL API.

### What I’ve tried

I’ve already tried several different approaches, used fetch, used direct JSON.stringify, and now using axios.

I even tried some extreme approaches such as manually building the JSON payload string instead of relying on JSON.stringify, axios, or fetch, etc. Just to make sure the transformation and structure are exactly as required by Shopify.

I’ve tried to manually serialize the JSON payload to eliminate any hidden transformations.

I was hoping that by manually serialising the JSON payload I’ll pitentially eliminate any hidden transformations or even get any insight into what’s going to happen if I completely bypass any serialization that might be happening within these!

What’s insane is that no matter the method I keep getting again and again the exact same error.

The originalUnitPrice is structured as an object with amount and currencyCode fields. I’ve double-checked that the prices are valid numbers and the currency code is correct.

The variant IDs are correct and exist in my store.

This is how I have structured my code:

export const action: ActionFunction = async ({ request }) => {
  try {
    // Authenticating the app proxy request
    const { admin, session } = await authenticate.public.appProxy(request);
    . . .

    // Ensuring the session has an accessToken
    . . .

    // Extracting the shop domain from the session
    . . .

    // Some other business logic

    // Preparing line items for the draft order
    const lineItems = requestData.items.map((item: DraftOrderItem) => ({
      variantId: `gid://shopify/ProductVariant/${item.variant_id}`,
      quantity: item.quantity,
      originalUnitPrice: {
        amount: parseFloat(item.price).toFixed(2).toString(), // Ensuring amount is formatted as a string with two decimal places
        currencyCode: item.currencyCode || "GBP",
      }
    }));
    console.log('Line items:', JSON.stringify(lineItems, null, 2));

    // GraphQL Variables
    const variables = {
      input: {
        email: requestData.customer_email,
        lineItems: lineItems,
        useCustomerDefaultAddress: true,
      }
    };

    console.log('GraphQL Variables:', JSON.stringify(variables, null, 2));

    // GraphQL mutation
    const query = `
      mutation createDraftOrder($input: DraftOrderInput!) {
        draftOrderCreate(input: $input) {
          draftOrder {
            id
            invoiceUrl
          }
          userErrors {
            field
            message
          }
        }
      }
    `;

    // I'm stringifying the payload directly
    const payload = JSON.stringify({
      query,
      variables
    });
    console.log('Serialized Payload before sending:', payload);

    // Sending the request using axios
    const response = await axios.post(
      `https://${session.shop}/admin/api/2024-07/graphql.json`,
      {
        query,
        variables
      },
      {
        headers: {
          'Content-Type': 'application/json',
          'X-Shopify-Access-Token': session.accessToken
        }
      }
    );

    const draftOrderData = response.data;
    console.log('Draft Order Response:', JSON.stringify(draftOrderData, null, 2));

    // Checking for user errors in the response
    if (draftOrderData.data?.draftOrderCreate?.userErrors?.length > 0) {
      console.error('User errors:', draftOrderData.data.draftOrderCreate.userErrors);
      return json({ error: draftOrderData.data.draftOrderCreate.userErrors[0].message }, { status: 400 });
    }

    // Extracting the invoice URL from the response
    const invoiceUrl = draftOrderData.data?.draftOrderCreate?.draftOrder?.invoiceUrl;

    return json({ checkout_url: invoiceUrl });
  } catch (error) {
    console.error('Error creating draft order:', error);
    return json({ error: 'An error occurred while creating the draft order' }, { status: 500 });
  }
};

### **Questions:**1. Are there any known issues or limitations with setting custom prices for draft orders via the GraphQL API?

  1. Is there an alternative approach to create draft orders with custom prices that I should consider?
  2. Why is my originalUnitPrice being converted to Ruby hash syntax in GraphQL mutation LOL? Is it possible that there’s some intermediary process that I’m not aware of in my environment or library that’s causing this conversion before the data reaches GraphQL API?
  3. Is there a way ensure MoneyInput is correctly formatted for DraftOrderInput in GraphQL API?

It would really help to get specific guidance on the expected format and anything really that points to the correct way to structure the MoneyInput for the originalUnitPrice field within the DraftOrderInput.

Any insights or suggestions would be greatly appreciated. Thank you in advance for your help!

According to the docs “originalUnitPrice” is deprecated and also the type takes a number without currency.

Try either removing currency like the example in the docs.

Or use “originalUnitPriceWithCurrency” instead.

https://shopify.dev/docs/api/admin-graphql/2024-10/mutations/draftOrderCreate