# Shopify Integration

> E-commerce accounting automation with Shopify.

- **Category:** Integrations
- HTML version: https://www.ondayzero.com/docs/guides/shopify
- All guides: https://www.ondayzero.com/docs/guides/index.md
- Endpoint catalog: https://www.ondayzero.com/docs/reference/index.md

---

# Shopify Integration

A comprehensive accounting-first integration for Shopify stores, providing revenue recognition, payout reconciliation, fee tracking, and financial reporting.

> The `/api/v1/shopify/*` endpoints below work with an API token but are not part of the public API Reference — they back DayZero's own Integrations page and may change. Booked activity lands in the public [Journal Entries](/docs/guides/journal-entries), [Inventory](/docs/guides/inventory) and [Reports](/docs/guides/reports) endpoints, which is where integrations should read from.

## What Gets Synced

| Data | Description |
|------|-------------|
| Orders | Pulled into the Shopify subledger; summarised into sales-recognition journal entries |
| Payouts | Settlement journal entries that match the bank deposit 1:1 |
| Products & inventory | Synced into the product catalog for inventory and COGS tracking |
| Customers | Customer records and balances |
| Refunds | Netted into the daily / payout summaries |
| Gift cards | Liability tracking |
| Disputes | Reserve management |

## Connect Shopify

### Option 1: OAuth (Recommended)

From the Integrations page in DayZero, enter your Shopify shop domain and click **Connect Shopify**. You'll be redirected to Shopify to authorize access.

Or via API:

```bash
curl "https://api.ondayzero.com/api/v1/shopify/install-url?shop=your-store" \
  -H "Authorization: Bearer dz_your_token_here" \
  -H "x-business-id: YOUR_BUSINESS_ID"
```

The response includes an `install_url`. Redirect the merchant to that URL to begin the OAuth flow. After authorization, DayZero registers webhooks and queues the initial sync. (Merchants who installed from the Shopify App Store before creating a DayZero account are attached with `POST /api/v1/shopify/finalize-pending-install?shop=your-store`.)

### Option 2: Manual Access Token

If you have a Shopify custom app access token:

```bash
curl -X POST "https://api.ondayzero.com/api/v1/shopify/setup" \
  -H "Authorization: Bearer dz_your_token_here" \
  -H "x-business-id: YOUR_BUSINESS_ID" \
  -H "Content-Type: application/json" \
  -d '{"shop": "your-store.myshopify.com", "access_token": "shpat_..."}'
```

## Automatic Accounting

DayZero never posts one journal entry per order — the general ledger only sees summaries. Orders, refunds, products and customers are pulled on a schedule into a **subledger**; webhooks are used only for payouts, disputes and app lifecycle. Two posting modes are available (`GET`/`PATCH /api/v1/shopify/subledger/posting-config`):

| `posting_mode` | Journal entries created |
|----------------|-------------------------|
| `per_payout` (default) | One entry per Shopify payout. Debits: the net deposit (to the bank ledger, or to the *Shopify Merchant Account* clearing ledger when bank matching is on), processing fees, refunds, discounts and gift-card redemptions. Credits: product sales, shipping, tips, duties and sales tax payable (one ledger per jurisdiction). COGS / inventory lines are added when inventory is tracked. The entry total equals the bank deposit, so bank matching is exact. |
| `daily_summary` | One sales-recognition entry per business-local day (Debit *Shopify Merchant Account* clearing / Credit revenue, tax payable, etc., plus COGS), and a separate settlement entry when each payout lands that moves cash from Clearing to Bank net of fees. Accrual-friendly. |

Posting is idempotent per day / per payout, so re-syncing never double-books. Other posting-config fields: `posting_start_date` (ignore history before this date; `clear_posting_start_date: true` removes it), `bank_match_enabled` (auto-match payouts to bank transactions) and `catalog_sync_hour` / `catalog_sync_timezone` for the nightly product sync.

## Payout Reconciliation

Shopify payouts are matched against bank deposits for reconciliation. Each payout includes a breakdown of orders, refunds, fees, and adjustments — `GET /api/v1/shopify/subledger/payouts` lists them (filter by `status`, `start_date`, `end_date`, `posted`), and `GET /api/v1/shopify/subledger/payouts/{payout_id}` shows the detail.

## Browse the Subledger

Everything DayZero pulled from Shopify is queryable, which is useful for tying a journal entry back to the orders behind it:

| Endpoint | Returns |
|----------|---------|
| `GET /api/v1/shopify/subledger/summary` | Record counts (orders, payouts, refunds, posted vs. unposted summaries), the clearing-account balance and the store's posting settings |
| `GET /api/v1/shopify/subledger/orders` | Orders; filter by `journal_entry_id`, `financial_status`, date range |
| `GET /api/v1/shopify/subledger/orders/{order_id}/line-items` | Line items for one order |
| `GET /api/v1/shopify/subledger/daily` | Per-day sales summaries |
| `GET /api/v1/shopify/subledger/customers` | Synced customers (`search` supported) |
| `GET /api/v1/shopify/subledger/backlink/{journal_entry_id}` | The Shopify records that produced a journal entry |

These list endpoints use `limit`/`offset` paging rather than cursors.

## Sync Status

```bash
curl "https://api.ondayzero.com/api/v1/shopify/status" \
  -H "Authorization: Bearer dz_your_token_here" \
  -H "x-business-id: YOUR_BUSINESS_ID"
```

Returns `connected`, `healthy`, `shop_domain`, `orders_synced`, `products_synced`, `last_sync`, and the current bootstrap progress (`sync_state`, `sync_stage`, `sync_started_at`, `sync_completed_at`, `sync_error`). `GET /api/v1/shopify/stores` lists the connected stores.

## Manual Import

To re-import products or pull all orders on demand:

```bash
# Import products
curl -X PUT "https://api.ondayzero.com/api/v1/shopify/import" \
  -H "Authorization: Bearer dz_your_token_here" \
  -H "x-business-id: YOUR_BUSINESS_ID"

# Import orders
curl -X POST "https://api.ondayzero.com/api/v1/shopify/import-orders" \
  -H "Authorization: Bearer dz_your_token_here" \
  -H "x-business-id: YOUR_BUSINESS_ID"
```

`POST /api/v1/shopify/bootstrap` queues the full end-to-end catch-up (products, inventory, orders, payouts, balance transactions and summary posting). If payout or refund counts look wrong — typically because `posting_start_date` filtered out history on the original sync — `POST /api/v1/shopify/resync` wipes the subledger (journal entries and ledgers are untouched) and re-pulls the entire history. `POST /api/v1/shopify/subledger/backfill` with `{"limit": 200}` (default 50) queues posting for paid payouts and daily summaries that were pulled but never booked — `GET /api/v1/shopify/subledger/summary` shows how many are `payouts_unposted` / `daily_summaries_unposted`.

## Disconnect

Preview the impact first with `GET /api/v1/shopify/disconnect/preview`, then choose how much to remove with `purge_mode`:

```bash
curl -X DELETE "https://api.ondayzero.com/api/v1/shopify/disconnect?purge_mode=connection_only" \
  -H "Authorization: Bearer dz_your_token_here" \
  -H "x-business-id: YOUR_BUSINESS_ID"
```

| `purge_mode` | Effect |
|--------------|--------|
| `connection_only` (default) | Revokes the token, removes webhooks and clears credentials. Subledger and journal entries stay. |
| `subledger` | Also deletes every synced Shopify record (orders, payouts, customers…). Journal entries stay intact. |
| `full` | Subledger purge plus a reversing journal entry for every Shopify-sourced entry, and removal of any Shopify-managed ledger left empty. Requires `confirm=WIPE`; pass `cutoff_date=YYYY-MM-DD` to leave entries in closed periods alone. |
