{
  "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
}