Curated for You shopify API

The shopify API from Curated for You — 14 operation(s) for shopify.

OpenAPI Specification

curated-for-you-shopify-api-openapi.yml Raw ↑
openapi: 3.1.0
info:
  title: Curated for You Chronicle shopify API
  description: "\n# API Usage Guide\n\n## Overview\n\nThe Curated for You API provides endpoints to manage and retrieve\ninformation authentication, curations, companies, and attributes. This\nguide will help you understand how to authenticate and interact with\nthe API effectively. Your permissions are setup beforehand when you\njoin the organization, for additional permissions please contact your\ncustomer care representative.\n\n## Authentication\n\nThe Curated for You API uses OAuth 2.0 Bearer token\nauthentication. Follow these steps to authenticate your requests:\n\n```bash\ncurl -X POST 'https://api.curatedforyou.io/api/v1/user/login' \\\n  -H 'Content-Type: application/x-www-form-urlencoded' \\\n  --data-urlencode 'username=your_email@example.com' \\\n  --data-urlencode 'password=your_password'\n```\n\n### Use the Token\n\nAfter a successful login, you will receive a token. This token lasts\nfor 30 minutes(?). Include the received token in the Authorization header\nfor all subsequent requests\n\n```bash\ncurl -G 'https://api.curatedforyou.io/api/v2/endpoint' \\\n  -H 'Authorization: Bearer ENTER_AUTH_TOKEN_HERE' \\\n  -H 'accept: application/json'\n```\n\n## Get Curation Products Guide\n\n### 1. Get Company Information\n\nRetrieve the companies you have access to:\n\n```bash\ncurl -G 'https://api.curatedforyou.io/api/v2/companies' \\\n  -H 'Authorization: Bearer ENTER_AUTH_TOKEN_HERE' \\\n  -H 'accept: application/json' \\\n  -d 'projection=code_name' \\\n  -d 'projection=display_name'\n```\n\nIn this example, we use projection limit our response to only\n`display_name` and the `code_name`.\n\n### 2. Get Curations\n\n\nUse the found `company_id` to get the curations within that company:\n\n```bash\ncurl -G 'https://api.curatedforyou.io/api/v2/curations' \\\n  -H 'Authorization: Bearer ENTER_AUTH_TOKEN_HERE' \\\n  -H 'accept: application/json' \\\n  -d 'company_id=ENTER_COMPANY_ID_HERE' \\\n  -d 'sort_by=created_at:desc' \\\n  -d 'limit=50' \\\n  -d 'offset=0' \\\n  -d 'projection=curation_display_name' \\\n  -d 'projection=curation_group_number' \\\n  -d 'projection=curation_description' \\\n  -d 'projection=export_page_url_handle' \\\n  -d 'projection=company_product_ids'\n```\n\nThis example shows the different ways we can return information by\nspecifying different projections.\n\n### 3. Get Most Recent Snapshot\n\nAfter finding your specific curation we can query the snapshot that\nwas exported to determine what information we should be displaying.\n\nThe curation_group_number is the important id to use and the\ninformation on how and what was exported can be found in the\n`meta_data` field.\n\n```bash\ncurl -G 'https://api.curatedforyou.io/api/v2/curation_snapshots' \\\n  -H 'Authorization: Bearer ENTER_AUTH_TOKEN_HERE' \\\n  -H 'accept: application/json' \\\n  -d 'company_id=ENTER_COMPANY_ID_HERE' \\\n  -d 'curation_group_number=CURATION_GROUP_NUMBER_HERE' \\\n  -d 'sort_by=created_at:desc' \\\n  -d 'limit=50' \\\n  -d 'offset=0' \\\n  -d 'projection=products' \\\n  -d 'projection=meta_data' \\ # <<< where the last export time is found\n  -d 'projection=display_name' \\\n  -d 'projection=created_at'\n```\n"
  version: 2.0.1
  x-logo:
    url: https://app.curatedforyou.io/cfy_logo.png
tags:
- name: shopify
paths:
  /api/v2/shopify/install:
    get:
      tags:
      - shopify
      summary: Install
      description: 'Shopify OAuth install handler.


        Verifies HMAC, stores nonce in Redis keyed by nonce value (not shop),

        then redirects the merchant to Shopify''s OAuth consent screen.'
      operationId: install_api_v2_shopify_install_get
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema: {}
      x-codeSamples:
      - lang: Shell
        source: "curl --location\\\n --request GET 'https://api.curatedforyou.io/api/v2/shopify/install'\\\n--header 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2NzMwNDkxNzgsImV4cCI6MTcwNDU4NTE3OCwiYXVkIjoid3d3LmV4YW1w'\\\n--header 'Content-Type: application/json'\\\n"
        label: Curl
      - lang: Python
        source: "import requests\n\nurl = \"https://api.curatedforyou.io/api/v2/shopify/install\"\nheaders ={\n    \"Authorization\": \"Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2NzMwNDkxNzgsImV4cCI6MTcwNDU4NTE3OCwiYXVkIjoid3d3LmV4YW1w\",\n    \"Content-Type\": \"application/json\"\n}\nresponse = requests.request(\"GET\", url, headers=headers)\nprint(response.json())"
        label: Python3
  /api/v2/shopify/callback:
    get:
      tags:
      - shopify
      summary: Callback
      description: 'Shopify OAuth callback.


        Verifies HMAC + nonce, exchanges code for access token, stores token in

        GCP Secret Manager, upserts ShopifyStore DB row, redirects into Admin iframe.'
      operationId: callback_api_v2_shopify_callback_get
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema: {}
      x-codeSamples:
      - lang: Shell
        source: "curl --location\\\n --request GET 'https://api.curatedforyou.io/api/v2/shopify/callback'\\\n--header 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2NzMwNDkxNzgsImV4cCI6MTcwNDU4NTE3OCwiYXVkIjoid3d3LmV4YW1w'\\\n--header 'Content-Type: application/json'\\\n"
        label: Curl
      - lang: Python
        source: "import requests\n\nurl = \"https://api.curatedforyou.io/api/v2/shopify/callback\"\nheaders ={\n    \"Authorization\": \"Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2NzMwNDkxNzgsImV4cCI6MTcwNDU4NTE3OCwiYXVkIjoid3d3LmV4YW1w\",\n    \"Content-Type\": \"application/json\"\n}\nresponse = requests.request(\"GET\", url, headers=headers)\nprint(response.json())"
        label: Python3
  /api/v2/shopify/app:
    get:
      tags:
      - shopify
      summary: App Status Page
      description: 'Serve the Account Status page (embedded app home).


        No session token required for the page load — App Bridge v4 injects the token

        into subsequent fetch() calls automatically.'
      operationId: app_status_page_api_v2_shopify_app_get
      responses:
        '200':
          description: Successful Response
          content:
            text/html:
              schema:
                type: string
      x-codeSamples:
      - lang: Shell
        source: "curl --location\\\n --request GET 'https://api.curatedforyou.io/api/v2/shopify/app'\\\n--header 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2NzMwNDkxNzgsImV4cCI6MTcwNDU4NTE3OCwiYXVkIjoid3d3LmV4YW1w'\\\n--header 'Content-Type: application/json'\\\n"
        label: Curl
      - lang: Python
        source: "import requests\n\nurl = \"https://api.curatedforyou.io/api/v2/shopify/app\"\nheaders ={\n    \"Authorization\": \"Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2NzMwNDkxNzgsImV4cCI6MTcwNDU4NTE3OCwiYXVkIjoid3d3LmV4YW1w\",\n    \"Content-Type\": \"application/json\"\n}\nresponse = requests.request(\"GET\", url, headers=headers)\nprint(response.json())"
        label: Python3
  /api/v2/shopify/app/collections:
    get:
      tags:
      - shopify
      summary: App Collections Page
      description: Serve the Collections page of the embedded app.
      operationId: app_collections_page_api_v2_shopify_app_collections_get
      responses:
        '200':
          description: Successful Response
          content:
            text/html:
              schema:
                type: string
      x-codeSamples:
      - lang: Shell
        source: "curl --location\\\n --request GET 'https://api.curatedforyou.io/api/v2/shopify/app/collections'\\\n--header 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2NzMwNDkxNzgsImV4cCI6MTcwNDU4NTE3OCwiYXVkIjoid3d3LmV4YW1w'\\\n--header 'Content-Type: application/json'\\\n"
        label: Curl
      - lang: Python
        source: "import requests\n\nurl = \"https://api.curatedforyou.io/api/v2/shopify/app/collections\"\nheaders ={\n    \"Authorization\": \"Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2NzMwNDkxNzgsImV4cCI6MTcwNDU4NTE3OCwiYXVkIjoid3d3LmV4YW1w\",\n    \"Content-Type\": \"application/json\"\n}\nresponse = requests.request(\"GET\", url, headers=headers)\nprint(response.json())"
        label: Python3
  /api/v2/shopify/ensure-installed:
    post:
      tags:
      - shopify
      summary: Ensure Installed
      description: 'Exchange session token for offline access token on first load.


        Managed installation bypasses /install and /callback — Shopify handles

        consent itself and loads application_url directly in the iframe. This

        endpoint detects the missing store record and exchanges the session token

        (JWT) for a non-expiring offline access token via Shopify''s token exchange

        API.


        Idempotent: returns immediately if store record already exists.'
      operationId: ensure_installed_api_v2_shopify_ensure_installed_post
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
                title: Response Ensure Installed Api V2 Shopify Ensure Installed Post
      x-codeSamples:
      - lang: Shell
        source: "curl --location\\\n --request POST 'https://api.curatedforyou.io/api/v2/shopify/ensure-installed'\\\n--header 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2NzMwNDkxNzgsImV4cCI6MTcwNDU4NTE3OCwiYXVkIjoid3d3LmV4YW1w'\\\n--header 'Content-Type: application/json'\\\n"
        label: Curl
      - lang: Python
        source: "import requests\nimport json\n\nurl = \"https://api.curatedforyou.io/api/v2/shopify/ensure-installed\"\nheaders ={\n    \"Authorization\": \"Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2NzMwNDkxNzgsImV4cCI6MTcwNDU4NTE3OCwiYXVkIjoid3d3LmV4YW1w\",\n    \"Content-Type\": \"application/json\"\n}\nresponse = requests.request(\"POST\", url, headers=headers)\nprint(response.json())"
        label: Python3
  /api/v2/shopify/status:
    get:
      tags:
      - shopify
      summary: Get Store Status
      description: 'Return whether the store is linked to a CFY company account.


        Returns unlinked (not 404) when the store is brand-new — there is a brief window

        after OAuth where the session token is valid but the DB upsert hasn''t committed yet.'
      operationId: get_store_status_api_v2_shopify_status_get
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StoreStatusResponse'
      x-codeSamples:
      - lang: Shell
        source: "curl --location\\\n --request GET 'https://api.curatedforyou.io/api/v2/shopify/status'\\\n--header 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2NzMwNDkxNzgsImV4cCI6MTcwNDU4NTE3OCwiYXVkIjoid3d3LmV4YW1w'\\\n--header 'Content-Type: application/json'\\\n"
        label: Curl
      - lang: Python
        source: "import requests\n\nurl = \"https://api.curatedforyou.io/api/v2/shopify/status\"\nheaders ={\n    \"Authorization\": \"Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2NzMwNDkxNzgsImV4cCI6MTcwNDU4NTE3OCwiYXVkIjoid3d3LmV4YW1w\",\n    \"Content-Type\": \"application/json\"\n}\nresponse = requests.request(\"GET\", url, headers=headers)\nprint(response.json())"
        label: Python3
  /api/v2/shopify/all-collections:
    get:
      tags:
      - shopify
      summary: List All Collections
      description: 'Fetch all Shopify collections for this store via GraphQL.


        Works for any installer regardless of company_id — reviewers see real data.

        Paginates up to MAX_PAGES (500 collections).'
      operationId: list_all_collections_api_v2_shopify_all_collections_get
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/AllCollectionResponse'
                type: array
                title: Response List All Collections Api V2 Shopify All Collections Get
      x-codeSamples:
      - lang: Shell
        source: "curl --location\\\n --request GET 'https://api.curatedforyou.io/api/v2/shopify/all-collections'\\\n--header 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2NzMwNDkxNzgsImV4cCI6MTcwNDU4NTE3OCwiYXVkIjoid3d3LmV4YW1w'\\\n--header 'Content-Type: application/json'\\\n"
        label: Curl
      - lang: Python
        source: "import requests\n\nurl = \"https://api.curatedforyou.io/api/v2/shopify/all-collections\"\nheaders ={\n    \"Authorization\": \"Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2NzMwNDkxNzgsImV4cCI6MTcwNDU4NTE3OCwiYXVkIjoid3d3LmV4YW1w\",\n    \"Content-Type\": \"application/json\"\n}\nresponse = requests.request(\"GET\", url, headers=headers)\nprint(response.json())"
        label: Python3
  /api/v2/shopify/collections:
    get:
      tags:
      - shopify
      summary: List Collections
      description: 'Return CFY-managed collections for this store.


        Note: this endpoint is for the export pipeline, not the embedded UI.

        The embedded UI uses /all-collections (Tab 2) and /status (Tab 1).

        Returns empty list if the store is not yet linked to a CFY company.'
      operationId: list_collections_api_v2_shopify_collections_get
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                items:
                  $ref: '#/components/schemas/CollectionResponse'
                type: array
                title: Response List Collections Api V2 Shopify Collections Get
      x-codeSamples:
      - lang: Shell
        source: "curl --location\\\n --request GET 'https://api.curatedforyou.io/api/v2/shopify/collections'\\\n--header 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2NzMwNDkxNzgsImV4cCI6MTcwNDU4NTE3OCwiYXVkIjoid3d3LmV4YW1w'\\\n--header 'Content-Type: application/json'\\\n"
        label: Curl
      - lang: Python
        source: "import requests\n\nurl = \"https://api.curatedforyou.io/api/v2/shopify/collections\"\nheaders ={\n    \"Authorization\": \"Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2NzMwNDkxNzgsImV4cCI6MTcwNDU4NTE3OCwiYXVkIjoid3d3LmV4YW1w\",\n    \"Content-Type\": \"application/json\"\n}\nresponse = requests.request(\"GET\", url, headers=headers)\nprint(response.json())"
        label: Python3
  /api/v2/shopify/request-setup:
    post:
      tags:
      - shopify
      summary: Request Setup
      description: 'Log an account setup request and notify CFY staff via Slack.


        Idempotent per store per 24 hours — prevents flooding from repeated clicks.'
      operationId: request_setup_api_v2_shopify_request_setup_post
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
                title: Response Request Setup Api V2 Shopify Request Setup Post
      x-codeSamples:
      - lang: Shell
        source: "curl --location\\\n --request POST 'https://api.curatedforyou.io/api/v2/shopify/request-setup'\\\n--header 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2NzMwNDkxNzgsImV4cCI6MTcwNDU4NTE3OCwiYXVkIjoid3d3LmV4YW1w'\\\n--header 'Content-Type: application/json'\\\n"
        label: Curl
      - lang: Python
        source: "import requests\nimport json\n\nurl = \"https://api.curatedforyou.io/api/v2/shopify/request-setup\"\nheaders ={\n    \"Authorization\": \"Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2NzMwNDkxNzgsImV4cCI6MTcwNDU4NTE3OCwiYXVkIjoid3d3LmV4YW1w\",\n    \"Content-Type\": \"application/json\"\n}\nresponse = requests.request(\"POST\", url, headers=headers)\nprint(response.json())"
        label: Python3
  /api/v2/shopify/collections/{handle}/resync:
    post:
      tags:
      - shopify
      summary: Resync Collection
      description: 'Enqueue a CFY export for the given collection handle.


        TODO: integrate with the export Cloud Tasks queue once the export pipeline

        supports public-app merchants. Currently returns 200 with status=queued.'
      operationId: resync_collection_api_v2_shopify_collections__handle__resync_post
      parameters:
      - name: handle
        in: path
        required: true
        schema:
          type: string
          title: Handle
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
                title: Response Resync Collection Api V2 Shopify Collections  Handle  Resync Post
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
      x-codeSamples:
      - lang: Shell
        source: "curl --location\\\n --request POST 'https://api.curatedforyou.io/api/v2/shopify/collections/{handle}/resync'\\\n--header 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2NzMwNDkxNzgsImV4cCI6MTcwNDU4NTE3OCwiYXVkIjoid3d3LmV4YW1w'\\\n--header 'Content-Type: application/json'\\\n"
        label: Curl
      - lang: Python
        source: "import requests\nimport json\n\nurl = \"https://api.curatedforyou.io/api/v2/shopify/collections/{handle}/resync\"\nheaders ={\n    \"Authorization\": \"Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2NzMwNDkxNzgsImV4cCI6MTcwNDU4NTE3OCwiYXVkIjoid3d3LmV4YW1w\",\n    \"Content-Type\": \"application/json\"\n}\nresponse = requests.request(\"POST\", url, headers=headers)\nprint(response.json())"
        label: Python3
  /api/v2/shopify/collections/{handle}/request-analysis:
    post:
      tags:
      - shopify
      summary: Request Collection Analysis
      description: 'Request CFY analysis for an unmanaged collection.


        Creates a ShopifyManagedCollection row with status=''requested''.

        Idempotent — returns appropriate status on duplicate requests.

        Works for unlinked stores (no company_id required).'
      operationId: request_collection_analysis_api_v2_shopify_collections__handle__request_analysis_post
      parameters:
      - name: handle
        in: path
        required: true
        schema:
          type: string
          title: Handle
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AnalysisRequestBody'
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                type: object
                additionalProperties: true
                title: Response Request Collection Analysis Api V2 Shopify Collections  Handle  Request Analysis Post
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
      x-codeSamples:
      - lang: Shell
        source: "curl --location\\\n --request POST 'https://api.curatedforyou.io/api/v2/shopify/collections/{handle}/request-analysis'\\\n--header 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2NzMwNDkxNzgsImV4cCI6MTcwNDU4NTE3OCwiYXVkIjoid3d3LmV4YW1w'\\\n--header 'Content-Type: application/json'\\\n"
        label: Curl
      - lang: Python
        source: "import requests\nimport json\n\nurl = \"https://api.curatedforyou.io/api/v2/shopify/collections/{handle}/request-analysis\"\nheaders ={\n    \"Authorization\": \"Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2NzMwNDkxNzgsImV4cCI6MTcwNDU4NTE3OCwiYXVkIjoid3d3LmV4YW1w\",\n    \"Content-Type\": \"application/json\"\n}\nresponse = requests.request(\"POST\", url, headers=headers)\nprint(response.json())"
        label: Python3
  /api/v2/shopify/webhooks:
    post:
      tags:
      - shopify
      summary: Webhooks
      description: 'Handle all Shopify webhook topics at a single endpoint.


        All four GDPR + app/uninstalled topics are routed here.

        HMAC is verified before any payload parsing.

        Returns 200 for all valid requests, 401 for HMAC failures.'
      operationId: webhooks_api_v2_shopify_webhooks_post
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                additionalProperties: true
                type: object
                title: Response Webhooks Api V2 Shopify Webhooks Post
      x-codeSamples:
      - lang: Shell
        source: "curl --location\\\n --request POST 'https://api.curatedforyou.io/api/v2/shopify/webhooks'\\\n--header 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2NzMwNDkxNzgsImV4cCI6MTcwNDU4NTE3OCwiYXVkIjoid3d3LmV4YW1w'\\\n--header 'Content-Type: application/json'\\\n"
        label: Curl
      - lang: Python
        source: "import requests\nimport json\n\nurl = \"https://api.curatedforyou.io/api/v2/shopify/webhooks\"\nheaders ={\n    \"Authorization\": \"Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2NzMwNDkxNzgsImV4cCI6MTcwNDU4NTE3OCwiYXVkIjoid3d3LmV4YW1w\",\n    \"Content-Type\": \"application/json\"\n}\nresponse = requests.request(\"POST\", url, headers=headers)\nprint(response.json())"
        label: Python3
  /api/v2/shopify/cd/{app_key}/install:
    get:
      tags:
      - shopify
      summary: Install
      description: 'CD OAuth install handler.


        Mirrors the public-app `/install`: verifies HMAC against the CD app''s

        client_secret, stores a nonce keyed by value (not shop) to prevent

        two-tab races, and redirects the merchant to Shopify''s consent screen.'
      operationId: install_api_v2_shopify_cd__app_key__install_get
      parameters:
      - name: app_key
        in: path
        required: true
        schema:
          type: string
          title: App Key
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema: {}
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
      x-codeSamples:
      - lang: Shell
        source: "curl --location\\\n --request GET 'https://api.curatedforyou.io/api/v2/shopify/cd/{app_key}/install'\\\n--header 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2NzMwNDkxNzgsImV4cCI6MTcwNDU4NTE3OCwiYXVkIjoid3d3LmV4YW1w'\\\n--header 'Content-Type: application/json'\\\n"
        label: Curl
      - lang: Python
        source: "import requests\n\nurl = \"https://api.curatedforyou.io/api/v2/shopify/cd/{app_key}/install\"\nheaders ={\n    \"Authorization\": \"Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2NzMwNDkxNzgsImV4cCI6MTcwNDU4NTE3OCwiYXVkIjoid3d3LmV4YW1w\",\n    \"Content-Type\": \"application/json\"\n}\nresponse = requests.request(\"GET\", url, headers=headers)\nprint(response.json())"
        label: Python3
  /api/v2/shopify/cd/{app_key}/callback:
    get:
      tags:
      - shopify
      summary: Callback
      description: 'CD OAuth callback.


        Verifies HMAC + nonce, exchanges the auth code for a token, validates

        granted scopes, stores the token in Secret Manager at

        `shopify_{app_key}__{slug}_key`, and upserts a ShopifyStore row with

        `shopify_app_key={app_key}`.'
      operationId: callback_api_v2_shopify_cd__app_key__callback_get
      parameters:
      - name: app_key
        in: path
        required: true
        schema:
          type: string
          title: App Key
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema: {}
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
      x-codeSamples:
      - lang: Shell
        source: "curl --location\\\n --request GET 'https://api.curatedforyou.io/api/v2/shopify/cd/{app_key}/callback'\\\n--header 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2NzMwNDkxNzgsImV4cCI6MTcwNDU4NTE3OCwiYXVkIjoid3d3LmV4YW1w'\\\n--header 'Content-Type: application/json'\\\n"
        label: Curl
      - lang: Python
        source: "import requests\n\nurl = \"https://api.curatedforyou.io/api/v2/shopify/cd/{app_key}/callback\"\nheaders ={\n    \"Authorization\": \"Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE2NzMwNDkxNzgsImV4cCI6MTcwNDU4NTE3OCwiYXVkIjoid3d3LmV4YW1w\",\n    \"Content-Type\": \"application/json\"\n}\nresponse = requests.request(\"GET\", url, headers=headers)\nprint(response.json())"
        label: Python3
components:
  schemas:
    HTTPValidationError:
      properties:
        detail:
          items:
            $ref: '#/components/schemas/ValidationError'
          type: array
          title: Detail
      type: object
      title: HTTPValidationError
    ValidationError:
      properties:
        loc:
          items:
            anyOf:
            - type: string
            - type: integer
          type: array
          title: Location
        msg:
          type: string
          title: Message
        type:
          type: string
          title: Error Type
      type: object
      required:
      - loc
      - msg
      - type
      title: ValidationError
    AnalysisRequestBody:
      properties:
        title:
          type: string
          title: Title
      type: object
      required:
      - title
      title: AnalysisRequestBody
    AllCollectionResponse:
      properties:
        handle:
          type: string
          title: Handle
        title:
          type: string
          title: Title
        type:
          type: string
          title: Type
        product_count:
          anyOf:
          - type: integer
          - type: 'null'
          title: Product Count
        updated_at:
          anyOf:
          - type: string
            format: date-time
          - type: 'null'
          title: Updated At
        managed_status:
          type: string
          title: Managed Status
        admin_url:
          type: string
          title: Admin Url
      type: object
      required:
      - handle
      - title
      - type
      - product_count
      - updated_at
      - managed_status
      - admin_url
      title: AllCollectionResponse
      description: All Shopify collections — returned by GET /all-collections.
    CollectionResponse:
      properties:
        handle:
          type: string
          title: Handle
        title:
          type: string
          title: Title
        type:
          type: string
          title: Type
        product_count:
          anyOf:
          - type: integer
          - type: 'null'
          title: Product Count
        last_synced_at:
          anyOf:
          - type: string
            format: date-time
          - type: 'null'
          title: Last Synced At
        status:
          type: string
          title: Status
      type: object
      required:
      - handle
      - title
      - type
      - product_count
      - last_synced_at
      - status
      title: CollectionResponse
      description: CFY-managed collections — returned by GET /collections.
    StoreStatusResponse:
      properties:
        linked:
          type: boolean
          title: Linked
        shop_domain:
          type: string
          title: Shop Domain
      type: object
      required:
      - linked
      - shop_domain
      title: StoreStatusResponse
      description: Store link status — returned by GET /status.