> ## 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.

# Get one conversion event

> One event by its event ID.

```php Laravel wrap theme={"dark"}
use Fetchski\AttractCmsLaravel\Facades\Attract;

$getConversionEvent = Attract::conversionEvents()->getConversionEvent(eventId: 'tenant-42:order:1234:purchase');
```

```json 200 wrap theme={"dark"}
{
  "event": {
    "eventId": "b8f2c1de-4f5a-4c1e-9a53-0d6f2b7c1e9a",
    "conversion": {
      "id": "ckq1w2e3r4t5y6u7i8o9p0",
      "key": "quote-request",
      "title": "Quote request"
    },
    "trigger": {
      "type": "form",
      "ref": "ckformid123"
    },
    "source": "browser",
    "occurredAt": "2026-09-21T15:04:05.000Z",
    "recordedAt": "2026-09-21T15:04:05.412Z",
    "updatedAt": "2026-09-21T15:04:07.020Z",
    "page": {
      "url": "https://example.com/contact?gclid=Cj0KCQjw",
      "path": "/contact",
      "referrer": "https://www.google.com"
    },
    "visitorId": "0b8e6a4c-8d2f-4b6e-9a1c-3f5d7e9b1a2c",
    "attribution": {
      "firstTouch": {
        "at": "2026-09-20T09:12:00.000Z",
        "path": "/",
        "landingUrl": "https://example.com/?gclid=Cj0KCQjw",
        "referrerOrigin": "https://www.google.com",
        "adPlatforms": [
          "google"
        ],
        "campaign": {
          "utm_source": "google",
          "utm_campaign": "spring-sale"
        },
        "clickIds": {
          "gclid": "Cj0KCQjw"
        }
      },
      "adPlatforms": [
        "google"
      ],
      "campaign": {
        "utm_source": "google",
        "utm_campaign": "spring-sale"
      },
      "clickIds": {
        "gclid": "Cj0KCQjw"
      },
      "browserIds": {
        "_gcl_au": "1.1.1234567890.1790000000"
      },
      "provenance": {
        "gclid": "ourRecord",
        "_gcl_au": "platformCookie"
      }
    },
    "consent": {
      "categories": {
        "analytics": true,
        "marketing": true
      },
      "mode": "lenient",
      "country": "US",
      "gpc": false,
      "policyVersion": 7
    },
    "value": 50,
    "currency": "USD",
    "userData": {
      "emailSha256": "b4c9a289323b21a01c3e940f150eb9b8c542587f1abfd8f0e1cc1ffc5e475514",
      "phoneSha256": "b4c9a289323b21a01c3e940f150eb9b8c542587f1abfd8f0e1cc1ffc5e475514",
      "phoneDigitsSha256": "b4c9a289323b21a01c3e940f150eb9b8c542587f1abfd8f0e1cc1ffc5e475514"
    },
    "submissionId": "ck9submission1",
    "delivery": {
      "google-ads": {
        "browser": "sent",
        "server": {
          "status": "sent",
          "attempts": 1,
          "sentAt": "2026-09-21T15:04:07.001Z",
          "lastAttemptAt": "2026-09-21T15:04:07.001Z"
        }
      },
      "ga4": {
        "browser": "sent",
        "server": {
          "status": "n/a",
          "attempts": 0
        }
      }
    },
    "status": "active",
    "adjustments": []
  }
}
```

## In your app

A controller answers a route on your app: it calls `getConversionEvent()` and returns the answer.

**1. Add the controller**

```php app/Http/Controllers/GetConversionEventController.php theme={"dark"}
<?php

declare(strict_types=1);

namespace App\Http\Controllers;

use Fetchski\AttractCmsLaravel\Data\GetConversionEventResponse;
use Fetchski\AttractCmsLaravel\Facades\Attract;

final class GetConversionEventController
{
    public function __invoke(string $eventId): GetConversionEventResponse
    {
        return Attract::conversionEvents()->getConversionEvent(eventId: $eventId);
    }
}
```

**2. Add the route**

```php routes/web.php theme={"dark"}
use App\Http\Controllers\GetConversionEventController;
use Illuminate\Support\Facades\Route;

Route::get('/conversion-event/{eventId}', GetConversionEventController::class);
```

**3. Call it**

```bash Terminal theme={"dark"}
curl http://localhost:8000/conversion-event/tenant-42:order:1234:purchase
```

With `php artisan serve` running, it prints its answer as JSON.

> [!TIP]
>
> To keep the call in an Action instead, this writes `app/Actions/Attract/GetConversionEvent.php` (an invokable Action that calls `getConversionEvent()`) and its test under `tests/Feature/Actions/Attract/` (Pest when your app has it):
>
> ```bash Terminal theme={"dark"}
> php artisan make:attract-action conversionEvents.getConversionEvent
> ```

## Call it from the terminal

```bash Terminal theme={"dark"}
php artisan attract:call conversionEvents.getConversionEvent eventId=tenant-42:order:1234:purchase
```

It calls `getConversionEvent()` on your default site and prints its answer as JSON. See [Call any method from the terminal](https://docs.fetchski-attract.com/call-command.md) for every argument type.

> [accordion] Required parameters
>
> Named arguments, e.g. `getConversionEvent(eventId: …)`; leave out the optional ones.
>
> **eventId** `string` *required*
>
> **Required.** The event ID. Matches `^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$`. At most 128 characters.

> [accordion] Returns
>
> A `GetConversionEventResponse` (`Fetchski\AttractCmsLaravel\Data\GetConversionEventResponse`). The event. Read its properties (`$getConversionEvent->event`): each says whether the site always sends it.
>
> **event** `ConversionEvent`
>
> **Always sent.** A `ConversionEvent`. One conversion occurrence. Fields the visitor’s consent did not allow are ABSENT, not empty. Never returned: the visitor’s IP address and user agent, outbox lease and retry bookkeeping.
>
> **ConversionEvent (Fetchski\AttractCmsLaravel\Data\ConversionEvent)**
>
> **eventId** `string`
>
> **Always sent.** Opaque, unique per site, stable. The CMS mints a UUID for events it originates; another system mints its own (e.g. `tenant-42:order:1234:purchase`). Forwarded unchanged to every ad platform as its de-duplication key. Matches `^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$`. At most 128 characters.
>
> **conversion** `ConversionEventConversion`
>
> **Always sent.** A `ConversionEventConversion`. The Conversion it counts for.
>
> **ConversionEventConversion (Fetchski\AttractCmsLaravel\Data\ConversionEventConversion)**
>
> **id** `string`
>
> **Always sent.** The Conversion’s id.
>
> **key** `string`
>
> **Always sent.** The Conversion’s key when the event was recorded.
>
> **title** `?string`
>
> **Sometimes sent:** `null` when the site leaves it out. The Conversion’s name in the CMS.
>
> **source** `Enums\\ConversionEventSource|string`
>
> **Always sent.** How it was recorded: `browser` (the site’s tag), `external` (posted through the Conversion Data API), or `server` (the site’s own server; nothing records one yet). One of `browser`, `server`, `external`; a newer site may send others, so keep a fallback.
>
> **occurredAt** `CarbonImmutable`
>
> **Always sent.** When it happened. Format: an ISO 8601 date and time.
>
> **updatedAt** `CarbonImmutable`
>
> **Always sent.** Last change (delivery outcome, adjustment) — the sync order. Format: an ISO 8601 date and time.
>
> **delivery** `array\<string, DeliveryState\>`
>
> **Always sent.** A map of names to `DeliveryState`. Per destination the Conversion reports to.
>
> **DeliveryState (Fetchski\AttractCmsLaravel\Data\DeliveryState)**
>
> **browser** `Enums\\Browser|string|null`
>
> **Sometimes sent:** `null` when the site leaves it out. Whether the page fired it at this platform’s tag; `n/a` when the Conversion sends to this platform from the server only. One of `sent`, `not-sent`, `n/a`; a newer site may send others, so keep a fallback.
>
> **server** `?ServerDeliveryState`
>
> **Sometimes sent:** `null` when the site leaves it out. A `ServerDeliveryState`. Server-side delivery to this platform’s API (Google Data Manager, GA4 Measurement Protocol, Meta Conversions API, TikTok Events API).
>
> **ServerDeliveryState (Fetchski\AttractCmsLaravel\Data\ServerDeliveryState)**
>
> **status** `Enums\\ServerDeliveryStateStatus|string`
>
> **Always sent.** `queued`/`retry` — owed; `sent` — delivered (`test: true` in a platform test mode); `failed` — attempts used up or a permanent error; `not-configured` — credentials, pixel, Google tag or conversion action missing; `expired` — older than the platform accepts (Google Ads 90 days, Meta and TikTok 7, Google Analytics 3); `not-sent` — retracted first, or nothing the platform can match; `no-consent` — the visitor did not allow Marketing; `n/a` — the platform has no server API here. One of `queued`, `retry`, `sent`, `failed`, `not-configured`, `expired`, `not-sent`, `no-consent`, `n/a`; a newer site may send others, so keep a fallback.
>
> **attempts** `int`
>
> **Always sent.** Sends tried so far. At least 0.
>
> **sentAt** `?CarbonImmutable`
>
> **Sometimes sent:** `null` when the site leaves it out. When the platform took it; absent until then. Format: an ISO 8601 date and time.
>
> **lastAttemptAt** `?CarbonImmutable`
>
> **Sometimes sent:** `null` when the site leaves it out. The last try; absent before the first. Format: an ISO 8601 date and time.
>
> **nextAttemptAt** `?CarbonImmutable`
>
> **Sometimes sent:** `null` when the site leaves it out. Set while `status` is `retry`; null otherwise. Format: an ISO 8601 date and time.
>
> **lastError** `?string`
>
> **Sometimes sent:** `null` when the site leaves it out. Why the last try failed, as the platform said. At most 500 characters.
>
> **detail** `?string`
>
> **Sometimes sent:** `null` when the site leaves it out. Why it wasn’t sent, for a status it ended in without trying (e.g. “The event was retracted.”). At most 500 characters.
>
> **test** `?bool`
>
> **Sometimes sent:** `null` when the site leaves it out. Present (true) only when it went to the platform’s test mode. Always `true`.
>
> **adjustment** `?AdjustmentDeliveryState`
>
> **Sometimes sent:** `null` when the site leaves it out. An `AdjustmentDeliveryState`. Telling this platform about the latest adjustment, once it had already received the event. Google Ads: the event re-sent with its new value (a retraction is sent as 0 — Google still counts the conversion). Google Analytics: a `refund`, for purchases only and only to lower the value. Meta, TikTok, Google Tag Manager and browser-only rows cannot be told (`unsupported`, with what to do instead in `detail`).
>
> **AdjustmentDeliveryState (Fetchski\AttractCmsLaravel\Data\AdjustmentDeliveryState)**
>
> **adjustmentId** `string`
>
> **Always sent.** Opaque, unique per site, stable. The CMS mints a UUID for events it originates; another system mints its own (e.g. `tenant-42:order:1234:purchase`). Forwarded unchanged to every ad platform as its de-duplication key. Matches `^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$`. At most 128 characters.
>
> **action** `Enums\\Action|string`
>
> **Always sent.** `retract` (withdraw the conversion) or `restate` (a new value). One of `retract`, `restate`; a newer site may send others, so keep a fallback.
>
> **status** `Enums\\AdjustmentDeliveryStateStatus|string`
>
> **Always sent.** `queued`/`retry` — owed; `sent` — delivered (`test: true` in a platform test mode); `failed` — attempts used up or a permanent error; `not-configured` — credentials or the live Google tag missing; `unsupported` — the platform cannot be told (`detail` says why and what to do instead); `not-needed` — the platform already holds this value. One of `queued`, `retry`, `sent`, `failed`, `not-configured`, `unsupported`, `not-needed`; a newer site may send others, so keep a fallback.
>
> **attempts** `int`
>
> **Always sent.** Sends tried so far. At least 0.
>
> **value** `?float`
>
> **Sometimes sent:** `null` when the site leaves it out. The value the platform holds once it is sent. At least 0.
>
> **refund** `?float`
>
> **Sometimes sent:** `null` when the site leaves it out. Google Analytics: the amount refunded. At least 0.
>
> **currency** `?string`
>
> **Sometimes sent:** `null` when the site leaves it out. ISO 4217 code, e.g. `USD`. Matches `^[A-Z]{3}$`.
>
> **sentAt** `?CarbonImmutable`
>
> **Sometimes sent:** `null` when the site leaves it out. When the platform took it; absent until then. Format: an ISO 8601 date and time.
>
> **lastAttemptAt** `?CarbonImmutable`
>
> **Sometimes sent:** `null` when the site leaves it out. The last try; absent before the first. Format: an ISO 8601 date and time.
>
> **nextAttemptAt** `?CarbonImmutable`
>
> **Sometimes sent:** `null` when the site leaves it out. Set while `status` is `retry`; null otherwise. Format: an ISO 8601 date and time.
>
> **lastError** `?string`
>
> **Sometimes sent:** `null` when the site leaves it out. Why the last try failed, as the platform said. At most 500 characters.
>
> **detail** `?string`
>
> **Sometimes sent:** `null` when the site leaves it out. Why it wasn’t sent, for a status it ended in without trying (e.g. “The event was retracted.”). At most 500 characters.
>
> **test** `?bool`
>
> **Sometimes sent:** `null` when the site leaves it out. Present (true) only when it went to the platform’s test mode. Always `true`.
>
> **status** `Enums\\Status|string`
>
> **Always sent.** `active`; `retracted` (withdrawn by an adjustment) or `adjusted` (its value restated). One of `active`, `retracted`, `adjusted`; a newer site may send others, so keep a fallback.
>
> **adjustments** `Collection\<int, Adjustment\>`
>
> **Always sent.** A list of `Adjustment`. Every adjustment, oldest first (empty when none).
>
> **Adjustment (Fetchski\AttractCmsLaravel\Data\Adjustment)**
>
> **adjustmentId** `string`
>
> **Always sent.** Opaque, unique per site, stable. The CMS mints a UUID for events it originates; another system mints its own (e.g. `tenant-42:order:1234:purchase`). Forwarded unchanged to every ad platform as its de-duplication key. Matches `^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$`. At most 128 characters.
>
> **action** `Enums\\Action|string`
>
> **Always sent.** `retract` (withdraw the conversion) or `restate` (a new value). One of `retract`, `restate`; a newer site may send others, so keep a fallback.
>
> **recordedAt** `CarbonImmutable`
>
> **Always sent.** When the site stored it. Format: an ISO 8601 date and time.
>
> **value** `?float`
>
> **Sometimes sent:** `null` when the site leaves it out. The restated value (restate only). At least 0.
>
> **currency** `?string`
>
> **Sometimes sent:** `null` when the site leaves it out. ISO 4217 code of `value`. Matches `^[A-Z]{3}$`.
>
> **reason** `?string`
>
> **Sometimes sent:** `null` when the site leaves it out. Why, as the writer gave it. At most 500 characters.
>
> **occurredAt** `?CarbonImmutable`
>
> **Sometimes sent:** `null` when the site leaves it out. When the change happened (as the writer said). Format: an ISO 8601 date and time.
>
> **recordedBy** `?RecordedBy`
>
> **Sometimes sent:** `null` when the site leaves it out. A `RecordedBy`. Who wrote it: an API key (its public key ID and label) or an administrator.
>
> **RecordedBy (Fetchski\AttractCmsLaravel\Data\RecordedBy)**
>
> **type** `Enums\\RecordedByType|string`
>
> **Always sent.** An API key or a signed-in person. One of `apiKey`, `user`; a newer site may send others, so keep a fallback.
>
> **label** `string`
>
> **Always sent.** The key’s label, or the person’s name.
>
> **keyId** `?string`
>
> **Sometimes sent:** `null` when the site leaves it out. An API key: its public key id.
>
> **id** `?string`
>
> **Sometimes sent:** `null` when the site leaves it out. A person: their user `_id`.
>
> **trigger** `?ConversionEventTrigger`
>
> **Sometimes sent:** `null` when the site leaves it out. A `ConversionEventTrigger`. What fired it.
>
> **ConversionEventTrigger (Fetchski\AttractCmsLaravel\Data\ConversionEventTrigger)**
>
> **type** `Enums\\ConversionEventTriggerType|string|null`
>
> **Sometimes sent:** `null` when the site leaves it out. A form submitted, a page viewed, a link clicked, a chat event, or an `external` fact. One of `form`, `pageView`, `linkClick`, `chat`, `external`; a newer site may send others, so keep a fallback.
>
> **ref** `?string`
>
> **Sometimes sent:** `null` when the site leaves it out. What exactly: the form’s `aposDocId` (`form`), the page path (`pageView`), the link rule that matched or `outbound:<host>` / `download:<type>` (`linkClick`), the chat event (`chat`), or the producer’s reference (`external`). At most 256 characters.
>
> **recordedAt** `?CarbonImmutable`
>
> **Sometimes sent:** `null` when the site leaves it out. When the site stored it. Format: an ISO 8601 date and time.
>
> **page** `?ConversionEventPageInfo`
>
> **Sometimes sent:** `null` when the site leaves it out. A `ConversionEventPageInfo`. The page it happened on.
>
> **ConversionEventPageInfo (Fetchski\AttractCmsLaravel\Data\ConversionEventPageInfo)**
>
> **url** `?string`
>
> **Sometimes sent:** `null` when the site leaves it out. Analytics or Marketing only. Format: an absolute URL.
>
> **path** `?string`
>
> **Sometimes sent:** `null` when the site leaves it out. Its path.
>
> **referrer** `?string`
>
> **Sometimes sent:** `null` when the site leaves it out. Origin only.
>
> **visitorId** `?string`
>
> **Sometimes sent:** `null` when the site leaves it out. Analytics or Marketing only.
>
> **attribution** `?EventAttribution`
>
> **Sometimes sent:** `null` when the site leaves it out. An `EventAttribution`. The attribution snapshot the event copied when it happened — independent of the visitor record’s later changes or expiry.
>
> **EventAttribution (Fetchski\AttractCmsLaravel\Data\EventAttribution)**
>
> **adPlatforms** `Collection\<int, string\>`
>
> **Always sent.** The ad platforms whose click ids the visits carried (empty when none).
>
> **campaign** `array\<string, string\>`
>
> **Always sent.** The campaign parameters in force (`utm_*`, …); empty when none.
>
> **firstTouch** `?AttributionTouch`
>
> **Sometimes sent:** `null` when the site leaves it out. An `AttributionTouch`. The visitor’s first visit on record; null when unknown.
>
> **AttributionTouch (Fetchski\AttractCmsLaravel\Data\AttributionTouch)**
>
> **at** `?CarbonImmutable`
>
> **Sometimes sent:** `null` when the site leaves it out. When the page view happened. Format: an ISO 8601 date and time.
>
> **path** `?string`
>
> **Sometimes sent:** `null` when the site leaves it out. The page’s path.
>
> **landingUrl** `?string`
>
> **Sometimes sent:** `null` when the site leaves it out. The full landing URL. Without Marketing consent its identifier parameters are removed. Format: an absolute URL.
>
> **referrerOrigin** `?string`
>
> **Sometimes sent:** `null` when the site leaves it out. Where the visitor came from; null for a direct visit.
>
> **adPlatforms** `Collection\<int, Enums\\AdPlatforms|string\>|null`
>
> **Sometimes sent:** `null` when the site leaves it out. Platforms whose ad click ID the URL carried — kept for every visitor, consent or not. Each one of `google`, `meta`, `tiktok`, `other`.
>
> **campaign** `array\<string, string\>|null`
>
> **Sometimes sent:** `null` when the site leaves it out. UTM and campaign parameters (`utm_*`, `gad_source`, `gad_campaignid`, `srsltid`).
>
> **clickIds** `array\<string, string\>|null`
>
> **Sometimes sent:** `null` when the site leaves it out. Ad click IDs (`gclid`, `gbraid`, `wbraid`, `fbclid`, `ttclid`). Marketing only.
>
> **linker** `array\<string, string\>|null`
>
> **Sometimes sent:** `null` when the site leaves it out. Google’s cross-domain linker value (`_gl`). Marketing only.
>
> **lastTouch** `?AttributionTouch`
>
> **Sometimes sent:** `null` when the site leaves it out. An `AttributionTouch`. Their last visit with an ad click or campaign parameters; null when none. Its fields are listed under the first `AttributionTouch` above.
>
> **clickIds** `array\<string, string\>|null`
>
> **Sometimes sent:** `null` when the site leaves it out. Marketing only.
>
> **browserIds** `array\<string, string\>|null`
>
> **Sometimes sent:** `null` when the site leaves it out. Ad-platform browser IDs (`_gcl_au` — the cross-site key — `_gcl_aw`, `_gcl_gb`, `_gcl_gs`, `_gcl_dc`, `_ga`, `_fbp`, `_fbc`, `_ttp`). Marketing only.
>
> **gaSessions** `array\<string, string\>|null`
>
> **Sometimes sent:** `null` when the site leaves it out. The visitor’s Google Analytics session per data stream when it happened (from the `_ga_<container>` cookies), keyed by the measurement ID without its `G-`. The GA4 server sender joins the event to that session. Marketing only.
>
> **provenance** `array\<string, Enums\\Provenance|string\>|null`
>
> **Sometimes sent:** `null` when the site leaves it out. Where each identifier came from: the visitor’s attribution record, the page URL, the platform’s own cookie, or the system that posted an external fact.
>
> **consent** `?ConsentSnapshot`
>
> **Sometimes sent:** `null` when the site leaves it out. A `ConsentSnapshot`. The consent in force when the event happened. For an external fact (`origin: producer`) the posting system’s flags are recorded as `adUserData` / `adPersonalization` / `gpc` (limited data use).
>
> **ConsentSnapshot (Fetchski\AttractCmsLaravel\Data\ConsentSnapshot)**
>
> **categories** `ConsentSnapshotCategories`
>
> **Always sent.** A `ConsentSnapshotCategories`. What the visitor allowed.
>
> **ConsentSnapshotCategories (Fetchski\AttractCmsLaravel\Data\ConsentSnapshotCategories)**
>
> **analytics** `?bool`
>
> **Sometimes sent:** `null` when the site leaves it out. Analytics cookies and measurement.
>
> **marketing** `?bool`
>
> **Sometimes sent:** `null` when the site leaves it out. Marketing: ad platforms and their identifiers.
>
> **mode** `Enums\\ConsentSnapshotMode|string`
>
> **Always sent.** The rule that applied to the visitor: `lenient` (a notice-only region: non-essential ran unless they declined) or `strict` (an opt-in region: nothing non-essential until they agreed). One of `lenient`, `strict`; a newer site may send others, so keep a fallback.
>
> **gpc** `bool`
>
> **Always sent.** Global Privacy Control, or the producer’s limited-data-use flag.
>
> **country** `?string`
>
> **Sometimes sent:** `null` when the site leaves it out. The visitor’s two-letter country, when known; null otherwise. Matches `^[A-Z]{2}$`.
>
> **policyVersion** `?int`
>
> **Sometimes sent:** `null` when the site leaves it out. The consent policy version the visitor answered; null when none.
>
> **adUserData** `?bool`
>
> **Sometimes sent:** `null` when the site leaves it out. An external fact: the producer allowed sending user data to ad platforms.
>
> **adPersonalization** `?bool`
>
> **Sometimes sent:** `null` when the site leaves it out. An external fact: the producer allowed ad personalization.
>
> **origin** `?string`
>
> **Sometimes sent:** `null` when the site leaves it out. Present (`producer`) when the consent came with an external fact. Always `producer`.
>
> **value** `?float`
>
> **Sometimes sent:** `null` when the site leaves it out. Its value, when the Conversion has one. At least 0.
>
> **adjustedValue** `?float`
>
> **Sometimes sent:** `null` when the site leaves it out. The latest restated value. At least 0.
>
> **currency** `?string`
>
> **Sometimes sent:** `null` when the site leaves it out. ISO 4217 code of `value`, e.g. `USD`. Matches `^[A-Z]{3}$`.
>
> **userData** `?UserDataHashes`
>
> **Sometimes sent:** `null` when the site leaves it out. An `UserDataHashes`. Hashed contact details. Email: trimmed + lower-cased; `emailGoogleSha256` only for a Gmail address with dots (Google drops them). Phone: E.164 — `phoneSha256` with the "+", `phoneDigitsSha256` without (Meta’s form).
>
> **UserDataHashes (Fetchski\AttractCmsLaravel\Data\UserDataHashes)**
>
> **emailSha256** `?string`
>
> **Sometimes sent:** `null` when the site leaves it out. Lower-case hex SHA-256 digest. Matches `^[0-9a-f]{64}$`.
>
> **emailGoogleSha256** `?string`
>
> **Sometimes sent:** `null` when the site leaves it out. Lower-case hex SHA-256 digest. Matches `^[0-9a-f]{64}$`.
>
> **phoneSha256** `?string`
>
> **Sometimes sent:** `null` when the site leaves it out. Lower-case hex SHA-256 digest. Matches `^[0-9a-f]{64}$`.
>
> **phoneDigitsSha256** `?string`
>
> **Sometimes sent:** `null` when the site leaves it out. Lower-case hex SHA-256 digest. Matches `^[0-9a-f]{64}$`.
>
> **submissionId** `?string`
>
> **Sometimes sent:** `null` when the site leaves it out. The form submission a form conversion came from.
>
> **producer** `?Producer`
>
> **Sometimes sent:** `null` when the site leaves it out. A `Producer`. The system that posted an external fact.
>
> **Producer (Fetchski\AttractCmsLaravel\Data\Producer)**
>
> **system** `string`
>
> **Always sent.** The system’s name, as it posted it (lowercase, `a-z0-9._-`, at most 64).
>
> **contentHash** `string`
>
> **Always sent.** Fingerprint of the posted fact (idempotency).
>
> **recordedBy** `RecordedBy`
>
> **Always sent.** A `RecordedBy`. Who wrote it: an API key (its public key ID and label) or an administrator. Its fields are listed under the first `RecordedBy` above.
>
> **ref** `?string`
>
> **Sometimes sent:** `null` when the site leaves it out. Its own reference for the fact (at most 256 characters), when it gave one.
>
> **join** `?ConversionEventJoin`
>
> **Sometimes sent:** `null` when the site leaves it out. A `ConversionEventJoin`. How an external fact was joined to a visitor (`by: null` — no match).
>
> **ConversionEventJoin (Fetchski\AttractCmsLaravel\Data\ConversionEventJoin)**
>
> **by** `Enums\\By|string|null`
>
> **Sometimes sent:** `null` when the site leaves it out. The identifier that matched, or null for none. One of `visitorId`, `token`, `gclAu`, `gclid`, `gbraid`, `wbraid`, `fbclid`, `ttclid`, `emailSha256`, `phoneSha256`; a newer site may send others, so keep a fallback.

> [accordion] Errors
>
> Nothing to catch: the SDK retries what is safe, and Laravel logs and renders the rest (see [Errors and testing](https://docs.fetchski-attract.com/laravel/errors.md)).
>
> **AuthenticationFailed** `401`
>
> 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`).
>
> **Retried first:** No. **Read:** `siteMessage()`, `errorName()`, `data()`.
>
> **Forbidden** `403`
>
> The API key lacks the scope this route needs (the message names it), or the key is used from an address outside its allowlist.
>
> **Retried first:** No. **Read:** `siteMessage()`, `errorName()`, `data()`.
>
> **NotFound** `404`
>
> 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.
>
> **Retried first:** No. **Read:** `siteMessage()`, `errorName()`, `data()`.
>
> **RateLimited** `429`
>
> The key used up its reads- or writes-per-minute limit (set per key; defaults 600 / 120). Still, after the client’s own waits and retries.
>
> **Retried first:** Yes: the client waits `Retry-After` (1 s when absent; over 60 s it gives up at once) and retries, up to `retries` times (2 by default). **Read:** `retryAfter()` (seconds, or null), `siteMessage()`.
>
> **SiteError** `5xx`
>
> The site failed (or something in front of it did). Its logs say why.
>
> **Retried first:** A 502, 503 or 504: yes, after 0.5 s then 1 s. A 500: no. **Read:** `siteMessage()`, `errorName()`, `data()`.
>
> **SiteUnreachable** `no answer`
>
> No answer: DNS, a refused connection, TLS, or the timeout (15 s per attempt by default).
>
> **Retried first:** Yes, after 0.5 s then 1 s. **Read:** `reason()`, `neverConnected()`.
>
> **RequestNotSent** `not sent`
>
> The client refused to send it (e.g. a path segment of `..`); the site never saw it.
>
> **Retried first:** No. **Read:** `reason()` (what to change).
>
> **SiteRequestFailed** `any other`
>
> Any other status, or a success whose JSON doesn’t match this SDK version’s types (update the SDK).
>
> **Retried first:** No. **Read:** `reason()`, `status()`.
>
> **UnknownSite** `setup`
>
> `Attract::site('acme')` named a slug that isn’t under `sites` in `config/fetchski-attract.php`.
>
> **Retried first:** No: nothing was sent. **Read:** `getMessage()`, `$e->slug`.
>
> **SiteConfigurationError** `setup`
>
> The site’s config entry is wrong: no `url`, a malformed `api_key`, a key filed under another slug, or no default site.
>
> **Retried first:** No: nothing was sent. **Read:** `getMessage()` (what to fix).

## Behavior

* **Access:** an API key with `conversions:read` — or its group master `ads:all`, or `admin:all` — or a signed-in user holding the `conversion-events: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`. The client waits and retries for you; you see `RateLimited` only if that runs out.
* **Retries:** a read is safe to repeat: the client retries it on no answer, a 502/503/504 and a 429.
* **Timeout:** 15 s per attempt by default; raise `timeout` in `config/fetchski-attract.php` for longer.

## More examples

```php Or null wrap theme={"dark"}
use Fetchski\AttractCmsLaravel\Facades\Attract;

$getConversionEvent = Attract::conversionEvents()->findConversionEvent(eventId: 'tenant-42:order:1234:purchase'); // ?GetConversionEventResponse: null when the site answers 404
```

## Test it

It calls the route above with the SDK faked: the fake answers every call with a valid example, so nothing leaves your machine and no config or key is needed. Save it as `tests/Feature/GetConversionEventTest.php`, then run your tests:

```php tests/Feature/GetConversionEventTest.php theme={"dark"}
<?php

declare(strict_types=1);

namespace Tests\Feature;

use Fetchski\AttractCmsLaravel\Facades\Attract;
use Tests\TestCase;

final class GetConversionEventTest extends TestCase
{
    public function test_get_conversion_event(): void
    {
        Attract::fake();

        $this->getJson('/conversion-event/tenant-42:order:1234:purchase')->assertOk();

        Attract::assertCalled('conversionEvents.getConversionEvent', fn (array $args): bool => $args['eventId'] === 'tenant-42:order:1234:purchase');
    }
}
```

```bash Terminal theme={"dark"}
php artisan test
```

**Answer with your own values:** fake `conversionEvents.getConversionEvent` with `GetConversionEventResponse::factory()->make([...])`, the fields you give over a valid example (nested ones too).

```php tests/Feature/GetConversionEventAnswerTest.php theme={"dark"}
<?php

declare(strict_types=1);

namespace Tests\Feature;

use Fetchski\AttractCmsLaravel\Data\GetConversionEventResponse;
use Fetchski\AttractCmsLaravel\Facades\Attract;
use Tests\TestCase;

final class GetConversionEventAnswerTest extends TestCase
{
    public function test_get_conversion_event_answers_your_values(): void
    {
        Attract::fake([
            'conversionEvents.getConversionEvent' => GetConversionEventResponse::factory()->make(['event' => ['eventId' => 'Your value']]),
        ]);

        $getConversionEvent = Attract::conversionEvents()->getConversionEvent(eventId: 'tenant-42:order:1234:purchase');

        $this->assertSame('Your value', $getConversionEvent->event->eventId);
    }
}
```

## Related

> [card] [List conversion events (incremental sync)](https://docs.fetchski-attract.com/laravel/reference/conversion-events/list-conversion-events.md)
>
> `Attract::conversionEvents()->listConversionEvents()`

> [card] [Post a conversion another system saw](https://docs.fetchski-attract.com/laravel/reference/conversion-events/create-external-conversion-event.md)
>
> `Attract::conversionEvents()->createExternalConversionEvent()`

> [card] [Retract or restate a conversion event](https://docs.fetchski-attract.com/laravel/reference/conversion-events/adjust-conversion-event.md)
>
> `Attract::conversionEvents()->adjustConversionEvent()`

> [card] [GET /conversion-event/\{eventId}](https://docs.fetchski-attract.com/api-reference/conversion-events/get-one-conversion-event.md)
>
> The endpoint this calls, in the API reference: the HTTP request and answer, and every rule the site checks.


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