I love the email_marketing_consent
and sms_marketing_consent
fields on the customer
resource, but they have an inconvenient behavior.
While it seems entirely natural to only update consent_updated_at
if consent changes, this significantly lowers the utility of this field. Now I, as the client, need to ensure in order processing of REST requests to PUT
on the customer resource. Observe what happens if I make requests to shopify out of order.
Initial state:
{
"customer": {
"email_marketing_consent": {
"state": "unsubscribed",
"opt_in_level": "single_opt_in",
"consent_updated_at": "2024-10-31T00:00:00-04:00"
}
}
}
00:00:01 - subscribe enqueued with consent_updated_at 12:00:01
00:00:02 - unsubscribe enqueued with consent_updated_at 12:00:02
00:00:03 - subscribe dequeued with consent_updated_at 12:00:01, rate-limited and re-enqueued
00:00:04 - unsubscribe dequeued with consent_updated_at 12:00:02, processed and ignored because consent did not change
00:00:05 - subscribe dequeued with consent_updated_at 12:00:01 processed and applied
Intended state:
{
"customer": {
"email_marketing_consent": {
"state": "unsubscribed",
"opt_in_level": "single_opt_in",
"consent_updated_at": "2024-10-31T00:00:02-04:00"
}
}
}
Actual state:
{
"customer": {
"email_marketing_consent": {
"state": "subscribed",
"opt_in_level": "single_opt_in",
"consent_updated_at": "2024-10-31T00:00:01-04:00"
}
}
}
Because shopify enforces rate limiting, serially processing requests is non-trivial to implement. This requires a duplication of the behavior shopify is already doing on the api, but with this minor logic tweak.
Why does this come up in practice? Consider a customer that rapidly subscribes and unsubscribes from email, because we implemented a toggle that makes unsubscription painless. We have to isolate this traffic because shopify may rate-limit a request. A rate-limit response might re-enqueue a message causing it to be consumed out of order. This will lead to drift between the Shopify consent and consent stored on our system.
Am I interpreting the shopify api behavior correctly? If so, I humbly request shopify alters its current behavior.
I have replicated this behavior with both 2024-01
and 2025-01
REST APIs. I assume the logic is the same for GraphQL, but I have not yet transitioned to this API.