
A store has three shirts. One location holds one shirt, another holds two. An order for three arrives, and Shopify assigns the whole line to the first location, which finishes the day reading negative two.
Nothing was oversold in aggregate. The store had three, the customer bought three. The negative number is an artifact of how orders get routed (opens in new tab), and it has nothing to do with any setting anyone turned on.
Cycle counts, reorder points and forecasting don’t touch that number. The failures that cost money during peak are structural, they live in the platform’s own model, and none of the three below is a counting error.
Shopify Inventory Management Runs On Two Vocabularies
The Shopify admin displays five inventory states. The GraphQL Admin API exposes eight. Your merchant and your developer are reading different taxonomies of the same shelf, which is why they disagree about what’s in stock and why neither of them is wrong.
The Admin Shows Five States. The API Has Eight.
The five merchant-facing states (opens in new tab) are On hand, Available, Committed, Unavailable and Incoming, and On hand is the total of Committed, Unavailable and Available. The API has eight states (opens in new tab): incoming, on_hand, available, committed, reserved, damaged, safety_stock and quality_control.
The admin’s Unavailable bucket is four API states wearing one label. Quantities in reserved, damaged, safety_stock and quality_control display as Unavailable to merchants tracking inventory in the admin.
| API state | What it holds | Admin label | Writable through the Admin API |
|---|---|---|---|
| incoming | Units on their way to a location, not physically present there | Incoming | No. Derived from transfers, purchase orders and apps |
| on_hand | Everything physically at the location, equal to the sum of the six states below | On hand | Settable as an absolute value |
| available | Units a merchant can sell | Available | Yes, absolute set or delta adjust |
| committed | Units set aside against a placed but unfulfilled order, a draft-order reservation, or a transfer marked ready to ship | Committed | No. Shopify-managed only |
| reserved | On-hand units temporarily set aside, for example for a hold or an inspection | Unavailable | Yes, adjust or move |
| damaged | On-hand units not sellable or usable due to damage | Unavailable | Yes, adjust or move |
| safety_stock | On-hand units held back to guard against overselling | Unavailable | Yes, adjust or move |
| quality_control | On-hand units in inspection and not sellable until it clears | Unavailable | Yes, adjust or move |
The two arithmetics are the same equation at different resolutions. Admin On hand is Committed plus Unavailable plus Available; API on_hand is available plus committed plus reserved plus damaged plus safety_stock plus quality_control. Substitute the four states that make up Unavailable and the two lines are identical. Incoming sits outside both totals, because neither one counts stock that hasn’t landed.
Seven of the eight states are mutually exclusive, and on_hand isn’t one of them, because on_hand is the sum of six of the others. Getting that backwards produces a reconciliation script that double-counts every unit in the warehouse.
The admin’s fourth unavailable reason (opens in new tab) is labeled Other, for inventory that’s unavailable for a reason you’ve determined, while the API’s fourth unavailable state is reserved. Don’t assume those two are the same field.
Committed Is Shopify-Managed And Never Writable
You can’t use the Admin API to adjust or move inventory quantities (opens in new tab) in the committed state. Shopify manages it through order creation, order fulfillment, draft-order reservations, and transfers marked ready to ship.
That single restriction is one way an ERP sync oversells. An ERP that thinks in one “quantity on hand” number can’t push that number to Shopify, because Shopify is holding units in committed against orders the ERP may not have ingested yet. Push raw on-hand and you overwrite the arithmetic, restoring units that are already spoken for. This is the mechanical reason connecting Shopify to an ERP has to be designed around who owns which number, not around which fields map to which.
Does Shopify hold inventory for a shopper during checkout? The commit point is order placement: Shopify decrements available and increments committed when an order is placed, then decrements committed on fulfillment. Shopify doesn’t document a time-boxed hold that runs while a shopper sits on the checkout page, or whether adding to cart reserves anything. The one expiring reservation Shopify documents for merchants to control is reserveInventoryUntil on draft order creation (opens in new tab), which takes a datetime and applies to draft orders rather than checkout.
Absolute sets are for systems that act as the source of truth for inventory quantities; deltas are for everything else. A system that’s the source of truth and doesn’t care about the current quantity doesn’t need to implement compare and swap (opens in new tab), and Shopify’s guidance is to opt out. If a team can’t say which one their system is, it shouldn’t be writing absolute values into a live catalog.
Multi-Location Routing Oversells Your Shopify Inventory On Its Own
If a location has enough inventory to fulfill an entire order, the order goes there. If no single location can fulfill the order, it’s split across multiple locations or the highest-priority location oversells the product.
Shopify will split line items across locations. By default it won’t split a single line item’s quantity. An order for three of one SKU can’t be sourced one from A and two from B, so when no location holds three, the highest-priority stocked location takes all three and goes negative.
That’s the negative two. In Shopify’s own example, with New York holding one shirt, orders for six and for seven shirts both land as oversold at New York, assigned to the highest-priority location where shirts are stocked.
Why is my inventory negative when I never turned on overselling? Because per-location depth thinned below order size. Peak is exactly when that happens: order quantities rise, stock spreads across more locations as you pre-position for shipping speed, and “no single location can fill this” stops being an edge case. The number isn’t a counting error, and no amount of cycle counting prevents it.
Continue Selling Is The Second Cause, Not The First
Out of stock means tracked inventory at zero or below, and the Continue selling when out of stock (opens in new tab) option lets customers buy past that point. In the API it’s the inventoryPolicy field on a product variant, with the enum values CONTINUE and DENY (opens in new tab) meaning customers can and can’t buy after it’s out of stock.
It’s a per-variant setting, which makes auditing it across the catalog a finite task rather than vague advice. Shopify lists legitimate reasons to use it: preorders, stock arriving soon, selling first and ordering later, and not tracking real quantities in Shopify at all.
Continue selling when out of stock doesn’t apply to orders placed from Shopify POS. Staff can keep selling when available inventory reaches zero and below, and POS warns them before they sell an item that isn’t available.
Online Quantity Is Not Your Total Shopify Inventory
Ecommerce inventory management has three different correct answers to “how many do we have,” and merchandising, warehouse and the storefront each use a different one. Total inventory counts stock at every location, including the ones that don’t fulfill online orders. Online quantity counts only locations set to fulfill online orders. And sellableOnlineQuantity on the ProductVariant object is the total sellable quantity for online channels (opens in new tab), which doesn’t represent total available inventory or capture limitations based on customer location.
Preventing a location from fulfilling online orders removes that location’s inventory from a product’s online quantity, and online quantity is the number displayed to customers when you show inventory levels. That location’s stock can still fulfill draft orders and orders from other sales channels.
Why does my product show out of stock when I have stock? If your online-fulfilling location sits at zero or below while a location that doesn’t fulfill online orders holds available inventory, the item still shows as out of stock to customers, and you have to add stock to the online-fulfilling location to make it buyable. A retail-only store quietly holding forty units doesn’t help a storefront reading zero.
Inventory also doesn’t pool. Each location’s inventory is independent (opens in new tab) and can’t be shared or pooled with other locations, a new product starts at every location with a quantity of zero, and a product must be active at a minimum of one location to be sellable.
How many locations can I have? The active location limits by plan (opens in new tab) are 2 on Starter, 10 on Basic, 10 on Grow, 10 on Advanced, and 200 on Plus. Deactivated locations don’t count against the limit, and apps that physically stock inventory for you, including dropshipping apps and third-party logistics services, are treated as locations without counting toward it.
Basic, Grow and Advanced share the same figure, and the step change happens at Plus.
Five Of Eight Inventory States Fire No Webhook
Shopify has six inventory webhook topics: create, update and delete on inventory items, and connect, disconnect and update on inventory levels. Changes to the committed, reserved, damaged, safety_stock and quality_control states don’t trigger webhooks (opens in new tab).
Five of the eight states are silent. The one your peak integration most wants to observe, stock being committed against an order that just landed, produces no inventory webhook at all. A 3PL polling inventory_levels/update sees the available movement and has no event stream describing the commit, and every safety-stock, damage and quality-control adjustment your own warehouse app writes is invisible to every other subscriber.
Why Shopify Inventory Tracking Needs A Reconciliation Job
Shopify’s own guidance is not to rely on receiving data from webhooks (opens in new tab):
Your app shouldn't rely on receiving data from Shopify webhooks. Webhook delivery isn't always guaranteed, and your app can miss or mishandle events for other reasons, such as handler failures or downtime. For redundancy, use reconciliation jobs to periodically fetch data from Shopify so that your app stays consistent with Shopify's data.
Ordering isn’t guaranteed either, within a topic or across topics for the same resource, and Shopify recommends organizing on the X-Shopify-Triggered-At header or the payload’s updated_at. The implementation it suggests is the whole design: query with updated_at filters for everything changed since the last run.
An HTTPS webhook subscription (opens in new tab) gets a one-second connection timeout and a five-second timeout for the entire request. Shopify treats any response outside the 200 range, including 3XX codes, as an error, and retries 8 times over the next 4 hours. After 8 consecutive failures the subscription is automatically deleted if it was configured through the Admin API, with warning emails going to the app’s emergency developer email address.
Five seconds is generous until your own handler is queueing behind a traffic spike, and eight consecutive failures inside a four-hour window during peak don’t degrade your inventory feed, they remove it, with the only notice landing in whatever address sits in that emergency contact field. Checking that address costs a minute and it belongs in the Black Friday readiness sequence alongside the rest of the pre-peak checks.
We build these integrations, and the argument we make to every client is the same: the reconciliation job ships with version one, not as a hardening phase scheduled for January.
Two Traps In Shopify's Inventory API
Shopify’s inventory API currently holds two traps: a mutation reference that contradicts its own schema, and a behavior change that developers and merchants each meet separately.
The first is in the inventorySetQuantities reference (opens in new tab) at API versions 2026-07 and 2026-10. The mutation’s prose description still describes a compare-and-set built on a compareQuantity field with an ignoreCompareQuantity opt-out, and several of the reference’s own code examples pass ignoreCompareQuantity: true. The schema block in that same reference contains neither field. Concurrency control in both versions is changeFromQuantity on the per-item input, so a developer who copies the examples writes against fields the schema doesn’t define.
Read the schema block, not the prose, and re-check the page before you build, because Shopify may correct it. The mechanism itself is worth using: changeFromQuantity lets you declare the quantity you expect to find before your change applies, and the mutation fails with a CHANGE_FROM_QUANTITY_STALE error rather than overwriting concurrent work. Shopify’s own worked example runs 100 red shoes down to 90 while 20 sold underneath, losing 20 units silently. Compare and swap is implemented on several mutations, including inventoryAdjustQuantities, inventoryMoveQuantities, inventorySetQuantities and productVariantsBulkUpdate.
Quantity Writes That Succeed And Sell Nothing
The second trap is a behavior change rather than a documentation error, and it’s the quieter of the two. Before API version 2026-04, the three quantity mutations returned an ITEM_NOT_STOCKED_AT_LOCATION error when the inventory item wasn’t stocked at the location. From 2026-04 onward, those mutations can create an inactive InventoryLevel and store the quantity, and the item isn’t available for sale or fulfillment from that location until it’s activated there.
The API returns success. Your logs are clean. Your integration reports that it wrote 400 units into the Denver warehouse, and Denver sells zero of them.
On the merchant side, a dash in the Available column means the product is inactive at that location, so it isn’t sellable or fulfillable from there even though an On hand quantity is recorded. This commonly happens after transferring inventory, receiving a transfer, adding a new store location, or updating quantities with the bulk editor or an inventory CSV file, because those actions record quantity without changing activation.
The fix is to pair every quantity write with explicit activation through inventoryActivate (opens in new tab), or the bulk toggle when one item needs activating across several locations. Activating a level that already holds quantities without supplying available or on-hand values preserves what’s there.
You can’t solve that ordering inside a bulk import. The GraphQL Admin API doesn’t serially process the contents of the JSONL file (opens in new tab), and Shopify warns against relying on a particular sequence of lines. “Set the quantity, then activate the item” isn’t expressible as two adjacent lines in one file, and on 2026-04 and later, attempting it is how you produce a warehouse full of dashes the week before peak.
From API version 2026-01 and higher each app can run up to five bulk mutation operations concurrently per shop, the operation has to complete within 24 hours or it’s stopped and marked failed, the mutation passed in is limited to one connection field, and the JSONL file can’t exceed 100MB.
Shopify Inventory Integrations Pinned To 2025-10 Fall Forward After October 16, 2026
Shopify releases a new API version every three months (opens in new tab) at the start of the quarter, with each stable version supported for a minimum of 12 months and at least nine months of overlap between consecutive versions. Version 2026-10 was released October 1, 2026 and is accessible until October 16, 2027 at 15:00 UTC. Each stable release is published alongside a release candidate for the next version, which may include backwards-incompatible changes and which Shopify doesn’t recommend for production.
| API version | Release date | Accessible until |
|---|---|---|
| 2025-10 | October 1, 2025 | October 16, 2026, 15:00 UTC |
| 2026-01 | January 1, 2026 | January 16, 2027, 15:00 UTC |
| 2026-04 | April 1, 2026 | April 16, 2027, 15:00 UTC |
| 2026-07 | July 1, 2026 | July 16, 2027, 15:00 UTC |
| 2026-10 | October 1, 2026 | October 16, 2027, 15:00 UTC |
Shopify set October 16, 2026 at 15:00 UTC as the end of access to 2025-10, a date in the middle of peak preparation.
Shopify doesn’t break you outright. If your app targets an inaccessible version it falls forward and responds using the oldest accessible stable version, and responses carry an X-Shopify-API-Version header reflecting the version actually used. If that header differs from what you requested, you’re pinned to something inaccessible and Shopify has quietly moved you.
Reading that header on one response is a free pre-peak audit, and it’s Shopify’s own suggested check. Webhook payloads are versioned the same way and carry the same header.
Falling forward means your integration changes behavior on Shopify’s schedule rather than yours: from October 16, 2026 a request pinned to 2025-10 is served by 2026-01, the oldest accessible stable version until January 16, 2027, and each later retirement moves it forward again. Two version-scoped changes sit on that path for inventory work, at 2026-04 and 2026-07. Scheduled inventory changes are being phased out: inventorySetScheduledChanges isn’t available in the 2026-07 Admin API, scheduledChanges on InventoryLevel is deprecated, and scheduling requires an API version between 2024-01 and 2026-04. Anything that scheduled incoming-to-available transitions breaks on 2026-07 and every version released since, including the one that’s latest today, and the mutation never changed quantities on its own anyway.
The second is idempotency. As of 2026-01, inventorySetQuantities supports an optional idempotency key through the @idempotent directive, and as of 2026-04 the key is required. Shopify tracks idempotency keys for 24 hours (opens in new tab), after which duplicates are treated as separate operations, so a dead-letter queue that retries a failed adjustment two days later has no protection and double-applies the change.
Stocky Is Gone And Shopify Inventory Management Moved Into The Admin
Stocky is no longer available as of August 31, 2026 (opens in new tab). You now need to manage inventory in the Shopify admin and Shopify POS. The app was delisted from the App Store on February 2, 2026, and the Stocky APIs stopped working on August 31, 2026, so third-party tools that integrated with it need rebuilding against Shopify’s inventory system.
Four workflows move into the admin: moving inventory across locations with transfers, tracking and receiving purchase orders from suppliers, adjusting inventory quantities, and reporting on historical inventory changes.
Shopify says you’ll have read-only access to export your data for at least 90 days after August 31, 2026, which puts the earliest end of that window in roughly late November. Historical data such as old purchase orders and stocktakes doesn’t move into Shopify automatically, historical purchase orders can’t be imported, and suppliers can’t be exported from Stocky at all.
Native CSV upload for purchase orders only adds product line items to a new draft purchase order and can’t import past statuses, received quantities, or supplier links. So the export window can close as early as the end of November 2026, when a POS-heavy retailer has the least time to pull the data. Export it while the read-only access is still open.
The replacement primitives exist and they aren’t ready for production. The physical inventory feature preview (opens in new tab), announced on July 17, 2026 (opens in new tab), adds bins, counts and purchase orders as API primitives. It runs on the unstable version of the GraphQL Admin API, only stores with the preview enabled can call it, and Shopify says the APIs are under active development and subject to change.
The model is additive. Bins add a finer grain inside a location, so an item’s on-hand can be split across the bins where it’s physically stored, and the location’s total on-hand equals the unbinned quantity plus the sum of every bin quantity. Existing calls still work, with quantity set at the location level landing in the unbinned quantity, and the location-level aggregates for available, committed and on-hand stay unchanged.
For the automation gap Stocky leaves behind, Shopify’s guidance is to set up automated low-stock alerts using Shopify Flow, and it markets Sidekick as able to answer reordering questions and draft the purchase order or transfer for you based on sales data. The Sidekick part is Shopify promoting its own product, so read it as a vendor capability claim rather than a measured outcome. What it does establish is that reorder suggestions live in Sidekick, not in a report.
The 28-Day Window Behind Shopify's Inventory Reports
Shopify’s native inventory reports (opens in new tab) are accurate. They’re also answering a different question than the one you’re asking in November, and the reason is a window you can’t change.
Days of inventory remaining is calculated as the total quantity of items still in inventory divided by the average quantity of items sold per day, and a variant’s average quantity sold per day is calculated on its sales over the last 28 days. ABC product analysis grades each variant on the percentage of revenue it contributed over the last 28 days, and the timeframe can’t be adjusted.
Run either report on November 10, 2026 and the trailing window covers October 13 through November 9. It contains no Black Friday, no Cyber Monday, and no promotional traffic. It will tell you, correctly, how long your stock lasts at pre-peak demand.
Sell-through lags too. Shopify calculates sell-through rate as total quantity sold divided by total quantity sold plus total quantity still in inventory. Due to data latency and processing times, the most recent period the report can return is approximately two days prior to the current date, closer to three days for earlier time zones such as UTC +14:00. During the fastest 96 hours of your year, the number you’re reading describes a period that ended two days ago.
Reports display a maximum of 1,000 rows (opens in new tab), with totals still calculated across all rows, which means a large catalog needs an export before it can be analyzed. Historical data for inventory-based metrics goes back only to October 1, 2023, and historical inventory for deleted locations doesn’t appear in inventory reports since only active locations show in historical reporting data. Deleting a location stops its history appearing in the reports.
None of that is a defect. These are month-end and trailing-average instruments, they’re good at describing what your catalog normally does, and peak planning needs a multiplier applied on top that Shopify doesn’t supply. Build the multiplier from last year’s own numbers, before the window you’re comparing against fills up with this year’s peak.
Safety Stock: Shopify Inventory Management Gives You The Shelf, Not The Rule
Shopify supports safety stock as a state, and a state isn’t a policy.
There’s a first-class safety_stock state defined as on-hand units set aside to help guard against overselling, a matching safety_stock value among the 17 adjustment reasons the reason field accepts, and inventoryMoveQuantities to move units from available into safety_stock and back. Shopify frames those transitions as insuring against fluctuations in demand. The admin exposes the merchant-facing equivalents through the Safety stock unavailable state and the Adjust by flow.
Shopify doesn’t document a safety-stock threshold or reorder point per variant in the admin, or automatic replenishment of safety_stock, so moves are explicit mutations or explicit admin adjustments. For low-stock notifications, Shopify tells merchants to install an inventory alert app (opens in new tab) from the App Store or to use Shopify Flow.
So reserving peak buffer is a mechanical operation you perform, or one you automate yourself by pairing inventoryMoveQuantities with your own threshold logic. That’s a build decision to make before peak, not a setting to find in November.
Moving units into safety_stock reduces available, available drives online quantity, and online quantity is what customers see. Buffer isn’t free. It’s a decision to display a smaller number on purpose, and everyone who reads that number needs to know it was deliberate.
Store Inventory Control Levers You Can Set Before Peak
Shopify has five order routing rules, applied in sequence, with each rule operating on the results of the previous one. Order routing (opens in new tab) requires two or more active locations. The default strategy (opens in new tab) optimizes for fulfillment from the closest location within the destination market that has all the items in stock.
Straight Lines, Ranked Locations, And A Boolean Metafield
The five rules are minimize split fulfillments, stay within the destination market, ship from the closest location, use ranked locations, and use location metafields. Ranked locations and location metafields (opens in new tab) can each be added to a strategy more than once, and ranked locations work by group, so every location in the topmost group shares a ranking of 1.
The metafield rule is the peak lever: define a boolean location metafield, such as Shopify’s own example has_capacity, and prioritize locations that have capacity to fulfill orders. A warehouse drowning on the Monday after Black Friday gets de-prioritized by flipping one metafield, with no inventory moved and no location deactivated. That’s a change you can make at 2am, and it only exists if someone created the metafield and added the rule beforehand.
Read the closest-location rule literally. Shopify measures distance in a straight line, which isn’t driving distance, recommends applying the rule last, and prioritizes the older location when two locations share an address. A location across a bay or a mountain range can beat one an hour closer by road.
Local pickup has its own failure mode under load. By default, pickup is available (opens in new tab) only when the entire order is in stock at the pickup location. Configure store transfers and pickup stays available when the location runs dry, but the checkout displays an estimated pickup time that accounts for the transfer, so the customer’s expectation gets set against a transfer now competing with everything else the store is doing.
Two admin habits are worth fixing now. When you use the bulk editor you don’t need to specify origin and destination, and a record of your inventory movements isn’t tracked. The fastest manual fix during a peak incident is the one path that leaves no movement history, which is precisely the history you’ll want on the Monday after.
The other is the audit trail. referenceDocumentUri is accepted on inventoryAdjustQuantities, inventorySetQuantities and inventoryMoveQuantities, it takes a freeform URI recording why a change happened, and Shopify’s own examples are shaped like gid://erp/StockAdjustment/ADJ-2024-001 and gid://3pl-system/CycleCount/CC-2024-0125.
It surfaces in the adjustment history merchants see and in the inventory adjustment reports. Without it, the history says an app did it; with it, the history names the purchase order or sync job. It costs nothing to add, and Shopify publishes no way to attach one to an adjustment that’s already recorded.
One more cheap catch for anyone syncing from an external system: Shopify treats SKU strings as case-sensitive, and SKUs returned from the fetch stock endpoint (opens in new tab) are matched to Shopify product SKUs respecting case. ABC-123 and abc-123 are two different SKUs here, so check how your ERP treats case on item codes.
A full inventory request happens once every hour to keep Shopify current with a remote fulfillment service. Scope that number carefully: it’s the fulfillment-service polling cadence for legacy fulfillment service apps, not a general statement about how often Shopify syncs with 3PLs, and it doesn’t apply to a 3PL writing through the Admin API. Where it does apply, an hour is several product lifetimes during a flash sale.
The Shopify Inventory Management Setup That Holds Through Peak
This setup is a sequence, not a menu, and most of it is configuration rather than development.
- Audit the Continue selling setting across the catalog, because it's set per variant
- Audit which locations have "Use inventory at this location to fulfill online orders" turned on, because that toggle defines the number customers see
- Accept that routing oversells when no single location can fill an order, and staff your incident response for negative numbers that aren't errors
- Create a boolean capacity metafield on every location and add the routing rule that reads it, before you need it
- Check the emergency developer contact address on every app writing inventory, because that's where the only warning goes
- Read the API version header on one response from every integration and confirm nothing is pinned to a version that's inaccessible or about to be
- Find anything that schedules inventory changes, because scheduling doesn't exist in API version 2026-07 or later
- Pair every quantity write with an explicit activation call, or expect dashes where stock should be
- Send a reference URI on every adjustment starting now, so the adjustment history names the purchase order or sync job behind each change
- Keep the bulk editor out of peak-week corrections, because it records no movement history
- Set safety stock by explicitly moving units out of available, and tell merchandising the displayed number is smaller on purpose
- Export your Stocky data, including a manual capture of suppliers, since suppliers can't be exported
The reconciliation job is the one line item that’s real engineering, and it’s the one that pays for itself in a single incident. Query with updated_at filters for everything changed since the last run, on a schedule, against every location. Shopify tells you to build it. Five of eight states give you no other way to see the shelf.
We end up in this decision on most replatforms and most ERP builds, and the question is always the same: who owns the number. Answer that first and the API tells you which mutation to use, whether to implement compare and swap, and what your reconciliation job is allowed to overwrite. Answer it late and you find out during peak.
Scope Your Shopify Inventory Management Before Peak, Not During It
You built a business that can handle the volume. The setup underneath it should be able to prove that, at 2am on the Saturday after Black Friday, with a negative number on a dashboard and someone asking what happened.
Every fix above is cheap before peak and expensive in November. The metafield, the activation pairing, the reference URIs, the reconciliation job: each one covers what happens after you build it, and none of them reaches backwards over the orders that already landed.
Talk to us about scoping your inventory architecture before peak. We’ll map your locations, routing rules, integrations and API versions against what the platform actually does, and hand you the gaps in priority order.
If you want to see how we work before you talk to us, here’s how BLKDG works as a Shopify Partner.
Not sure where the gap is? That's exactly what the Digital Marketing Growth Audit is for.
A free, no-obligation look at where your site can win more traffic and conversions, with a clear digital marketing roadmap to get there. Just a straight read on where your digital presence stands and where it's headed.



