Saving data to cart / order meta fields

I would like to set certain properties on cart objects using the storefront API and metafields. ‘Carts’ themselves don’t have metafields, but orders do. I’ve created two order-level metafields, and I was seemingly able to save them on the cart in the following way (documented here: cartMetafieldsSet - Storefront API):

mutation cartMetafieldsSet($metafields: [CartMetafieldsSetInput!]!) {
  cartMetafieldsSet(metafields: $metafields) {
    metafields {
      key
      value
      namespace
    }
    userErrors {
      field
      message
    }
  }
}
shopifyStorefrontClient.cartMetafieldsSet({
  metafields: [
    {
      key: CartMetaFieldKeys.posthog_distinct_id,
      value: "123",
      ownerId: cartId,
      type: 'string',
    },
  ],
});

This appears to work – I am able to see the metafields when I fetch carts like so:

fragment CartFields on Cart {
  id
  metafields(
    identifiers: [
      { namespace: "app--122", key: "posthog_distinct_id" }
    ]
  ) {
    key
    value
  }
}

However, when the cart is eventually converted to an order, I don’t see the metafields that appear in the UI being populated – they’re all blank.

1.) Am I going about this the right way? If not, is there a better / different approach? Are metafields set on the cart ‘moved over’ when the cart is turned into an order?

2.) When fetching the cart metafields, rather than using ‘custom’ as the namespace (which is what it shows up as the UI), I need to use the app ID. This is somewhat unusual – using ‘custom’ normally works, but not here. Why is this the case?

Thank you in advance.

1 Like

Hey @sebastienpowell :waving_hand: - this is definitely a tricky thing to implement. Carts themselves are technically temporary, so their metafields aren’t intended to persist beyond the usefulness/life of the cart if that makes sense (they are really useful for cart transform Functions for example).

If you wanted data that is entered on a cart object to persist through an order, the current way to do this is through cart attributes, which are key/value pairs that persist when the order is created.

You could theoretically convert these into metafields after the order is created if you did need to use them within a metafield object, but they would still remain as attributes on the order.

Hope this helps/makes sense - let me know if I can clarify anything on my end here :slight_smile:

@Alan_G Thanks for getting back to me, that’s helpful.

I’m curiousl; what is the logic behind cart attributes being persisted through to the order (which seems to be the case), but metafields not? This is all the more strange, given that the metafields I’m working with are order-level metafields. Somehow, I’m able to set them on the cart, but when the cart is converted into an order, the values aren’t carried over – whereas cart attributes are.

Just to explain what I’m trying to achieve: we’re currently using cart attributes, however it can happen that one or more services attempt to update these simultaneously. This creates a kind of race condition: because they overwrite rather than merge, every service updating them needs to have the very latest version of the cart and its attributes when sending its payload – or it risks overwriting another change that’s just been made.

If metafields aren’t an option here, we’ll look for another workaround, but I’d be interested to understand the reasoning behind this behaviour, and whether there’s any chance it might change in the near future.

Thanks again!

Hey @sebastienpowell :waving_hand: thanks for following up/clarifying!

Cart metafields stay on the (temporary) cart, so they vanish once the cart becomes an order. Cart attributes, however, are copied verbatim to the order’s note_attributes, which is why they persist there.

Here’s the best path right now from what I can tell:

  • Keep writing the values to cart attributes so they ride through checkout.

  • Subscribe to an orders/create webhook. When orders/create fires, use the Admin API to copy those values into your order-level metafields.

If attribute race conditions are an issue, you could instead stash the data in a Cart note (also survives as an Order note) and migrate it to metafields from there.

If you’d like cart metafields to auto-persist on order creation, I’m happy to raise a feature request—just send over a short merchant-facing use-case and I’ll pass it along to the product team, just let me know.

Hope that helps—let me know if I can help out further!

@Alan_G thanks for getting back to me, and for the helpful suggestions.

Using cart notes is an interesting idea, though I worry it might get a little messy. As for the feature request: that would be great if it’s possible. Totally understand if it doesn’t get prioritised or actioned, but right now I’m struggling to understand the logic of why cart attributes persist into orders while cart metafields do not. It would feel more consistent if they did, and in our case, it would solve the problem cleanly.

Regarding the use case – I’m not sure if you need this in a particular format, but here’s our situation:

  1. We’re using cart attributes to persist PostHog IDs, allowing us to share them between our headless frontend (built in Next.js) and Shopify’s hosted checkout. There’s more context on why we’re doing this here: Cookies in pixel sandbox
  2. We use various third-party extensions, some of which write to cart attributes. It seems like race conditions may be occurring here – in theory we could try to refactor things, but I’m not confident we’ll ever be able to guarantee the order in which those attributes are written.
  3. Metafields would help avoid these conflicts, since they can be scoped to specific fields – meaning one service won’t accidentally overwrite another’s data unless it’s explicitly updating the same field (which would be highly unlikely, particularly in the case of third-party services). With attributes, the whole object gets overwritten, which is much riskier.

Hope that all makes sense. Let me know if you need any clarification. I’ll give the cart note approach a go, otherwise we’ll stick to using attributes – the occasional loss of analytics data isn’t a huge issue, but longer term it would be great to have a more robust solution.

Hey @sebastienpowell - thanks for sharing your use case here, I’ll pass this along in the feature request for sure, definitely agree it would be a lot easier to create a way to track analytics with persistent objects like that.

In terms of why cart metafields don’t persist into newly created orders, my understanding is that they are generally meant to be ephemeral (just like cart objects) and are meant for use with checkout functions/extensibility for the most part, but I’ll double check internally here to confirm that with you and loop back if I am able to share a more concrete answer.

Speak with you again here soon :slight_smile:

1 Like

Great, thanks @Alan_G! I’ll wait to hear back from you.

Hey @sebastienpowell :waving_hand: I was able to speak with some folks on our cart/checkout product team and we confirmed that cart metafields don’t currently pass over to completed orders at the moment.

We are looking at enabling this in the future, but I don’t have a guaranteed timeline on that though.

I did do a bit of digging as well, and it looks like this would be possible using a post-purchase Checkout UI Extension:

Checkout UI extensions are currently for Plus merchants only, but I did want to share this as a possible workaround. Hope this helps, let me know if I can help out any further as always :slight_smile: