# E. Shopify App

The Envoi Shopify public app is the minimum-lift pathway: install the app on your store, and Envoi appears as a live rate at checkout, orders sync automatically into Envoi as they are placed, and fulfillment and delivery updates flow back into Shopify (and on to your customer) without any code on your side. Everything on this page is driven by the app plus per-retailer configuration that your Envoi contact manages. Shopify is the ecommerce platform behind many live Envoi retailers.

## Prerequisites

- A Shopify store on a plan that supports **third-party carrier-calculated shipping** (Shopify's CarrierService API) — required for live checkout rates. Order sync and fulfillment updates work regardless.
- An Envoi retailer account with at least one fulfillment location (pickup schedule) covering your shipping origin. New merchants can create the retailer during app onboarding (below).
- Your Envoi-serviceable delivery area understood: rates only appear for destinations whose postal code maps to one of your activated fulfillment locations.

## Architecture

```text
+----------------------+   OAuth install + webhooks    +---------------------+
|    Shopify store     | ----------------------------> |                     |
|                      |                               |      Envoi API      |
|  Checkout ------------- CarrierService rate callback |  (Shopify modules)  |
|                      | <---------------------------- |   rate + service    |
|                      |                               |        name         |
|  orders/create ---------- order webhook ------------> |  creates Envoi     |
|                      |                               |   order             |
|  Fulfillment <----------- fulfillment + tracking ---- |  on pickup /       |
|  + customer notify   |    + "delivered" event        |   delivery          |
+----------------------+                               +---------------------+
```

## Install flow

1. Install the Envoi app (from the Shopify App Store, or via the install link Envoi provides). The app runs a standard Shopify OAuth flow: you approve the requested scopes, and Envoi verifies the callback (state cookie + HMAC) before exchanging the code for an access token.
2. On install, the app automatically:
   - registers its **webhook subscriptions** (order creation, app uninstall, and the GDPR compliance topics), and
   - registers the **`envoi` carrier service** with Shopify so checkout can request rates.
3. You are redirected to onboarding:
   - **Existing Envoi retailer** — sign in and the store links to your retailer; you land in the dashboard with order sync enabled.
   - **New merchant** — a short signup form (company name, store address, pickup location) creates the retailer, the fulfillment schedule (weekday operation by default), and a default rate configuration, and notifies the Envoi team to complete setup.
4. There is also a **custom-app variant** (per-store API credentials instead of the public app listing) used for specific merchants — your Envoi contact will tell you if this applies; the behavior after install is the same.

Multiple stores can be linked to one retailer — each store domain is registered separately and carries its own access token, currency, and timezone.

## Checkout rates (CarrierService)

At checkout, Shopify calls Envoi's carrier-service callback with the cart and destination. Envoi answers with either a single rate or nothing (in which case the Envoi option simply does not appear):

- **Serviceability first.** The destination postal code must match one of your **activated fulfillment locations**; if it does not, no rate is returned and checkout falls back to your other shipping options.
- **Pricing is per-retailer configuration**, managed by your Envoi contact: a flat price (`price`), and optionally a **free tier** (`enableFreeTier` + `freeTierValue`) — when the cart's item total exceeds the threshold, the Envoi rate is returned at **$0**. Rates can be switched off entirely (`enableShipping: false`) while keeping order sync.
- **Service naming is dynamic.** The default service name is `24h Delivery by Envoi`, with a description you control. Two configurable variants override it automatically:
  - **After cutoff** — once the fulfillment location's cutoff (pickup time minus time-to-prepare, in the location's timezone) has passed, the after-cutoff name/description is shown instead (e.g. "Next-Day Delivery by Envoi").
  - **Non-operating day** — on days the location does not operate, the non-operating-day name/description is shown.
  - Descriptions support a **`{date}` placeholder**, replaced with the next available delivery date (e.g. "Delivered Tue, Jul 22") — strongly recommended so the promise at checkout always matches reality.
- **Inventory-aware rates (optional).** Retailers with a connected warehouse service can have Envoi suppress the rate when stock is insufficient to fulfill the cart.
- A **no-checkout mode** exists for retailers who want order sync without an Envoi option at checkout.

## Order sync

- Envoi subscribes to Shopify's **order creation webhook**. When an order is placed, Envoi verifies the webhook HMAC, matches the shipping address's postal code to one of your activated fulfillment locations, and creates the Envoi order — recipient, address (line 2 included), phone, email, and coordinates when Shopify provides them. The Shopify order name (e.g. `#1001`) becomes the Envoi order `number`, and a scan barcode is generated automatically (`E-<shopify order id>`) unless your account uses its own tracking-code prefix.
- **Duplicates are prevented** — each Shopify order ID creates at most one Envoi order, so webhook retries are safe.
- **Unserviceable orders are not dropped silently**: if no fulfillment location covers the destination, the order is skipped for delivery and flagged internally for review — but it does not error your store.
- **Created-unfulfilled vs. on-fulfillment**: by default orders sync at creation (unfulfilled) so same-day cutoffs are not lost waiting for warehouse action. Per-retailer configuration controls whether Envoi then drives the Shopify fulfillment itself (see below) — discuss with your Envoi contact if your operation must control fulfillment timing from Shopify's side instead.
- **Historical sync**: on connection, recent orders (a trailing window of days) can be imported so you do not lose in-flight orders during cutover.
- The app implements Shopify's **GDPR webhooks** — customer data requests, customer redaction (order data is obfuscated), and shop redaction (all shop orders obfuscated and the store record removed).

Note: order sync is webhook-driven from `orders/create` — it is not filtered by which shipping option the customer chose unless your configuration says otherwise. Confirm the intended behavior (all orders vs. Envoi-rate orders only) with your Envoi contact during setup.

## Fulfillment updates back to Shopify

For retailers with fulfillment sync enabled, Envoi pushes status back into Shopify automatically:

1. When the order enters delivery, Envoi creates a **Shopify fulfillment** against the order's fulfillment order, with tracking company `Envoi`, the Envoi tracking number, and the tracking URL (`https://app.yourenvoi.com/tracking/<code>`), and **notifies the customer** through Shopify's native notification.
2. On successful delivery, Envoi posts a **`delivered` fulfillment event**, so Shopify's order timeline and customer-facing tracking reflect the final state.
3. The Shopify order can be closed on completion.

Your customers therefore get Shopify-native emails with a working tracking link — no notification work on your side.

## Multi-store and multi-location mapping

- **Multiple stores → one retailer.** Each installed store is registered against your retailer independently; orders from every store flow into the same Envoi account, tagged with their source store.
- **Locations map via postal code.** Envoi's fulfillment locations (pickup schedules) each cover a service area; an incoming order (or checkout rate request) is matched to the location whose coverage includes the destination postal code. To ship from multiple stores/DCs, have your Envoi contact create one fulfillment location per origin with its own cutoff, pickup time, and days of operation.
- Store timezone and currency are captured at install and used for cutoff math and rate currency respectively.

## Coexisting with the API

The app does not lock you out of the REST API — the orders it creates are ordinary Envoi orders under your retailer:

- Look them up, update, or cancel them via [`GET/PUT/DELETE /public/orders`](../api/orders/get-update-cancel.md).
- Pull POD photos via [Shipment Tracking](../api/tracking/shipment-tracking.md).
- Subscribe to [Envoi webhooks](../webhooks/overview.md) in parallel with the Shopify fulfillment updates (e.g. to feed a CS tool).
- Create additional non-Shopify orders (wholesale, replacements) via [`POST /public/orders`](../api/orders/create.md) against the same account.

Start with the [Quickstart](../getting-started/quickstart.md) if you want an API key alongside the app.

## Testing and go-live

Test on a development store against Envoi staging before touching production:

1. Install the app on a dev store; verify the carrier service and webhooks registered (your Envoi contact can confirm from the Envoi side, where every rate request and sync is logged).
2. Place test checkouts: serviceable address (rate appears, correct price), free-tier cart (price $0), unserviceable postal code (no Envoi option), after cutoff (after-cutoff naming), non-operating day.
3. Place a test order and confirm it appears in the Envoi dashboard with correct address, phone, and order number.
4. Drive it through delivery with the [simulate endpoint](../api/tracking/simulate.md) and confirm the Shopify fulfillment, customer notification, and delivered event.

Go-live checklist:

1. App installed on the production store; carrier service visible in Shopify shipping settings.
2. Pricing configuration confirmed (price, free-tier threshold, currency) with your Envoi contact.
3. Service names/descriptions set for all three states — default, after-cutoff, non-operating day — with `{date}` in the descriptions.
4. All shipping origins created as activated fulfillment locations with correct cutoffs and operating days.
5. Fulfillment-sync behavior confirmed (Envoi-driven vs. merchant-driven) and customer notification content reviewed.
6. A real low-value order placed and delivered end to end on day one, watched by both sides.

## Limitations and gotchas

- **Rates fail silent by design.** In production, any rate-request problem (store not linked, retailer not configured, no covering location, inventory short) returns *no rates* rather than an error — the customer just doesn't see Envoi. If Envoi "disappears" from checkout, check configuration and serviceability first; Envoi logs every rate request for diagnosis.
- **One rate, flat price.** The carrier service returns a single configured price (or $0 free tier) — it does not weight- or distance-price at checkout. If you need computed rates, use the [validate endpoint](../api/service-areas/validate.md) via the direct API.
- **Free tier compares the item subtotal** (item price × quantity) against the threshold — before shipping and tax. Set `freeTierValue` accordingly.
- **Carrier-calculated shipping is a Shopify plan feature.** Without it, the CarrierService cannot serve live rates — order sync still works, and some merchants run that way deliberately.
- **Cutoff naming needs all fields set.** The after-cutoff/non-operating-day names only apply when both the name *and* description variant are configured — half-configured variants fall back to the default name, which can promise same-day after cutoff.
- **Address quality is inherited from Shopify.** No checkout-time address validation happens beyond postal-code serviceability; buzzer/unit data lives in address line 2 only if the customer typed it.
- **Uninstalling the app stops sync immediately** (Shopify revokes the token and fires the uninstall webhook). Reinstalling re-links the store, but orders placed while uninstalled must be back-filled — talk to your Envoi contact.
