Shopify deprecated the authorizationCode field on OrderTransaction in the Admin GraphQL API, starting with the 2026-01 version, and recommends you switch to paymentId. The migration guidance is a one line find and replace: swap authorizationCode for paymentId in your queries. It compiles, it runs, the API returns a 200, and your payment reconciliation is now quietly wrong.
This is a sharper trap than a field simply going away, because the two fields are not the same kind of value. Here is the sentence from Shopify's own deprecation notice that matters most: paymentId returns a Shopify-specific identifier, which differs from the provider-specific value in authorizationCode. A plain rename assumes the new field is the old field under a new name. It is not. You changed the name, and you also silently changed what the value means, and nothing in your code or the API response will tell you.
What actually changed
OrderTransaction.authorizationCode held the authorization code from the underlying payment provider, the processor-specific value tied to a given gateway. Shopify is steering everyone toward paymentId, which gives a consistent, Shopify-specific identifier for reconciliation that is stable across every payment provider. On its own terms this is a reasonable change. A standardized Shopify ID is genuinely more stable than a grab bag of provider-specific codes.
The danger is in how the migration is framed. Shopify's recommended migration is to replace instances of authorizationCode with paymentId, and the example in the notice is exactly that: the same query with one field name swapped. For a lot of code that is fine. For any code that does something with the value, specifically code that reconciles Shopify transactions against a payment processor's own records, it is not fine, because the processor knows the authorization code and has never heard of Shopify's internal payment ID.
Why this is a silent failure and not a loud one
Walk through what happens to a reconciliation job after a naive swap.
Before the change, your job reads authorizationCode off each transaction and matches it against the authorization codes in your payment processor's settlement data. The two line up because they are the same value from the same source.
After the swap, your job reads paymentId, a Shopify-specific identifier, and matches it against the processor's authorization codes. They never line up, because they are two unrelated identifier spaces. Every match fails.
Now, critically, consider what that failure looks like. The GraphQL query is valid. The field exists and returns a value. The API returns a 200. Your code runs start to finish without an exception. The only symptom is that reconciliation matches start coming back empty, which, depending on how your job is written, surfaces as transactions flagged unreconciled, or as a reconciliation report that quietly shows everything as unmatched, or as a downstream process that silently does nothing because it found no matches. None of that pages anyone. None of it shows up as an error rate. It shows up days later as a finance person asking why nothing has reconciled this week.
That is the whole shape of the problem. The field is present, the type is right, the call succeeds, and the value is semantically wrong for what your code does with it.
The migration guidance leads you straight into it
The thing that makes this one worth calling out is that doing exactly what the deprecation notice shows you is what causes the break. The notice does include a warning that paymentId differs from authorizationCode and that you should ensure your systems store and reconcile using the new ID format. That warning is correct and easy to miss, because it sits next to a migration example that is a pure field rename. A developer under deadline, working through a list of deprecations, sees "replace authorizationCode with paymentId," does precisely that, watches the tests pass, and moves on. The warning scrolls past. The swap ships. Reconciliation breaks on the next settlement cycle.
This is the gap between "I migrated the field" and "I migrated the meaning." A find and replace handles the former and silently skips the latter.
This is contract drift, with a twist
Most of the time, contract drift is a field that vanishes or a value that goes null. This is a less obvious variant: the field you read is present and populated, but what it represents has changed underneath you. Your code's assumption about the meaning of the value, not just its presence or type, is what drifted out of sync with reality.
It is still API contract drift in the way that matters: the live API no longer behaves the way your code was written to assume, while still returning perfectly valid responses. A 200 with a well formed paymentId tells you nothing about whether that ID means what your reconciliation logic needs it to mean. And no contract test you wrote before the change will catch it, because a test built on a fixture where paymentId matches your expected value encodes the same wrong assumption the production code has. The way you catch a semantic change like this is by checking real behavior against reality, which here means verifying that the identifiers you reconcile against actually match on live data, not that the field is merely present. That is the domain of runtime monitoring rather than build time testing.
What to actually do
If you read OrderTransaction.authorizationCode anywhere, do not treat the migration as a rename.
Find every place you read authorizationCode and, for each one, ask what the value is used for, not just whether it is present. Where it is used purely as an opaque reference that you store and display, swapping to paymentId is likely fine. Where it is matched against a payment processor's own records, swapping the field without changing the matching logic will break reconciliation, and you need to either reconcile on a value both systems share or map between the Shopify ID and the provider's code deliberately. Before you cut over, run both fields on live transactions and confirm that the new identifier actually matches what you reconcile against, rather than assuming the rename preserved the relationship. And keep checking after cutover, because a reconciliation break is exactly the kind of failure that stays invisible until someone goes looking for money that did not match.
The lesson outlives the field
This is one field on one object in one commerce platform's API. If you do not touch payment reconciliation, this specific change is not your problem. But the pattern is worth internalizing, because it is subtler than the usual removal. A field did not disappear. A field was replaced by another field that looks like a drop in substitute, is documented as a drop in substitute, and holds a different kind of value. The call still succeeds. The field is still there. The data is just quietly wrong for what you use it for, and the official migration path walks you into it. The only reliable way to know your integration still does what you think is to check real responses against real expectations on live traffic, not to trust that a rename preserved the meaning.
Frequently asked questions
What changed with Shopify's OrderTransaction.authorizationCode field?
Starting with the 2026-01 Admin GraphQL API version, Shopify deprecated the authorizationCode field on the OrderTransaction object and recommends using paymentId instead. authorizationCode held the payment provider's own authorization code, while paymentId returns a Shopify-specific identifier that is consistent across all payment providers. The two fields hold different kinds of values, so they are not interchangeable for every use.
Why is swapping authorizationCode for paymentId risky?
Because the two fields do not hold the same value. authorizationCode is the provider-specific code, and paymentId is a Shopify-specific identifier. Code that reconciles Shopify transactions against a payment processor's records relies on a value both systems share. After a naive swap, the reconciliation job reads a Shopify ID and tries to match it against the processor's authorization codes, which never match. The query still returns a 200 and a valid value, so nothing errors, and the break surfaces downstream as unreconciled transactions.
Does this throw an error?
No. The paymentId field exists and returns a valid value, so the GraphQL query succeeds with a 200 and your code runs without an exception. The failure is semantic: the value is the wrong kind of identifier for matching against external payment records. That is why it tends to surface as empty reconciliation matches or unmatched transaction reports days later, rather than as an immediate API error.
How do I migrate safely?
Treat it as a meaning change, not a rename. Identify every place you read authorizationCode and determine what the value is used for. Where it is matched against a payment processor's records, do not simply swap the field; reconcile on an identifier both systems share or map explicitly between the Shopify payment ID and the provider's code. Run both fields on live transactions before cutover to confirm the new identifier matches what you reconcile against, and monitor after cutover since a reconciliation break stays invisible until someone audits it
Top comments (0)