{ "opencollection": "1.0.0", "info": { "name": "Curated for You Chronicle shopify API", "version": "2.0.1" }, "items": [ { "info": { "name": "shopify", "type": "folder" }, "items": [ { "info": { "name": "Install", "type": "http" }, "http": { "method": "GET", "url": "{{baseUrl}}/api/v2/shopify/install" }, "docs": "Shopify OAuth install handler.\n\nVerifies HMAC, stores nonce in Redis keyed by nonce value (not shop),\nthen redirects the merchant to Shopify's OAuth consent screen." }, { "info": { "name": "Callback", "type": "http" }, "http": { "method": "GET", "url": "{{baseUrl}}/api/v2/shopify/callback" }, "docs": "Shopify OAuth callback.\n\nVerifies HMAC + nonce, exchanges code for access token, stores token in\nGCP Secret Manager, upserts ShopifyStore DB row, redirects into Admin iframe." }, { "info": { "name": "App Status Page", "type": "http" }, "http": { "method": "GET", "url": "{{baseUrl}}/api/v2/shopify/app" }, "docs": "Serve the Account Status page (embedded app home).\n\nNo session token required for the page load — App Bridge v4 injects the token\ninto subsequent fetch() calls automatically." }, { "info": { "name": "App Collections Page", "type": "http" }, "http": { "method": "GET", "url": "{{baseUrl}}/api/v2/shopify/app/collections" }, "docs": "Serve the Collections page of the embedded app." }, { "info": { "name": "Ensure Installed", "type": "http" }, "http": { "method": "POST", "url": "{{baseUrl}}/api/v2/shopify/ensure-installed" }, "docs": "Exchange session token for offline access token on first load.\n\nManaged installation bypasses /install and /callback — Shopify handles\nconsent itself and loads application_url directly in the iframe. This\nendpoint detects the missing store record and exchanges the session token\n(JWT) for a non-expiring offline access token via Shopify's token exchange\nAPI.\n\nIdempotent: returns immediately if store record already exists." }, { "info": { "name": "Get Store Status", "type": "http" }, "http": { "method": "GET", "url": "{{baseUrl}}/api/v2/shopify/status" }, "docs": "Return whether the store is linked to a CFY company account.\n\nReturns unlinked (not 404) when the store is brand-new — there is a brief window\nafter OAuth where the session token is valid but the DB upsert hasn't committed yet." }, { "info": { "name": "List All Collections", "type": "http" }, "http": { "method": "GET", "url": "{{baseUrl}}/api/v2/shopify/all-collections" }, "docs": "Fetch all Shopify collections for this store via GraphQL.\n\nWorks for any installer regardless of company_id — reviewers see real data.\nPaginates up to MAX_PAGES (500 collections)." }, { "info": { "name": "List Collections", "type": "http" }, "http": { "method": "GET", "url": "{{baseUrl}}/api/v2/shopify/collections" }, "docs": "Return CFY-managed collections for this store.\n\nNote: this endpoint is for the export pipeline, not the embedded UI.\nThe embedded UI uses /all-collections (Tab 2) and /status (Tab 1).\nReturns empty list if the store is not yet linked to a CFY company." }, { "info": { "name": "Request Setup", "type": "http" }, "http": { "method": "POST", "url": "{{baseUrl}}/api/v2/shopify/request-setup" }, "docs": "Log an account setup request and notify CFY staff via Slack.\n\nIdempotent per store per 24 hours — prevents flooding from repeated clicks." }, { "info": { "name": "Resync Collection", "type": "http" }, "http": { "method": "POST", "url": "{{baseUrl}}/api/v2/shopify/collections/:handle/resync", "params": [ { "name": "handle", "value": "", "type": "path" } ] }, "docs": "Enqueue a CFY export for the given collection handle.\n\nTODO: integrate with the export Cloud Tasks queue once the export pipeline\nsupports public-app merchants. Currently returns 200 with status=queued." }, { "info": { "name": "Request Collection Analysis", "type": "http" }, "http": { "method": "POST", "url": "{{baseUrl}}/api/v2/shopify/collections/:handle/request-analysis", "params": [ { "name": "handle", "value": "", "type": "path" } ], "body": { "type": "json", "data": "{}" } }, "docs": "Request CFY analysis for an unmanaged collection.\n\nCreates a ShopifyManagedCollection row with status='requested'.\nIdempotent — returns appropriate status on duplicate requests.\nWorks for unlinked stores (no company_id required)." }, { "info": { "name": "Webhooks", "type": "http" }, "http": { "method": "POST", "url": "{{baseUrl}}/api/v2/shopify/webhooks" }, "docs": "Handle all Shopify webhook topics at a single endpoint.\n\nAll four GDPR + app/uninstalled topics are routed here.\nHMAC is verified before any payload parsing.\nReturns 200 for all valid requests, 401 for HMAC failures." }, { "info": { "name": "Install", "type": "http" }, "http": { "method": "GET", "url": "{{baseUrl}}/api/v2/shopify/cd/:app_key/install", "params": [ { "name": "app_key", "value": "", "type": "path" } ] }, "docs": "CD OAuth install handler.\n\nMirrors the public-app `/install`: verifies HMAC against the CD app's\nclient_secret, stores a nonce keyed by value (not shop) to prevent\ntwo-tab races, and redirects the merchant to Shopify's consent screen." }, { "info": { "name": "Callback", "type": "http" }, "http": { "method": "GET", "url": "{{baseUrl}}/api/v2/shopify/cd/:app_key/callback", "params": [ { "name": "app_key", "value": "", "type": "path" } ] }, "docs": "CD OAuth callback.\n\nVerifies HMAC + nonce, exchanges the auth code for a token, validates\ngranted scopes, stores the token in Secret Manager at\n`shopify_{app_key}__{slug}_key`, and upserts a ShopifyStore row with\n`shopify_app_key={app_key}`." } ] } ], "bundled": true }