Shopify — integration guide
DPP Agent connects to Shopify via the Admin REST API with an OAuth-installable app. One pass populates a DPP per variant SKU, with the variant's barcode acting as the GTIN.
What the adapter does, in one sentence
DPP Agent paginates /admin/api/<version>/products.json, splits each
Product into one DPP row per variant whose barcode is a valid GTIN
(8–14 digits), and ingests through the conflict-aware pipeline.
Read-only, no Shopify-side changes, no write-back.
Authentication
OAuth Admin app install — the standard Shopify app flow:
- Operator clicks Connect Shopify in DPP Agent
- Redirect to
<shop>.myshopify.com/admin/oauth/authorizewith the requested scopes - Merchant approves
- Shopify redirects back to our OAuth callback at
/api/integrations/shopify/oauth-callbackwith acode - We exchange the code for a permanent access token, encrypt it, store it
All subsequent calls use X-Shopify-Access-Token: <token>.
Credential handling
Same envelope as the other adapters: AES-256-GCM at rest, redacted in the UI, decrypted in-memory only, never logged. Uninstalling the DPP Agent app from the Shopify admin revokes the token immediately.
Scope limitation
1. Single OAuth scope: read_products
The app installs with only read_products. No customers, no
orders, no draft orders, no inventory levels, no app billing data —
nothing beyond the product catalogue.
If your store enables the Customer Data protected scopes, our install flow does not request them and Shopify will not grant them.
2. Status filter
settings.product_status = "active" (default) restricts the fetch to
live products. Drafts and archived items are not returned.
3. Barcode-as-GTIN gate
Variants without a valid 8–14 digit barcode are dropped at the mapper before they reach the ingest pipeline. This means:
- Apparel SKUs with no EAN never produce a DPP row
- Products that use SKU instead of barcode are not mapped
- You can backfill barcodes in Shopify and re-sync
4. Network IP allowlist
Shopify does not expose a firewall, but Vercel-side egress IPs are published if your monitoring infrastructure wants to verify caller origin.
Settings reference
| Setting | Required | Purpose |
|---|---|---|
shop_domain |
yes | <your-shop>.myshopify.com |
api_version |
no | Default 2025-07 |
product_status |
no | Default active. Can be draft or archived for one-off pulls. |
The OAuth access token is stored in the encrypted credential vault and is not editable from the UI — to rotate, uninstall + reinstall the app from the Shopify admin.
Sync semantics
Conflict policy is the platform default:
- Draft DPPs are overwritten
- Active DPPs spawn
dpp_revisions(status=pending)for human review - Withdrawn / archived / recalled DPPs are skipped
Deletion: if a product disappears from Shopify, the corresponding DPP
remains in dpp_runtime. Same EN 18219 / EN 18223 retention reasoning
as the other adapters.
Onboarding sequence
- Operator clicks Connect Shopify, picks the store domain
- OAuth consent → approve
read_products - Callback exchanges code for token, stores it, redirects back
- Adapter runs a connection test against
/admin/api/<v>/shop.jsonand reports the store name - First sync,
sync_max_pages = 2-3, validates the product → DPP mapping - Full sync once mapping is locked
- Cron picks up the schedule (6h default)
Compliance hooks
| ESPR field | Shopify path |
|---|---|
espr.pid.gtin |
variants[].barcode (must be 8–14 digits) |
espr.pid.product_name |
title |
espr.pid.product_description |
body_html (HTML stripped, capped at 600 chars) |
espr.pid.color |
variants[].option1 |
espr.pid.size |
variants[].option2 |
tex.product.brand_category |
product_type |
tex.pid.model_number |
variants[].sku |
tex.product.hero_image_url |
images[0].src |
Shopify doesn't structure supplier or supply-chain data — for AGEC traceability connect Delogue or TrusTrace alongside.
Troubleshooting
- OAuth callback returns "invalid scope" — your custom Shopify app
was configured without
read_products; reinstall after fixing scope - HTTP 401 on sync — token revoked (app was uninstalled from Shopify admin); reinstall via DPP Agent → Integrations
- HTTP 429 with
Retry-After— Shopify rate limit; the adapter honours the header automatically. Repeated 429 means the store is under heavy load; lowerpage_sizeor schedule sync off-peak - Empty
products— all products are draft/archived butproduct_statusis set toactive - GTIN missing on many rows — variants don't have barcodes filled. Bulk-update via Shopify CSV before relaunching the sync
Open a ticket at support@dppagent.com with the integration ID + log.