Skip to content
Article

Create a Shopify Store-Aware Conversational Chatbot

We turned a basic Shopify custom app install into an immediately useful, store-scoped AI chat experience. This deep dive walks through our OAuth flow, tenant/shop mapping, AI orchestration pattern, guardrails, and operational hardening so each merchant lands in an intelligent chat already aware of their store.
Create a Shopify Store-Aware Conversational Chatbot

Create a Shopify Store-Aware Conversational Chatbot

Installing a Shopify custom app is easy. Making that install immediately useful is much harder.

Our goal was simple to describe but non-trivial to implement: the moment a merchant finishes installing our app, they should land in an AI chat that already "knows" their store—its identity, permissions, and (when allowed) key data like products and orders.

This article walks through the end-to-end engineering approach we took to make that happen, from OAuth to tenant mapping to AI orchestration and operational hardening.

1. Why We Built This

Our first version of the app did what many integrations do:

  • The merchant installed the app.
  • We completed OAuth and stored a token.
  • They landed on a generic dashboard that asked for more configuration.

Technically, the install was a success. Practically, the merchant still had zero immediate AI value.

We wanted to change that. Our new target experience:

  • Merchant clicks "Install" in Shopify.
  • OAuth completes and we safely persist tokens and mapping.
  • We bootstrap a session that understands their specific store (and only their store).
  • The merchant lands directly in an AI chat that is ready to answer, "Show me my top products this week" or "Draft an email about the new collection" with real, store-scoped data.

Along the way we had to solve several constraints:

  • Secure OAuth and HMAC validation in the Shopify model.
  • Multi-tenant mapping of shops to internal tenants.
  • Reliable token and session handling (retries, idempotency, uninstall handling).
  • AI orchestration that could safely call Shopify APIs on behalf of the right store.

2. System Architecture at a Glance

We ended up with a layered architecture that separates concerns but keeps the install-to-chat path tight.

Core Components

  • Shopify custom app: the app surface that merchants install from the Shopify App Store or as a custom app, plus any embedded UI.
  • Installer / auth backend: handles OAuth dance, HMAC verification, token exchange, and mapping of shopDomain to an internal tenantId.
  • Chat backend: manages chat sessions, user identities, routing, and persistence of messages.
  • AI orchestration layer: wraps the model, prompt strategy, and server-side tooling that can read/write Shopify data.
  • Data store: persists tenants, Shopify tokens, install status, and audit logs.

Trust Boundaries

We explicitly modeled the main trust boundaries:

  • Browser ↔ App backend: browser is untrusted; all sensitive logic lives on the backend with CSRF protection and session controls.
  • App backend ↔ Shopify APIs: authenticated via Admin API tokens; scopes are minimized.
  • App backend ↔ AI service: we never send raw tokens or PII to the model; we send normalized, redacted data and strict instructions.

High-Level Flow

Merchant clicks "Install" in Shopify
  → Shopify redirects to our app's install endpoint with query params
    → We validate & redirect to Shopify OAuth authorization URL
      → Shopify calls our callback with authorization code
        → We exchange code for access token & verify
          → We create/update tenant mapping (shopDomain → tenantId)
            → We mint an app session & open a tenant-scoped chat

3. Shopify Install + OAuth Flow (Step-by-Step)

Shopify's OAuth flow is well-documented, but getting it production-ready and wired into AI chat requires attention to the small details.

3.1 Install Entrypoint

When a merchant installs the app, Shopify sends them to our install endpoint, for example:

GET /shopify/install?shop={shopDomain}&host={encodedHost}&timestamp=...&hmac=...

At this point we:

  • Verify required query params (shop, hmac, timestamp).
  • Validate the HMAC signature against our app secret.
  • Generate a state/nonce value and store it server-side (session or short-lived cache).
  • Redirect to Shopify's OAuth authorization URL.

3.2 Authorization Redirect

We construct an authorization URL similar to:

https://{shopDomain}/admin/oauth/authorize
  ?client_id={apiKey}
  &scope={scopes}
  &redirect_uri={ourCallbackUrl}
  &state={csrfToken}
  &grant_options[]={offline|online}

Here, two decisions matter:

  • Scopes: we requested only what we needed for chat use cases (for example, read_orders, read_products, write_draft_orders). Less scope means less risk.
  • Token type: Shopify supports offline and online tokens.

We chose to use:

  • Offline tokens for background tasks (e.g., precomputing embeddings, syncing catalogs).
  • Online tokens when we needed to act on behalf of a specific admin user in real time.

3.3 Callback Handling & Token Exchange

After the merchant approves, Shopify calls our callback, e.g.:

GET /shopify/callback?code=...&shop={shopDomain}&state=...&hmac=...

Our callback logic performs several hard checks before ever creating a tenant or issuing an app session:

  1. Verify HMAC using the app secret and raw query params.
  2. Validate state/nonce to prevent CSRF attacks.
  3. Validate shopDomain (basic format and, if desired, allowed list).
  4. Exchange code for access token with Shopify's token endpoint.
  5. Persist or update the tenant/shop mapping and token (idempotently).

Examples of failure modes we explicitly handled:

  • Invalid state: we return an error and do not create or modify any tenant records.
  • Missing shop param: we log, show a generic error, and halt.
  • Duplicate callback: we treat the callback as idempotent using a combination of shopDomain and a unique install identifier.
  • Stale install: callbacks older than a small time window are rejected and logged.

3.4 Embedded App Context

If you are building an embedded Shopify app, some nuances apply:

  • Shopify loads your app in an iframe inside the admin; you need to handle host and session parameters.
  • You may use Shopify App Bridge or other official tooling to manage redirects and session tokens.
  • We still keep all sensitive OAuth handling on our backend; the browser only sees short-lived, app-specific tokens.

4. Tenant/Shop Mapping: The Real Foundation

OAuth is table stakes. The core design decision is actually your tenant/shop mapping model.

We treat each Shopify store as a tenant inside our system. The key identifiers we persist:

  • shopDomain (unique per Shopify store, e.g. example-shop.myshopify.com)
  • shopifyAccessToken (offline and/or online, encrypted at rest)
  • tenantId (our internal UUID for this store)
  • installStatus and timestamps (installed, uninstalled, reinstalled, etc.)
  • createdAt, updatedAt, lastSeenAt

Data Model Example

Tenant {
  tenantId: uuid,
  shopDomain: string,        // unique
  shopifyAccessToken: string // encrypted
  installStatus: enum,       // INSTALLED | UNINSTALLED | PENDING
  installedAt: datetime,
  uninstalledAt: datetime | null,
  lastSeenAt: datetime | null
}

Idempotent Install Logic

Install and callback handling must be safe to retry. Our strategy:

  • Use shopDomain as the natural key.
  • If a record exists, update the token and timestamps instead of creating a duplicate.
  • Wrap the mapping + token save in a transaction with an idempotency key derived from the callback.
  • Log a correlation ID across the entire install flow for debugging.

Reinstall and Uninstall Behavior

We subscribed to the app/uninstalled webhook to handle lifecycle events:

  • On uninstall, we mark the tenant as UNINSTALLED, revoke or invalidate tokens, and clean up cached data.
  • On reinstall, we reuse the same tenantId for continuity but generate a new access token and refresh installStatus.

Critically, we enforce strict cross-tenant isolation. Every request that touches Shopify APIs goes through a resolution step:

  1. Resolve the request's app session to a tenantId.
  2. Fetch the tenant's shopDomain and tokens from storage.
  3. Ensure the AI tooling call can only operate on that tenant's store.

5. Handing Shopify Context Into AI Chat

Once the install is complete and the tenant is created, we immediately bootstrap a chat that is scoped to that store.

5.1 Bootstrap Flow After Install

  1. Resolve shopDomain → tenantId The callback handler looks up or creates the tenant and captures its tenantId.
  2. Mint an app session / auth token We issue a first-party session token (e.g., JWT or opaque token) bound to that tenantId and the current admin user.
  3. Redirect into chat UI The merchant lands on our chat frontend with a valid session token.
  4. Chat backend loads tenant-scoped context The chat backend calls our AI orchestration layer with a context package for that tenant.

5.2 The Tenant Context Package

When we call the AI orchestration layer, we do not just send the user message. We send a structured context bundle, for example:

{
  "tenantId": "...",
  "store": {
    "shopDomain": "example-shop.myshopify.com",
    "displayName": "Example Shop",
    "timezone": "America/New_York"
  },
  "capabilities": [
    "READ_PRODUCTS",
    "READ_ORDERS",
    "CREATE_DRAFT_ORDERS"
  ],
  "flags": {
    "canUseCustomerData": false,
    "betaFeatures": ["ai_email_drafts"]
  }
}

This gives the model clear boundaries on what it can and cannot do for that tenant.

5.3 Guardrails and Privacy

We added several guardrails:

  • No cross-tenant context: the orchestration layer never mixes data across tenants. Every tool call requires a tenantId.
  • Redaction of sensitive fields: for example, we avoid sending full customer emails or addresses to the model unless explicitly required and permitted.
  • Server-side authorization before executing tools: even if a prompt asks for "all customers", our server enforces capabilities and scopes.

6. AI Workflow Design

We use a tooling pattern where the model does not call Shopify directly. Instead, it asks our server for data, and the server talks to Shopify.

6.1 Prompt Layering

Each AI request is composed of several layers:

  1. System instructions Define the model's role, tone, and hard guardrails. For example, never hallucinate orders; if unsure, ask to fetch data via tools.
  2. Tenant/store metadata Derived from the context package: store name, timezone, capabilities, feature flags.
  3. User intent The actual user message ("Draft a marketing email for my top 3 products this week").

6.2 Tooling Pattern

We expose server-side tools to the model such as:

  • list_products({ limit, sort })
  • get_top_selling_products({ window })
  • create_draft_order({ lineItems, note })

The typical sequence:

  1. Model decides it needs data and calls a tool, e.g. get_top_selling_products({ window: '7d' }).
  2. Our server receives the tool call and resolves the tenantId from the chat session.
  3. The server calls the Shopify Admin API using the tenant's access token.
  4. We normalize and optionally redact the result.
  5. We pass that data back to the model as tool output.
  6. The model uses that trusted data to craft a response.

6.3 Why Server-Side Fetching Beats Client-Side Calls

We deliberately avoid client-side calls from the browser directly to Shopify for several reasons:

  • Security: access tokens never leave our backend.
  • Consistency: we centralize rate limiting, retries, and error handling.
  • Auditability: every tool call and Shopify API request can be logged with a correlation ID.
  • Guardrails: the model cannot bypass server-side checks, even if a prompt tries.

7. Reliability and Operational Hardening

A beautiful architecture is useless if installs fail or chats intermittently break. We invested early in reliability primitives.

7.1 Retry Strategy for OAuth & Callbacks

Network calls to Shopify (especially token exchange) can fail transiently. Our approach:

  • Use exponential backoff with jitter for the token exchange request.
  • Retry only idempotent operations (e.g., reading tenant, saving token with upsert semantics).
  • Never retry blindly on validation failures like bad HMAC or invalid state.

7.2 Idempotency Keys

For critical paths like the install callback, we use idempotency keys (e.g., a hash of shopDomain + timestamp or a dedicated installId):

  • If a request with the same key is reprocessed, we return the existing result.
  • We store the status (success/failure) and correlation ID with that key.

7.3 Observability and Correlation IDs

Every install and chat bootstrap request gets a correlation ID that we propagate across:

  • Install entrypoint
  • OAuth callback handler
  • Tenant mapping creation/update
  • Chat session bootstrap
  • First AI request and Shopify tool calls

We emit structured logs with fields like tenantId, shopDomain, correlationId, and eventType (INSTALL_STARTED, TOKEN_EXCHANGED, CHAT_BOOTSTRAPPED, etc.).

On top of logs, we track metrics such as:

  • Install success rate
  • Callback error rate (HMAC failures, state mismatches, token exchange errors)
  • Time from install start to first successful chat message

7.4 Webhook Considerations

We treat webhooks as first-class citizens:

  • app/uninstalled: mark tenant as uninstalled, revoke tokens, and disable AI tools for that tenant.
  • Optional domain or store updates: keep metadata fresh but never change tenantId for a store.

8. Security Decisions We Made

Security decisions are not just about compliance; they also reduce debugging pain and production surprises.

8.1 HMAC Verification

We implemented HMAC verification consistently for:

  • The initial install request.
  • The OAuth callback.
  • Webhooks (where Shopify signs the body, not query params).

Key practices:

  • Use the exact algorithm Shopify specifies (HMAC-SHA256) with the app secret.
  • Sort and encode query params precisely as documented before hashing.
  • Use constant-time comparison when checking expected vs. received HMAC.

8.2 CSRF & State Handling

Every OAuth authorization redirect includes a state parameter that we store and validate. If the state does not match on callback, we abort the flow and log the incident.

8.3 Token Storage & Encryption

Access tokens are stored only on the server side:

  • Encrypted at rest using our platform's KMS or a dedicated secrets solution.
  • Never exposed to the browser, logs, or analytics systems.
  • Access controlled by strict service-to-service permissions.

8.4 Scope Minimization and Least Privilege

We started from the minimal set of scopes necessary for our chat use cases and added only when a clear, approved feature required it. This also made our AI tooling surface simpler.

8.5 Session Expiration and Rotation

App sessions for admins:

  • Have a short, sliding expiration.
  • Can be invalidated if uninstall or token revocation occurs.
  • Are rotated periodically to reduce risk of token reuse.

9. Developer Experience & Debugging Playbook

Running a multi-tenant Shopify + AI app in production is as much about DX as it is about architecture. We built a small playbook for ourselves.

9.1 Local Testing Setup

  • Expose local callbacks via a tunneling tool (e.g., ngrok or similar) to receive Shopify redirects and webhooks.
  • Maintain separate app configurations for development, staging, and production with different API keys and redirect URLs.
  • Use seeded test stores with realistic but non-sensitive data.

9.2 Known Symptoms → Likely Cause

Symptom Likely Cause Merchant sees "invalid signature" during install HMAC verification bug (parameter encoding or missing fields) Install completes, but chat page shows unauthorized Tenant not created or session not bound to tenantId during callback Chat works, but AI replies with "I cannot access your data" Missing Admin API scopes or capability flags not set in context Occasional 500s on install callback Token exchange not wrapped in retries / idempotent storage

9.3 Checklist Before Shipping to Production

  • [ ] HMAC verification tests for install, callback, and webhooks.
  • [ ] End-to-end install flow tested on at least one real dev store.
  • [ ] Tenant/shop mapping is unique on shopDomain and idempotent.
  • [ ] Correlation IDs show a full trace from install to first chat message.
  • [ ] Metrics and alerts configured for install failures and token errors.
  • [ ] AI tools enforce tenant scoping and capability checks on the server.

10. Results and Lessons Learned

After rolling out this end-to-end pattern, we observed several improvements:

  • Faster time-to-value: merchants could send their first meaningful, store-aware chat message immediately after install.
  • Reduced support load: fewer "install succeeded but nothing works" tickets.
  • Safer experimentation: with strong tenant isolation and observability, we could add new AI tools without compromising security.

What We Would Do Differently

  • Design the tenant mapping and AI capability model even earlier, before writing any UI code.
  • Invest in a dedicated "sandbox" tenant mode to test new AI tools safely.
  • Standardize a small internal library for HMAC, state handling, and correlation IDs from day one.

Recommendations If You Are Building This

  1. Treat tenant/shop mapping as a first-class subsystem, not an afterthought attached to OAuth.
  2. Centralize AI tooling on the server with strict tenant-scoped APIs; never expose Shopify tokens to the client or the model.
  3. Make installs observable with correlation IDs and metrics so you can diagnose failures quickly.
  4. Start with minimal scopes and expand only when a concrete feature demands it.
  5. Bootstrap chat immediately after install so merchants feel value the moment they authorize your app.

Tools We Used (At a Glance)

Integration & Infrastructure Shopify OAuth (Admin API authorization flow) Shopify Admin API (store-scoped data and actions for AI tools) Shopify app scopes and token model (offline/online as needed) App uninstall webhook for cleanup lifecycle Internal tenant mapping service + persistent datastore AI chat orchestration layer with server-side tool execution Structured logging and monitoring for install-to-chat traceability

If you are designing a Shopify + AI integration today, focusing on install → tenant map → context-rich chat as a single coherent flow will pay dividends in both user experience and operational sanity.