Date Period
July 27, 2026
Read replicas and per-query database timeouts: Database queries accept an options argument. read_replica: true runs the whole operation against a read replica, and timeout sets the statement timeout in seconds. Passing options at all applies a default timeout of 19 seconds.
ecdh_compute filter: Computes a shared secret from an EC private key and a public key, accepting either a PEM-encoded EC public key or a Base64url-encoded raw key. The result can be returned as raw bytes, hex or base64.
hkdf filter: The HMAC-based Extract-and-Expand Key Derivation Function from RFC 5869, with optional salt, info, output length and hash algorithm.
Binary request bodies for API Calls: A body passed directly to the api_call_send mutation through the api_call argument is now delivered to the external service byte for byte, so an encrypted binary payload can be sent as an HTTP request body.
Deploy installs missing module dependencies: When a declared module is missing or its version does not match, deploy downloads it from the registry recorded in the module lock file's own registries map.
encrypt honours an explicit IV: The optional fourth argument, the initialization vector, now applies to symmetric algorithms. This makes deterministic-nonce schemes possible, such as Web Push message encryption.
jwt_encode accepts raw EC private keys: For ES256, ES384 and ES512, a key may now be a Base64url-encoded raw private key scalar as well as PEM.
Hardened archive extraction: Deploy and module archives are unpacked by a pure-Ruby unzipper rather than the system unzip, and entries carrying absolute paths or symlinks are rejected.
Low-level errors get full Liquid diagnostics: Errors such as FrozenError are wrapped as a proper Liquid error, with location information and structured logging.
Filter argument errors get a location: An invalid filter argument now renders with the usual Liquid error (path:line): prefix and full diagnostic data.
Repeated errors logged once: Identical errors are de-duplicated by their rendered message within a single render.
split leaves an array unchanged instead of corrupting it, and returns a clear argument error when given a hash.
Parse errors inside partials and JSON literals point at the partial's own file and line.
Instance Clone stores zero-byte files correctly instead of dropping them.
Type casting and sanitization switches are always restored after a caught error.
July 15, 2026
Multiline tag syntax: A Liquid tag can span multiple lines, so long or deeply nested arguments can be formatted for readability instead of being crammed onto one line. This applies to every tag, including assign, function, include and background.
JSON literal arguments: JSON objects and arrays can be passed straight to tags and filters, with no assign or parse_json step first. Nested structures, variables as values and string interpolation are all supported.
Liquid errors carry a stack trace: A rendering error now reports the file and line it came from, rather than only a top-level message. It uses the same mechanism as the log tag.
WebSocket subscriptions always resolve: A channel subscription now returns an explicit verdict either way. A failure arrives as a structured subscription_error carrying a machine-readable code (unauthorized, instance_not_found, subscribed_partial_error or internal), a human-readable description, and guidance on whether to retry.
New websockets_require_subscribed_partial flag: This app/config.yml flag requires a channel to have a subscribed partial. It defaults to true, so a channel without one has its subscriptions rejected. Review your channels before upgrading if any of them rely on the previous behaviour.
File upload library upgraded: Brings upstream security patches, bug fixes and performance improvements. No application changes are required.
July 10, 2026
June 29, 2026
asset_name_to_raw_url filter: Resolves an asset by name to its raw URL. {{ 'logo.png' | asset_name_to_raw_url }} returns the asset's raw URL, or nil when no asset of that name exists.
expires_in argument for the jwt_token field: The User.jwt_token GraphQL field now accepts an optional expires_in argument, in seconds, capped at one year. When it is omitted the token expires with the instance session timeout.
JWT authentication hardened: Three changes, all on by default. Tokens now carry standard iat and exp claims and are rejected once expired. Each token carries an iss claim tied to its instance, so a token minted on one instance cannot be replayed against another. And changing a password advances a per-user invalidation watermark, which invalidates every JWT issued before the change.
Cross-origin WebSocket support: A WebSocket connection now resolves the instance from the host rather than the Origin header alone, so cross-origin clients connect correctly. A channel with no explicit authorization now requires one before a cross-origin connection is accepted.
Updated internal dependencies: Refreshed for security and currency.
min and max GraphQL aggregations compute against the requested field instead of the record id.
hash_assign parses nested bracket lookups correctly.
Postgres cardinality errors during deploys involving certain associations no longer occur.
Stale MD5 cache entries are no longer left behind after a deploy.
Instance Clone continues when an individual asset fails, instead of stopping.
June 15, 2026
Release Date: June 11, 2026
is_percentage_processing_fee flag (default: false / fixed amount). The processing_fee_amount is automatically computed server-side on every save using the order/quote base (subtotal + tax + shipping − discounts, clamped to $0). Applies across the v2 API, CSV import, and quote-to-order conversion.quote_number is now automatically generated from the record ID on creation. The field is no longer required on POST or CSV import.Quotes
items[] flattened to canonical shape; id added to all nested objectsquote_status defaults to Draft when not supplied; enum and reference integrity validation added; quote_contact_id correctly derived; geo_json cascade fixedquote_number now enforceditems[] flattened; id added to nested objectsOrders
items[] flattened to canonical shape; doc examples correcteditems[] flattened in response; *_contact_id fields removed (not in schema); geo_json cascade fixeditems[] flattened in response; order_company correctly marked as not requiredOrder Items
order.uuid now rejectedpage/size required flags corrected; 400 example fixed; nested id fields addedOrder Discounts
id added to nested order and discount objects across GET list, GET single, DELETEOrder Shipping Packages
Payments
id added; stale enum values removed; 404 shape correctedid fixedQuote Discounts
id added to nested quote and discount objectsQuote Items
id added to nested objects in DELETE 200 doc exampleQuote Shipping Packages
page/size correctly marked Optionalid added to nested objectsSystem Fields
Custom Fields
id validation addedConfiguration
livemode rejectedmeasurement_unit and weight_unitFreight Suppliers
page/size correctly marked Optional in GET list docProducts
effective_price is now always computed server-side on create/update and cannot be overridden via the API or CSV importeffective_price on all existing products and variants using paginated queries (500 per page) to avoid table locksid sub-field added to all nested relation objects (order, quote, contact, company, discount, product, product variant) across orders and quotes v2 API responses and doc examplesIs Percentage Processing Fee columnitems[] on orders and quotes has been flattened from { id, properties: { uuid, ... } } to a flat object { id, uuid, ... } across all endpoints (GET list, GET single, POST, PATCH, DELETE). Any client reading item.properties.* will break.quote_number is now required on quotes PATCH.*_contact_id fields removed from Orders POST — these were not in the order schema and are no longer accepted.processing_fee_amount is now server-computed on every save. Any client-supplied value is ignored and overwritten. Clients should stop sending this field and read it back from the response.effective_price — effective_price on products and product variants now always stores the ex-tax active list price. Previously it stored the raw sale_price or regular_price regardless of tax-inclusive flags.
is_tax_included, is_sale_price_tax_included, or is_variant_sale_price_tax_included set to true will see effective_price change after the backfill migration runs.effective_price will reorder. Re-test any storefront listing, sort, or price-range filter that reads this field.effective_price is read-only. It is computed server-side on every create/update; values sent in POST/PATCH bodies or CSV imports are ignored and overwritten — read it back from the response.total_price is unchanged. It stays tax-inclusive.May 20, 2026
POST /crm/api/v2/custom-fields/contacts endpoint.POST /crm/api/v2/custom-fields/companies endpoint.created_by (set once at creation, immutable on edit) alongside last_updated_by, so the original creator stays visible after the activity has been edited.{"error":"unauthorized"}) on the original URL across all HTTP methods. Previously the response was a 302 redirect to the login page; clients that followed the redirect must update to handle the 401 directly.errors (array) to singular error (string). Clients that parse the errors array on this endpoint must switch to error.PlaceAutocompleteElement), keeping the feature working as Google retires the legacy widget.old_url contained a leading slash (was producing double-slash URLs)./v2/contacts/{uuid} now preserves every untouched property — previously, sending only the field you wanted to change could either return a 400 error or null out the rest of the contact./v2/tasks/{uuid} and /v2/tasks/comments/{uuid} no longer returns 400 when the request body omits a field; untouched properties are preserved, matching the contacts behaviour.May 20, 2026
May 19, 2026
Faster related_records: Reduced object allocations in related_records, improving performance up to 4x.
Asset cache invalidation now per-instance: Changed the internal implementation of cache invalidation for assets. The updated=... query parameter appended by the asset_url (and asset_path) filter is now derived from a per-instance assets_updated_at timestamp that is bumped whenever assets change, instead of being derived per asset. This means all asset URLs are invalidated together when any asset is updated, ensuring CDN caches stay consistent.
Batched page loading on deploy: Pages are now loaded in batches during deploy, providing better support for very large deploys - particularly those containing pages generated by Static Site Generators (SSG).
Faster Instance Clone asset dump: The asset manifest step of insites-cli duplicate is significantly faster, reducing the time spent in the export phase of cloning instances with large numbers of assets.
InstanceClone clone-record status on export failure: Previously, InstanceClone could leave the target instance's clone record in pending indefinitely when export failed (for example due to an oversized file). Failures are now surfaced cleanly as a terminal failed status with a sanitized error message, so insites-cli polling resolves instead of hanging.
cache_control argument for admin_assets_create and admin_asset_update mutations: When provided, the value (for example max-age=31536000, public) is stored in the asset's metadata and applied as the Cache-Control header on the underlying object in the file storage provider (S3, R2, etc.), allowing CDNs and browsers to cache assets according to your policy.
authorization_policies argument for admin_page_create and admin_page_update mutations: Authorization policies can now be associated with a page by name (the same identifier used in Liquid frontmatter, e.g. page_policy or modules/my_module/page_policy), in addition to the existing authorization_policy_ids. Names and ids can be combined; all provided names must resolve to existing AuthorizationPolicy records.
url argument in admin_assets_create: Asset URLs are now derived from the asset name and the per-instance CDN, so the argument is ignored. Upload bytes via admin_assets_presign_urls first, then call admin_assets_create without the url argument. The argument will be removed in a future release.May 14, 2026
search_by / keyword / exact interact, and how search combines with pagination and sort./admin/api/<module>/<resource>#<endpoint>. Older URLs redirect there automatically so existing bookmarks and shared links keep working.event_uuid on event sponsors, cart_uuid on cart items) now show a description instead of rendering blank..file_name, .extension, .url) under upload fields. The information is still available in the resource's response Object reference where it belongs.axios HTTP client from 0.27.x (end-of-life) to 1.16.0, picking up two years of security patches and bug fixes.May 08, 2026
/all_assets/api/v2/... to /asset/api/v2/... for a cleaner public API surface. External integrators must update their base path.GET /asset/api/v2/credentials so external API consumers can retrieve the S3 presigned-POST credentials required by the Add Asset flow. Listed in the API documentation under a new "Assets Credentials" section.Filter by
Date Period