> ## Documentation Index
> Fetch the complete documentation index at: https://docs.fetchski-attract.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Find visitors by an identifier

> Send **exactly one** lookup parameter. Returns every matching record, most recently seen first (at most 20) — a `_gcl_au` can in rare cases belong to several visitor IDs (for example when one of the two cookies was cleared and the other was not).

`token` opens an `fk_at` attribution token — the value this site adds to links to its Linked domains (a booking site) — and returns nothing once it has expired (90 days) or if it was forged. Click IDs match the first or last touch. `emailSha256` / `phoneSha256` find the visitors whose conversion events carried that hash (see `UserDataHashes` for the normalization).

**Access:** an API key with `attribution:read` — or its group master `ads:all`, or `admin:all` — or a signed-in user holding the `attribution:view` permission. Anonymous callers get 404.

**Rate limit:** the key’s own reads-per-minute limit, set when the key is created (default 600); over it, 429 with `Retry-After`.



## OpenAPI

````yaml /api-reference/openapi.yaml get /visitor-attribution
openapi: 3.1.0
info:
  title: Fetchski Attract CMS API
  description: >-
    Auto-generated OpenAPI spec for this Fetchski site instance. Authenticate
    machine-to-machine requests with an API key by sending the header
    `Authorization: ApiKey <token>`. Keys are issued in the CMS (API Keys) and
    carry scopes that limit them to specific actions. Each key also has its own
    security policy, chosen when it is created: a signing mode (token only,
    signed writes, or every request signed with the key’s signing secret via
    `X-Fetchski-Timestamp` + `X-Fetchski-Signature`), an optional network
    allowlist, and reads/writes-per-minute limits (429 with `Retry-After` over
    them). Some routes require a signature from every key — see each route.
  version: 1.0.0
servers:
  - url: https://{host}/api/v1
    description: 'Your site: set `host` to its host name.'
    variables:
      host:
        default: acme.example
security: []
tags:
  - name: Authentication
    description: >-
      Sign in as a person to get a bearer token or a session cookie.
      Integrations use an API key instead: send `Authorization: ApiKey <token>`.
  - name: SDK Packages
    description: >-
      The site’s own npm registry and Composer repository for the Attract SDKs,
      so an app installs them with one of the site’s API keys. Point npm, pnpm,
      Bun or Composer at them; the SDKs never call them.
  - name: Pages
    description: 'The page tree: each page’s content, place and URL.'
  - name: Global Content
    description: Site-wide content and settings every page shares.
  - name: Redirects
    description: Old URLs and where they now send visitors.
  - name: Internationalization
    description: The site’s locales.
  - name: Languages
    description: >-
      The site’s languages: the main language’s name and every further
      language’s name, code and Public switch. A save applies at once: a new
      language gets its home page and its own addresses (/fr/…), and its pages
      are then translated with each type’s localize routes.
  - name: Page Templates
    description: >-
      The site’s whole-page starting arrangements. They are not pages of the
      site (the Pages list leaves them out for API keys): read one, then make a
      page from it with Pages → create and `_copyingId` (`copyingId` in the
      SDKs).
  - name: Blog Posts
    description: The blog’s posts, and importing them from Markdown or MDX.
  - name: Blog Categories
    description: The categories posts are filed under.
  - name: Authors
    description: The people posts are credited to.
  - name: Team Members
    description: The people the Team section shows.
  - name: Testimonials
    description: Quotes from customers.
  - name: FAQ Items
    description: Questions and their answers.
  - name: FAQ Categories
    description: The groups questions are sorted into.
  - name: Google Reviews
    description: Reviews from the business’s Google profile, synced on a schedule.
  - name: Instagram Posts
    description: Posts from the connected Instagram account, synced on a schedule.
  - name: Service Areas
    description: The places the business serves, for the service map.
  - name: Cookies
    description: The cookies the consent banner declares.
  - name: Media
    description: The Images and Files libraries, and their tags.
  - name: Videos
    description: The Videos library.
  - name: Image Folders
    description: The folders that organize the Images library.
  - name: File Folders
    description: The folders that organize the Files library.
  - name: Video Folders
    description: The folders that organize the Videos library.
  - name: Photo Albums
    description: Hand-picked, ordered sets of photos for galleries.
  - name: Photo Feeds
    description: >-
      Feeds that collect photos from a connected photo source into the Images
      library on a schedule.
  - name: Attachments
    description: Uploading files and cropping images.
  - name: Fetchski Media
    description: >-
      Read-only discovery over the Images and Videos libraries: find what to
      reference in an MDX blog import, or which file to use, with its URLs.
  - name: Forms
    description: The site’s forms and their fields.
  - name: Form Submissions
    description: >-
      What visitors sent through the site’s forms. Submissions hold personal
      details: give a key `forms:read` only where it is needed.
  - name: Conversion Events
    description: >-
      The conversion event log — one record per conversion, with its attribution
      and consent snapshot and its delivery to each ad platform. Read it
      incrementally, post facts from other systems (a reservation purchase, a
      lead outcome), and adjust them (refunds, cancellations). One event ID
      travels everywhere as the platforms’ de-duplication key.
  - name: Visitor Attribution
    description: >-
      Per-visitor attribution on this site: first touch, last ad or campaign
      touch, and the ad platforms’ browser IDs. The join other systems use to
      attach a purchase to the marketing-site journey that led to it.
  - name: Ads Metrics
    description: >-
      Aggregates for dashboards: ad-click landings and conversions, by day,
      platform, campaign and more.
  - name: Conversions
    description: >-
      Conversion definitions — the visitor actions (a lead, a call click, a
      booking, a purchase) this site reports to the ad platforms, each with a
      row per platform. Managed by administrators in the CMS.
  - name: Webhooks
    description: >-
      Push notifications of form submissions and conversion-event changes to the
      site’s Webhook Destinations — signed with each destination’s own secret,
      retried for a day, at-least-once. Set destinations up in the CMS (Webhook
      Destinations, for people granted “Manage webhook destinations”): URL,
      events, optional filters and custom headers, and the signing secret shown
      once.
  - name: Request Log
    description: >-
      The page views the site captured (Connections → Tracking → Request log),
      and the query parameters on them: campaign tags and ad click IDs.
paths:
  /visitor-attribution:
    get:
      tags:
        - Visitor Attribution
      summary: Find visitors by an identifier
      description: >-
        Send **exactly one** lookup parameter. Returns every matching record,
        most recently seen first (at most 20) — a `_gcl_au` can in rare cases
        belong to several visitor IDs (for example when one of the two cookies
        was cleared and the other was not).


        `token` opens an `fk_at` attribution token — the value this site adds to
        links to its Linked domains (a booking site) — and returns nothing once
        it has expired (90 days) or if it was forged. Click IDs match the first
        or last touch. `emailSha256` / `phoneSha256` find the visitors whose
        conversion events carried that hash (see `UserDataHashes` for the
        normalization).


        **Access:** an API key with `attribution:read` — or its group master
        `ads:all`, or `admin:all` — or a signed-in user holding the
        `attribution:view` permission. Anonymous callers get 404.


        **Rate limit:** the key’s own reads-per-minute limit, set when the key
        is created (default 600); over it, 429 with `Retry-After`.
      operationId: findVisitorAttributions
      parameters:
        - name: token
          in: query
          required: false
          schema:
            type: string
            maxLength: 512
          description: An `fk_at` attribution token.
        - name: gclAu
          in: query
          required: false
          schema:
            type: string
            maxLength: 512
          description: The `_gcl_au` cookie value (Google’s cross-site browser ID).
        - name: fbp
          in: query
          required: false
          schema:
            type: string
            maxLength: 512
          description: The `_fbp` cookie value.
        - name: fbc
          in: query
          required: false
          schema:
            type: string
            maxLength: 512
          description: The `_fbc` cookie value.
        - name: ttp
          in: query
          required: false
          schema:
            type: string
            maxLength: 512
          description: The `_ttp` cookie value.
        - name: gclid
          in: query
          required: false
          schema:
            type: string
            maxLength: 512
          description: A Google click ID.
        - name: gbraid
          in: query
          required: false
          schema:
            type: string
            maxLength: 512
          description: A Google iOS app-campaign click ID.
        - name: wbraid
          in: query
          required: false
          schema:
            type: string
            maxLength: 512
          description: A Google iOS web-campaign click ID.
        - name: fbclid
          in: query
          required: false
          schema:
            type: string
            maxLength: 512
          description: A Meta click ID.
        - name: ttclid
          in: query
          required: false
          schema:
            type: string
            maxLength: 512
          description: A TikTok click ID.
        - name: emailSha256
          in: query
          required: false
          schema:
            type: string
            pattern: ^[0-9a-f]{64}$
          description: SHA-256 of the trimmed, lower-cased email.
        - name: phoneSha256
          in: query
          required: false
          schema:
            type: string
            pattern: ^[0-9a-f]{64}$
          description: SHA-256 of the E.164 phone number with its "+".
      responses:
        '200':
          description: The matching records (possibly none).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FindVisitorAttributionsResponse'
              example:
                matchedBy: gclAu
                attributions:
                  - visitorId: 0b8e6a4c-8d2f-4b6e-9a1c-3f5d7e9b1a2c
                    firstTouch:
                      at: '2026-09-20T09:12:00.000Z'
                      path: /
                      landingUrl: https://example.com/?gclid=Cj0KCQjw&utm_source=google
                      referrerOrigin: https://www.google.com
                      adPlatforms:
                        - google
                      campaign:
                        utm_source: google
                        utm_campaign: spring-sale
                      clickIds:
                        gclid: Cj0KCQjw
                    browserIds:
                      _gcl_au: 1.1.1234567890.1790000000
                    marketing: true
                    firstSeenAt: '2026-09-20T09:12:00.000Z'
                    lastSeenAt: '2026-09-21T15:04:05.000Z'
                    expiresAt: '2027-03-20T15:04:05.000Z'
        '400':
          $ref: '#/components/responses/DataApiInvalid'
        '401':
          $ref: '#/components/responses/DataApiUnauthorized'
        '403':
          $ref: '#/components/responses/DataApiForbidden'
        '404':
          $ref: '#/components/responses/DataApiNotFound'
        '429':
          $ref: '#/components/responses/DataApiRateLimited'
      security:
        - ApiKeyAuth:
            - attribution:read
      x-codeSamples:
        - lang: typescript
          label: TypeScript
          source: >-
            import { createSiteClient } from '@fetchski/attract-cms-sdk';


            const site = createSiteClient();

            const findVisitorAttributions = await
            site.visitorAttribution().findVisitorAttributions();
        - lang: php
          label: Laravel
          source: >-
            use Fetchski\AttractCmsLaravel\Facades\Attract;


            $findVisitorAttributions =
            Attract::visitorAttribution()->findVisitorAttributions();
components:
  schemas:
    FindVisitorAttributionsResponse:
      type: object
      required:
        - matchedBy
        - attributions
      properties:
        matchedBy:
          type: string
          description: The lookup parameter used.
        attributions:
          type: array
          maxItems: 20
          items:
            $ref: '#/components/schemas/VisitorAttribution'
    VisitorAttribution:
      type: object
      description: >-
        One visitor’s attribution record. Kept for the site’s retention setting
        after the visitor’s last page view.
      required:
        - visitorId
        - firstTouch
        - marketing
        - firstSeenAt
        - lastSeenAt
        - expiresAt
      properties:
        visitorId:
          type: string
        firstTouch:
          $ref: '#/components/schemas/AttributionTouch'
        lastTouch:
          $ref: '#/components/schemas/AttributionTouch'
          description: Changes only on a visit with an ad click or campaign parameters.
        browserIds:
          $ref: '#/components/schemas/BrowserIds'
        marketing:
          type: boolean
          description: Whether the visitor allowed Marketing on their latest page view.
        firstSeenAt:
          type: string
          format: date-time
        lastSeenAt:
          type: string
          format: date-time
        expiresAt:
          type: string
          format: date-time
    DataApiError:
      type: object
      description: >-
        Every error. `name` → HTTP status: `invalid` 400,
        `invalid`/`unauthorized` 401, `forbidden` 403, `notfound` 404,
        `conflict` 409, `tooManyRequests` 429.
      required:
        - name
        - message
      properties:
        name:
          type: string
          enum:
            - invalid
            - unauthorized
            - forbidden
            - notfound
            - conflict
            - tooManyRequests
            - error
        message:
          type: string
        data:
          type: object
          properties:
            errors:
              type: array
              items:
                $ref: '#/components/schemas/DataApiFieldError'
              description: Present on 400.
            retryAfter:
              type: integer
              description: Present on 429.
            eventId:
              type: string
              description: Present on 409.
    AttributionTouch:
      type: object
      description: >-
        One touch of a visitor’s journey on this site. Identifier values
        (`clickIds`, `linker`) appear only when the visitor allowed Marketing.
      properties:
        at:
          type: string
          format: date-time
          description: When the page view happened.
        path:
          type: string
          example: /pricing
          description: The page’s path.
        landingUrl:
          type: string
          format: uri
          description: >-
            The full landing URL. Without Marketing consent its identifier
            parameters are removed.
        referrerOrigin:
          type:
            - string
            - 'null'
          description: Where the visitor came from; null for a direct visit.
          example: https://www.google.com
        adPlatforms:
          type: array
          items:
            type: string
            enum:
              - google
              - meta
              - tiktok
              - other
          description: >-
            Platforms whose ad click ID the URL carried — kept for every
            visitor, consent or not.
        campaign:
          type: object
          additionalProperties:
            type: string
          description: >-
            UTM and campaign parameters (`utm_*`, `gad_source`,
            `gad_campaignid`, `srsltid`).
          example:
            utm_source: google
            utm_campaign: spring-sale
        clickIds:
          type: object
          additionalProperties:
            type: string
          description: >-
            Ad click IDs (`gclid`, `gbraid`, `wbraid`, `fbclid`, `ttclid`).
            Marketing only.
          example:
            gclid: Cj0KCQjw…
        linker:
          type: object
          additionalProperties:
            type: string
          description: Google’s cross-domain linker value (`_gl`). Marketing only.
    BrowserIds:
      type: object
      additionalProperties:
        type: string
      description: >-
        Ad-platform browser IDs (`_gcl_au` — the cross-site key — `_gcl_aw`,
        `_gcl_gb`, `_gcl_gs`, `_gcl_dc`, `_ga`, `_fbp`, `_fbc`, `_ttp`).
        Marketing only.
      example:
        _gcl_au: 1.1.1234567890.1790000000
        _fbp: fb.1.1790000000000.123456789
    DataApiFieldError:
      type: object
      required:
        - name
        - message
      properties:
        name:
          type: string
          example: invalid
        code:
          type: integer
          example: 400
        path:
          type: string
          description: The query parameter or body field (dotted) the problem is in.
          example: match.emailSha256
        message:
          type: string
        data:
          type: object
  responses:
    DataApiInvalid:
      description: >-
        The request is not valid. `data.errors` lists each problem with the
        `path` of the parameter or body field it concerns.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/DataApiError'
          example:
            name: invalid
            message: The query is not valid.
            data:
              errors:
                - name: invalid
                  code: 400
                  path: limit
                  message: A whole number from 1 to 500.
                  data: {}
    DataApiUnauthorized:
      description: >-
        The API key is missing, malformed, unknown, disabled, expired or
        archived (`invalid`), or a request that must be signed — by the route,
        or by the key’s own signing mode — has a missing, stale or wrong
        signature (an upload signs its SHA-256, declared in
        `X-Fetchski-Content-SHA256`, in the body’s place) (`unauthorized`).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/DataApiError'
          example:
            name: unauthorized
            message: X-Fetchski-Signature does not match this request.
            data: {}
    DataApiForbidden:
      description: >-
        The API key lacks the scope this route needs (the message names it), or
        the key is used from an address outside its allowlist.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/DataApiError'
          example:
            name: forbidden
            message: 'This API key lacks the required scope: conversions:read.'
            data: {}
    DataApiNotFound:
      description: >-
        Nothing with this identifier — or the caller may not use the Data API at
        all (anonymous visitors and non-admin sessions get 404 so the API is not
        advertised). A write while conversion tracking is switched off also
        answers 404.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/DataApiError'
          example:
            name: notfound
            message: No event with this ID.
            data: {}
    DataApiRateLimited:
      description: >-
        The key used up its reads- or writes-per-minute limit (set per key;
        defaults 600 / 120). Wait `Retry-After` seconds.
      headers:
        Retry-After:
          description: Seconds until the budget resets.
          schema:
            type: integer
            minimum: 1
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/DataApiError'
          example:
            name: tooManyRequests
            message: This API key’s rate limit is used up for this minute.
            data:
              retryAfter: 12
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: Authorization
      description: >-
        Paste your API key as the CMS showed it: `fsk_<site>_<key id>_<secret>`
        (issued under API Keys). `ApiKey ` in front of it works too. Leave it
        empty to read as the public.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.