Streamlining Custom Design Requests: Your Guide to Shopify Customer Accounts & Draft Orders

Hey store owners! As a Shopify migration expert, I spend a lot of time digging through the community forums, and every now and then, a thread pops up that’s just gold. It tackles a common challenge in a really specific way, and the collective wisdom shared is invaluable. Recently, I stumbled upon a fantastic discussion about building a "Requested Custom Designs" page right within customer accounts, using Shopify's Draft Orders. It's a brilliant idea for businesses offering custom products, like the rug store owner, vedant_shopify_dev, who kicked off the thread.

vedant_shopify_dev's goal was clear: create a dedicated page for customers to track their custom design requests, much like they track regular orders. The proposed workflow involved creating a Draft Order right after a customer submitted their design specs. Sounds logical, right? But as always with custom development on Shopify, the devil's in the details. Let's break down the community's insights and give you a clear path forward.

TOS Compliance: Clearing the Air on Draft Orders

One of the first big questions vedant_shopify_dev had was about Shopify's Terms of Service (TOS). Is it okay to use Draft Orders for an inquiry or quote flow, especially when no immediate payment is involved and the customer isn't going through the standard checkout process?

The good news, as M.Rahman clarified, is a resounding "Yes, 100% compliant!" It turns out, using Draft Orders for custom design quotes, inquiries, or manual invoices is actually a standard practice in B2B and custom e-commerce. The TOS violation typically comes into play if you're trying to bypass Shopify Checkout for standard product sales by using an external payment gateway. As long as your final invoice link directs the customer back to Shopify Checkout (via the invoice_url) to complete their payment once the custom design is approved and priced, you're perfectly fine. So, breathe easy on that front!

The Architecture Challenge: Displaying Custom Requests Securely

Now, while using Draft Orders for tracking is TOS-compliant, actually *displaying* them to customers in their account area is where things get a bit more complex. vedant_shopify_dev suggested using the Customer Account API's UI Extensions, specifically the customer-account.page.render target, to build this custom page. This is absolutely the right direction for the frontend UI, giving you a dedicated, navigable page in the customer account menu.

However, here's the crucial point that M.Rahman and LucasMao highlighted: Draft Orders are Admin-Only Objects. This means they are not exposed in the Storefront API. Your client-side UI Extension, which uses storefront tokens, simply cannot query draftOrders directly for security reasons. This is a common pitfall, and it means you can't just fetch them directly from your UI Extension.

The Recommended Approach: App Proxy + Secure Backend

To securely display customer-specific Draft Orders, the community strongly recommends an architecture involving a custom backend service paired with a Shopify App Proxy. This ensures sensitive Admin API calls are never exposed client-side.

Here’s how to implement it:

  1. Set up your Customer Account UI Extension: Use the customer-account.page.render target. This creates a new, full-page experience in the customer account area, appearing next to "Orders" in the navigation.

    • Expert Tip from lumine: A full-page target like customer-account.page.render cannot coexist with any other target in the same extension. If you need other blocks elsewhere, they'll require a separate [[extensions.targeting]] entry. Also, for routing inside this page, you'll use Shopify's Navigation API with custom protocols, not your own router.
  2. Build a Lightweight Backend Service: This could be a Node.js, Remix, or Next.js application. If you’re building a custom Shopify App, you'll leverage an App Proxy (e.g., /apps/custom-requests) to route requests securely.

  3. Secure Customer Authentication: When a logged-in customer visits your custom page, your UI Extension will send their session token to your backend. Your backend then:

    • Verifies the Session Token API's signed JWT (JSON Web Token) using your app's shared secret.
    • The dest claim in the JWT confirms which shop the request came from.
    • The sub claim carries the customer's Global ID (GID). Be aware, as lumine pointed out, this sub claim is only present if your app has access to protected customer data. If you haven't gone through that approval, your backend can verify the shop but not the specific customer via GID, so plan your customer identification strategy carefully.
  4. Query Draft Orders via Admin GraphQL API: Once your backend has authenticated the customer and confirmed the shop, it can securely query the Shopify Admin GraphQL API for that customer's draft orders.

    query getCustomerDraftOrders {
      draftOrders(first: 10, query: "customerId:YOUR_NUMERIC_LEGACY_CUSTOMER_ID") {
        edges {
          node {
            id
            name
            status
            invoiceUrl
            lineItems(first: 10) {
              edges {
                node {
                  title
                  customAttributes {
                    key
                    value
                  }
                }
              }
            }
          }
        }
      }
    }
    
    • Important Correction from lumine: The correct search field for querying draft orders by customer is customerId with the numeric legacy ID, not customer_id. Using the wrong field will return an empty connection, making it look like the customer has no requests.
  5. Return Data to Frontend: Your backend processes the Admin API response and sends the structured data back to your UI Extension, which then renders the "Requested Custom Designs" page.

The Alternative: Metafields for Request Lifecycle Management

Both LucasMao and lumine raised a "bigger question" that's worth considering: Should a Draft Order exist at the very beginning of the request process? They suggest separating the design request record from the payment object. A request that goes through multiple revisions or gets rejected doesn't quite fit the OPEN, INVOICE_SENT, and COMPLETED statuses of a Draft Order.

Here’s the alternative:

  • Store Request Data in Metaobjects or Customer Metafields: Instead of creating a Draft Order immediately, store the initial custom design request details (including its status, like "Pending Review," "Revision Requested," "Quote Sent") in a custom database or directly within Shopify Metaobjects or Customer Metafields. Metaobjects are particularly powerful for this kind of structured custom data.

  • Create Draft Order ONLY When Price is Agreed: Once the design is approved and priced, and the customer is ready to proceed to payment, *then* create the Draft Order. This keeps your merchant's Draft Orders list clean of abandoned or pending requests and allows for a more granular status tracking of the design request itself.

This approach gives you more flexibility and a clearer separation of concerns. If you're just starting your journey into building powerful custom features on Shopify, remember that a strong foundation is key. You can start your own Shopify store today and explore these advanced capabilities.

Ultimately, the best solution combines these insights: use Customer Account UI Extensions for the frontend, a secure backend (with an App Proxy) to handle Admin API calls, and carefully consider whether to use Metafields for the initial request lifecycle before converting to a Draft Order for payment. This hybrid approach gives you the best of both worlds: a seamless customer experience and a robust, compliant backend.

Share:

Start with the tools

Explore migration tools

See options, compare methods, and pick the path that fits your store.

Explore migration tools