Shopify shops move to expiring access tokens automatically
Gadget now moves your installed Shopify shops from non-expiring offline access tokens to expiring ones in the background. Before, a shop only switched when a merchant opened your app, so shops whose merchants rarely open it stayed on the old token. Shopify requires public apps to use expiring tokens by January 1, 2027. After that date, requests made with a non-expiring token get authentication errors.
The migration runs for Shopify connections on API version 2026-04 or later that were set up through the Partner Dashboard or Dev Dashboard. Custom apps created in a store's admin are not affected. Gadget exchanges each shop's token with Shopify and keeps the new one refreshed from then on. Merchants don't see anything and don't need to reinstall. Shops that already have an expiring token are skipped, and upgrading a connection to 2026-04 or later starts its migration automatically.
To start the migration now instead of waiting, go to Settings -> Shopify in the editor and click Migrate shops to expiring tokens below your connection's API version. The button only shows while at least one installed shop still has a non-expiring token. Shopify revokes a shop's old token once it is exchanged, so this can't be undone.
What you may need to do:
- Connections on an API version before 2026-04 are not migrated. Upgrade the connection before January 1, 2027.
- Code that reads
accessTokenfrom theshopifyShopmodel and passes it to its own Shopify client can start failing once that shop migrates. The old token is revoked, and the new one expires after about 60 minutes and changes every time Gadget refreshes it. Useconnections.shopify.getAccessToken()instead. Clients fromconnections.shopify.current,forShopId(), andforShopDomain()already handle this, including clients created just before a shop migrated: they load the new token and retry the request once. - Shops whose current token Shopify rejects can't be migrated in the background. The merchant needs to open your app in their Shopify admin to get new tokens.
Faster and more reliable cancellation for large queues
Shopify TOML comparisons support custom production domains
Removed stale Shopify credentials from deleted TOML files
Shopify sign-ins now sync with settings checks
Development environments recover from hung app starts
Shopify configuration checks handle inaccessible organizations
Get Shopify access tokens for custom clients
connections.shopify.getAccessToken() to get a valid Admin API access token and its expiry. Specify a shopId or shopDomain, or use the current shop.Shopify B2B purchasing company fields populated during order syncs
shopifyOrder model doesn’t include the purchasingEntity field. The app must have read_customers access.
To populate these relationships on existing orders, run an order sync that covers those records. Draft orders aren’t included in this change.Reliable retries for Shopify sync timeouts
writecashtracking and read_rollouts access scopes.Corrected Shopify shop-specific registration field name
shopSpecificRegistration instead of shopSpecificWebhooks. The documentation now matches the field name in the shopifyShop model and more accurately describes metafield-based webhook registrations created for each shop through the Shopify GraphQL API.Cancel all pending jobs in a queue
Support for XCG currency in Shopify models
XCG to the predefined currency options for Shopify models, keeping currency fields aligned with Shopify’s GraphQL schema across all supported API versions.System-admin access checks no longer deny models or actions
GGTPERMISSIONDENIED errors. Gadget also no longer writes unnecessary per-model or per-action permission rules for the system-admin role.Load testing guide
Fixed Shopify orders stuck at outdated payment statuses
authorized and paid order webhooks with the same timestamp, Gadget now checks Shopify's current order state before applying the update. This ensures orders reflect the latest payment status regardless of webhook delivery order.Shopify API upgrades now update linked TOML files
webhooks.api_version in every linked TOML file while preserving comments and custom content. The upgrade also refreshes canonical webhook topics and starts the Shopify CLI deployment flow, keeping webhook configuration aligned with the upgraded connection.Support for new Shopify access scopes
checkoutsettings, deliverypromises, marketplacereturns, mobileplatformapplications, and serverpixels, writemarketplaceorders, readshopifypayments, readcapital, readshopifypaymentsaccountssensitive, and readshopifypaymentslegal_entitiesMore reliable nightly Shopify reconciliation
Prevent unintended Shopify webhook deletion during deploys
Fixed code editor loading errors after switching environments
Correct Shopify Collect syncs for flexible collections
shopifyCollect records only for custom collections. Flexible and smart collections are excluded, while shopifyCollectionProduct continues to sync resolved membership for every collection type. Existing shopifyCollect records previously created for flexible collections aren't removed by this change.Shopify installation rejection details in app logs
Fixed false Shopify application URL mismatch errors
shopify.app.*.toml settings when an environment starts, clearing stale errors and allowing automated ggt deploy commands to continue without --allow-problems.Fixed incorrect file loading errors in the code editor
Fix the editor freezing on apps with many files
ggt-v3-1-1-available
ggt update notifications now link to the correct GitHub release for the available version.Shopify reconciliation syncs skip unnecessary sub-resource fetches
ggt-v3-1-0-available
ggt field, including relationship fields, constraints, and Shopify metafield linksEnable Shopify models with ggt model add --type shopifyConfigure connections with ggt shopify connect, review connection details with ggt shopify status, and start filtered data syncs with ggt shopify syncKeep ggt dev and ggt logs running by automatically retrying WebSocket subscriptions after transient errorsPrevent ggt dev from crashing when a file is deleted while it’s being readFix harness event Redis memory leak
Fix assistant Redis OOM issue
OOM command not allowed error. Now, assistant result streams automatically expire to prevent the underlying store from being exhausted. If the assistant faces transient infrastructure pressure, it will show a clear "temporarily unavailable, please try again" message instead of a raw internal error.Fix for Shopify token backoff suppression
Fix install-or-render.data 404s in Shopify apps
.data requests, returning a soft redirect to the app home, ensuring seamless loading without user intervention.Drag-and-drop role reordering on permissions page
We've added drag-and-drop functionality for reordering roles directly on the access control / permissions page. This update allows you to arrange role columns by dragging headers, similar to column reordering in the data viewer. Previously, role order could only be modified by manually editing accessControl/permissions.gadget.ts through ggt.
Progressive charging for long-running DB queries
Database rate limiting now charges progressively higher rates for long-running queries. Short queries are unaffected, but queries that hold database connections for longer now consume the rate-limit budget faster, protecting your app from a single misbehaving operation exhausting database resources and slowing down everything else.
New logging syntax and commands due to ClickHouse logs migration
The syntax used to query environment logs has changed due to our under the hood migration of our logging infrastructure from Loki to ClickHouse. Lucene query syntax is now used for all logs queries. See the logs docs for more info and examples.
The logger API used to write to logs is unchanged.
Surge threshold visualization added to ops dashboard
The Request rate limit usage chart in the ops dashboard now includes a visual indicator for the surge threshold. A yellow dashed line and background indicate when workers are reaching or exceeding their surge limit. This enhancement helps distinguish the surge threshold from the hard max limit, providing a clearer view of resource usage.
Support for Shopify API version 2026-04
We've added support for Shopify API version 2026-04, which includes several updates:
- Model spec changes to accommodate new field additions and removals.
- Webhook updates including new topics like
progressReportedandmanuallyReportedProgressStopped. - Field deletions like
permitsSkuSharingin theFulfillmentServicemodel. - Structural changes such as the new discriminated union in
SubscriptionBillingAttemptState.
For a detailed list of changes, please refer to the full notes provided in the documentation.
Support for Shopify expiring offline access tokens
Gadget now automatically requests expiring offline access tokens for Shopify apps on API version 2026-04 and above. This is required by Shopify for all public apps. Access tokens expire after 60 minutes and are refreshed using stored refresh tokens.
Existing shops will be upgraded to expiring tokens the next time a merchant opens the app in the Shopify admin. No code changes are required in your apps.
New Shopify apps use Polaris web components
New Shopify apps now use Polaris web components for admin frontends.
web/components/AppBridgeNavigate sets up a listener for "shopify:navigate" events dispatched by s-link web components and routes them using the client-side router.
Gadget framework version 1.7.0: Shopify TOML's as the source of truth
We have released Gadget framework version 1.7.0, which shifts all Shopify app configuration into your shopify.app.toml files, including webhook subscriptions, filters, and access scopes.
Previously, Gadget set up Shopify access scopes and webhook configuration via API. For apps made with the Shopify CLI, this configuration is TOML-based. Framework version 1.7.0 unifies Gadget's connection configuration with Shopify's TOML spec, so your app configuration can be controlled with the TOML file like any other Shopify app.
You can still use the Gadget editor to set up connections, select access scopes and data models, add Shopify webhook triggers to actions, or add Shopify webhook filters. This configuration will also be captured in the TOML.
(Note that metafield webhook payloads are still registered by Gadget via API when they added to a Shopify model. We do not include them in webhook payloads if metafields are added directly to the TOML files.)
Gadget automatically migrates your configuration when you upgrade to v1.7.0. See the migration guide for more details.
Add copy-as-JSON buttons to data viewer
We've added new capabilities to the data viewer that allow users to easily copy data as JSON:
- Per-row copy button: Click the copy icon next to each row's delete button to copy the row as a JSON object.
- Multi-select copy button: Select multiple rows via checkboxes and use the "Copy records" button to copy all selected rows as a JSON array.
Both features serialize only visible fields using their API identifiers and replace password field values with "<omitted-password-value>" for security.
Fix applied to renameModelField mutation logic
The renameModelField mutation now correctly enforces field rename permissions, preventing unauthorized renaming of system or connection-owned fields marked as non-renamable. Users attempting to rename such fields will encounter an InvalidUserInputError. This ensures integrity and security in field management.
Release of ggt v3.0.0
A new version of ggt is now available with several major updates:
ggtnow requires Node.js 22 or later. Ensure you're using a supported Node version to continue receiving updates.- The
ggt logscommand now prints recent logs and exits by default. Use--followfor streaming. New filtering options include--startand--log-level. - New resource-first
ggt action addandggt modelsubcommands enhance development workflows. - Overhauled CLI
helpoutput for better usability with structured formatting and example guides. - Introduced
ggt completioncommand for generating shell completion scripts for various shells.
Auto-expand complex field selections in GraphQL operations
We've introduced automatic expansion of complex field selections in GraphQL operations. Now, fields like RichText, File, and RoleAssignments can be selected with true instead of manually specifying all sub-fields. This update simplifies the selection process while continuing to respect explicit partial selections.
Fix colors for threshold limits on request-rate chart
Previously, the request-rate chart failed to display the visual indicators for usage exceeding defined thresholds. The Y-axis was incorrectly capped, preventing over-threshold visuals. This fix updates the computeYScale logic, ensuring the chart correctly displays data points above the hard limit, bringing back the over-threshold colors.
Fix redirect loop with no Shopify installs
We've fixed an issue where clicking "Install app" on the Shopify Installs page would cause a redirect back to the Connections page if all shop installs were previously removed. The modal now correctly stays open, allowing for a new install.
Fix ChatGPT OAuth invalid_scope error
ChatGPT started requesting openid and offline_access scopes during OAuth authorization. Gadget's OIDC provider was only assigning the user scope to dynamically registered clients, like those created during ChatGPT plugin setup, even though the provider advertised all three scopes as supported.
This caused node-oidc-provider to reject the openid scope as not allowed for the client, resulting in an invalid_scope error. The fix updates dynamically registered clients to inherit all scopes the provider supports so ChatGPT OAuth flows complete successfully.
ggt v2.3.0 release
ggt v2.3.0 has been released!
ggt envis a new command for managing development environments from the terminal. You can now list, create, rename, activate, and delete environments easily. This makes it easier to work with git worktrees and worktree managers like OpenAI's Codex.ggt agent-plugin updatewill update existing agent plugins, ensuring you always have the latest features and fixes.
Empty directories no longer recreated
Empty parent directories are no longer recreated when deleted in the web editor.
Chart tab persistence in ops dashboard
Chart tab selections in the operations dashboard are now serialized to the URL, allowing for shareable links that preserve tab selections, and persisted preferences across page reloads and navigation.
Option to copy non-secret environment variables on dev environments creation
When creating new development environments, you can now copy non-secret environment variable values from the source environment.

Show/hide options in ops dashboard chart tables
A new right-click context menu with options for controlling line visibility has been added to the chart tables on the operations dashboard.

Arrange columns on a model's data page
You can now Arrange columns on your model's data page. This allows you to manually sort column order, and toggle column visibility.

ggt v2.2.0 release
The new version 2.2.0 of ggt introduces new commands for managing environment variables, running JS snippets using your API client, and fetching issues from the problems drawer.
- (new command)
ggt var: manage environment variables - (new command)
ggt eval: Run snippets against your API client - (new command)
ggt problems: Query for items in the problem drawer - (improvement)
ggt devandggt statusnow detect ifggt devis already running - (bug fix)
.ignorefile directory-only pattern fix - (bug fix) automatic retry logic for WebSocket connection failures
- (bug fix) Improved command execution outside of synced folders
Fix csrfToken parsing in react-router action functions
We would not always parse the csrfToken properly when it is used to submit forms in react-router action functions, resulting in and unauthenticated session context and GGT_PERMISSION_DENIED errors. This has been fixed, and the context in action functions will now have the correct session record and any roles/permissions granted to that session.
See the docs for an example of submitting forms with react-router.
Consistent handling of model pluralization in Gelly views
We've fixed a bug in computed views where omitting the space between a pluralized model selection and the opening { would unintentionally remove the last character from the pluralized model identifier in the result. This ensures consistent pluralization in API client results.
Import environment variables from another environment
You can now sync environment variable keys and non-secret values between development and production environments. An Import missing variables button is now on the environment variables page in the web editor. Click it to sync missing variables from another environment.

Fix framework upgrade dependency issues
This update resolves issues where framework upgrades could cause duplicate @gadgetinc/react installations and "not wrapped in a Gadget Provider" errors. This often happened when using @gadgetinc/react-shopify-app-bridge or @gadgetinc/react-bigcommerce.
The fix ensures that these packages are updated concurrently with @gadgetinc/react during upgrades, preventing peer dependency mismatches.
Support for nullable non-required enums
enum fields marked as required: false will now include null as a possible value. This change allows for better flexibility and alignment with expected behaviors when handling enums in the API client.
Fix role selectability when duplicating unselectable roles
When duplicating an unselectable role like shopify-app-user and renaming it, the new role can now be selected and assigned to an API key, resolving previous limitations.
New findAll, iterateAll APIs
findAll and iterateAll APIs have been added to your auto-generated API. When you do need to fetch all records for a model, you can use these APIs to simplify bulk reading and pagination.
iterateAll is recommended, as it is less time and memory intensive, and allows you to process records one at a time using for await:
for await (const record of api.task.iterateAll({
select: { id: true },
filter: {
// add filter conditions, if possible
},
})) {
// process each record one at a time
}findAll returns a flat array of records:
const allRecords = await api.task.findAll({
select: { id: true },
filter: {
// add filter conditions, if possible
},
});Framework version 1.6.0 released: configurable non-webhook fields for Shopify
Developers can upgrade their Gadget projects to framework version 1.6.0. This framework version includes support for configurable fetching on non-webhook fields and related models for Shopify apps.
Instead of automatically fetching model data not included in Shopify webhooks, non-webhook fields and models can be configured to only be fetched on sync or reconciliation:

schema.gadget.ts files for Shopify models also contain properties to track this configuration through source control:
export const schema: GadgetModel = {
type: "gadget/model-schema/v1",
storageKey: "DataModel-Shopify-Product",
fields: {
// fetch the seo field on product webhooks
seo: {
fetchData: "onWebhook",
},
},
};ggt v2.0.2 (patch release): Fix "e.split is not a function" error in environment logs
The latest patch addresses an issue where the error "e.split is not a function" would occur in environment logs. This fix ensures that non-string message values are converted to strings before processing, handling cases where logs from the server might have missing or non-string messages.
ggt commands unpause development environments
ggt commands that connect to or update environments, including ggt push, ggt pull, ggt add, and ggt deploy, will now automatically unpause development environments when run.
Prevent asset build failures caused by build process interruptions
yarn build, could fail with exit code 1 during deployments, even when the underlying build completed successfully. This was caused by the build process being interrupted during routine infrastructure scaling. Builds now properly complete before the system recycles resources.Copy button added to input panel in action history
We've added a copy-to-clipboard button in the Input section of the background action details page to make it easy to copy the action payload.
Shopify CLI connection flow: account confirmation and switch account
We've added the ability to switch Shopify accounts during the connection setup flow. Users can now see which Shopify account they're connected to and switch to a different account if needed.
Claude skill for Gadget best practices now available
A Claude Skill that captures Gadget best practices is now available.
Download the skill with
npx skills add gadget-inc/skills --skill gadget-best-practicesand start using it with /gadget-best-practices. It is compatible with all tools that support skills, including Claude Code and Cursor.
Sort records by related model values
Gadget now supports sorting the results of findMany, findFirst, and maybeFindFirst queries by values in related models, across belongsTo relationships.
For example, sorting posts by author name, where each post belongsTo author:
// sort the post records
await api.post.findMany({
sort: {
// by the name of the author
author: {
name: "Ascending"
}
}
});Support vector distance calculations in JS client
We've added support for arguments in scalar fields, enabling the selection of vector distance computations, cosine similarity or L2 distance, in read queries.
For example:
await api.document.findFirst({
select: {
id: true,
body: true,
embeddingCosineSimilarityTo: {
[api.$args]: { vector: [1, 2, 3] },
},
},
});Serve docs as markdown
The Gadget docs can now easily be copied as markdown for working with local agent tools like Cursor or Claude Code. Copy the markdown manually with the in-docs button or add .md to a docs page route, for example:
https://docs.gadget.dev/guides/getting-started/what-is-gadget.mdggt v2.0.1 patch release
The ggt 2.0.1 patch introduces automatic retries for HTTP requests encountering transient SSL/TLS errors, such as ERR_SSL_SSL/TLS_ALERT_BAD_RECORD_MAC and EPROTO. This improves resilience against network issues during SSL/TLS handshakes.
Support for Shopify API version 2026-01
We've upgraded to Shopify API version 2026-01 with the following changes:
- The
returnReasonfield on theReturnLineItemresource is deprecated and replaced with a newReturnReasonDefinitionobject connection. - Introduced a new model
ShopifyReturnReasonDefinition. - The variant field on
InventoryItemis deprecated, moving from ahasOneto ahasManyrelationship.
Ops dashboard now available in the editor
A new operations dashboard is now available in the Gadget editor.
The ops dashboard provides visualizations that allow you to monitor the status, traffic, and usage for your apps and environments. With the ops dashboard, you can:
- see a breakdown of HTTP status codes and errors, and monitor your overall app traffic
- monitor CPU and memory usage for your serverless workers
- check up on database storage, reads, and writes
- determine how often you make use of the
searchAPI (powered by Elasticsearch) - review your background action usage and get per-queue information
See the docs for more information on individual visualizations.
Data editor: Cmd+S save shortcut
Use adaptive rate limiter for manual requests to Shopify
You can hook into the rate limiter Gadget uses for Shopify syncs when making manual requests to Shopify’s API using background actions. Enqueue an action that makes a request to Shopify with the current shop context when you want to transform the response from Shopify:
export const onSuccess: ActionOnSuccess = async ({ api, connections }) => {
// the processCustomOrders action calls the Shopify API
await api.enqueue(api.processCustomOrders, {
// pass the shopifyShop ID to the background action
shopifyShop: connections.shopify.currentShopId,
// ...
});
};Or enqueue the shopify-api-node client call directly for durable writes:
export const onSuccess: ActionOnSuccess = async ({ api, connections }) => {
const shopify = connections.shopify.current;
// enqueue the productCreate mutation
await api.enqueue(shopify.graphql, {
query: `mutation ($input: ProductCreateInput!) {
productCreate(product: $input) {
product {
id
title
}
userErrors {
field
message
}
}
}`,
variables: {
input: {
title: "Cool socks",
},
},
});
};Improved JSON file error handling in code editor
JSON files that can't be parsed now display error messages in the problems drawer. The error details are converted into file positions, similar to the system used in CodeMirror's JSON extension. This allows error squiggles to be displayed in the code editor, enhancing the debugging process.
Updated list of outbound IP addresses
Starting today, Gadget may use 34.171.201.14 as an outbound IP. This IP was announced previously. Starting March 1st 2026, Gadget may also use 136.113.237.21 as an outbound IP. You must update your allowlists with this new IP before March 1st 2026.
Vite 7 upgrade and sandbox restart fix
We've upgraded new apps to use Vite 7. This update addresses a bug related to development app loads during sandbox restarts. The restructuring ensures the Vite client always loads properly, avoiding failed connections and hydration issues with the app.
Multi-session terminal support
We're excited to introduce multi-session support in the Gadget editor's terminal. Users can now create and manage multiple concurrent terminal sessions. A new sidebar UI displays all active sessions, showcasing the current running command or "new shell" if idle.
Sessions persist across drawer events and are automatically managed when environments switch.The assistant now has session awareness, reading from the selected terminal session and routing commands intelligently to avoid interruptions.
Fix orphaned webhooks on filter change
We've resolved an issue where some webhook registrations were orphaned when an action's shopifyFilter changed, leading to duplicate webhooks with different filters.
GGT version release: 2.0.0 (requires Node 20)
With the release of ggt v2.0.0, several major changes have been implemented:
- Node.js 20 or later is now required as Node.js 18 has reached end-of-life and no longer receives updates. Ensure your environment is using a supported version for security purposes.
- The new ggt debugger command has been added. It enables debugging via the Chrome DevTools Protocol on localhost:9229 and includes support for VS Code configuration.
- The .gadget/backup/ directory has been removed. Files will now be permanently deleted instead of being moved, as modern source control covers file recovery more effectively.
- Deprecated the ggt sync alias and removed support for .gadget/sync.json files from ggt v0.4.x.
Update to Tailwind CSS v4
Web applications now start with Tailwind CSS version 4, incorporating updated shadcn components. Documentation is included for upgrading existing applications from Tailwind v3 to v4.
To learn more, read the docs.
Select fields for API search queries
You can now specify which fields to search within API calls using the new searchFields parameter. This feature allows you to define the fields that are included in the search and assign relative weights to each field for better ranking control. Select specific fields to search:
const results = await api.post.findMany({
search: "gadget",
searchFields: {
title: true,
content: true,
},
});Boost field importance with weights:
const results = await api.post.findMany({
search: "gadget",
searchFields: {
title: { weight: 2.0 },
content: { weight: 1.0 },
tags: { weight: 0.5 },
},
});This enhancement is available on findMany, findFirst, maybeFindMany, and maybeFindFirst APIs for models with search indexing enabled, requiring framework version 1.5.0+.
Gelly function dateFormat added
dateFormat function has been introduced for Gelly, allowing you to format dates similarly to Postgres's to_char function. This provides a straightforward way to convert dates to strings like 2025-08 with leading zeros for months.Shopify default search and filter field configurations added
Fix for Sentry connection configuration issue
Release of framework version 1.5 with selective indexing
Preact hook support for Shopify extensions (API v2025-10+)
You can now use Gadget’s Preact hooks to read and write data in your Shopify extensions. The @gadgetinc/preact API mirrors
@gadgetinc/react, making migrations easier. useActionForm and component-related hooks aren’t available.
@gadgetinc/shopify-extensions now includes a Preact <Provider />: import { Provider } from "@gadgetinc/shopify-extensions/preact"
Manual index selection on fields
Choose which fields are indexed for sorting, filtering, and search. Sort/filter indexes use Postgres; search indexes use Elasticsearch. This reduces storage costs and gives finer control over query performance and relevance.
The Shopify model schema now shows which fields are indexed, improving visibility into data optimization.
Trigger type schema change
The shopify_sync trigger type now distinguishes between syncs and webhook reconciliations. Webhook-based actions use the new type shopify_webhook_reconciliation. This helps separate sync logic from reconciliation logic and is also reflected in the billing dashboard.
Internal refactor for Preact support
@gadgetinc/api-client-core
has been replaced with @gadgetinc/core. Shared client logic now lives in the generated client, simplifying architecture, improving performance, and reducing dependencies.
Computed fields in public API
Computed fields are no longer returned by default in public API queries — they must now be explicitly selected.
Background action management improvements
- Reintroduced the Cancel all, Delete all, and Retry all buttons for improved user control.
- The index page now uses URL query parameters to define state, making URLs shareable for consistent navigation experience.
Update Shopify CLI to version 3.86.1
App URL mismatch validation for managed installs
ggt v1.7.4 release
ggt push to improve reliability.Context limit warning in assistant conversations
New spend limit feature
Fix sync issues with Shopify models not including shopifyUpdatedAt field
shopifyUpdatedAt field were not correctly getting records updated upon nightly reconciliation sync.Standardize ruleset and sortOrder on ShopifyCollection
rules and sortOrder fields on ShopifyCollection have been standardized to match their incoming REST webhook shapes, ensuring consistency in data.Added missing enums for Shopify API version 25-10
UNLISTED to the status field.
ShopifyFulfillment*: Added CARRIERPICKEDUP to the displayStatus field.

