Skip to main content

Analytics Events

Overview

Seekora Analytics is a high-throughput ingest endpoint for storefront and search-attribution events. There are two supported paths to submit events, and both terminate at the same ingest endpoint and emit the same canonical event names:

PathWhen to usePackage
Seekora SDKYou're calling Seekora search from JS/TS — @seekora-ai/search-sdk gives you one-line trackXxx helpers, and @seekora-ai/ui-sdk-react fires the search-lifecycle events automatically when you mount <SearchProvider>.@seekora-ai/search-sdk, @seekora-ai/ui-sdk-react
Direct RESTNon-JS storefronts, server-side workers, mobile native shells, Liquid themes — anywhere you want to BYO transport and POST directly to the ingest endpoint.None — plain HTTPS

Endpoint

Seekora exposes exactly one public ingest path. Browser CORS is enabled on /v1/b only.

EnvironmentURL
Stagehttps://analytics-ingest.seekora.ai/v1/b
Productionhttps://analytics-ingest.seekora.ai/v1/b

Successful response

{ "ok": true, "okEvents": 5, "receivedEvents": 5 }

HTTP 200. okEvents equals the count of events in the batch that passed envelope-level validation at ingest; receivedEvents mirrors the size of the batch you sent. A 200 here means the batch was accepted — downstream processing runs asynchronously.

Error responses

{ "error": "error parsing message: ..." }

Returned with HTTP 400 when the body is malformed JSON or fails envelope validation.

Authentication

The ingest endpoint authenticates by a per-tenant writeKey carried at the top level of the request body. There is no Authorization header and no Bearer token on this surface — the writeKey alone identifies your tenant and store.

Where to put the writeKeyTop-level writeKey field on the request body
Where to find your writeKeyAdmin UI → StoresWelcomeEvents Write Key row
Programmatic retrievalGET /api/v3/account/analytics/writekey

See the Configuration guide for the full retrieval flow.

Request envelope

The top-level body shape is:

{
"writeKey": "<your-events-write-key>",
"batch": [ /* one or more events */ ],
"sentAt": "2026-05-31T14:50:00.500Z"
}
FieldTypeRequiredNotes
writeKeystring (UUID)yesPer-tenant ingest credential. Identifies the tenant and store.
batcharray of event objectsyesOne or more events — see per-event shape below.
sentAtstring (ISO-8601)yesWall-clock time when the client flushed the batch. Set this close to send time so ingest can correct for client clock drift.

Event fields

Each entry in batch is an event object with these fields:

FieldTypeRequiredNotes
typestringyesUse "track" for the canonical events listed below.
eventstringyesOne of the canonical event names — see the table below.
anonymousIdstring (UUID)yesClient-generated, stable per browser/session.
messageIdstring (UUID)yesUnique per event — used for downstream dedup.
timestampstring (ISO-8601)yesWhen the event occurred.
propertiesobjectyesPer-event payload — search_id, query, object_id, etc.
writeKeystring (UUID)yesThe same writeKey as the envelope; restated here for per-event routing.
userIdstringnoSet when the visitor is logged in. Used for identity stitching.

Canonical event names

The 18-event catalog below is the public event surface Seekora recognises. Sending an event name outside this list is silently dropped after ingest. Keep this set exact.

EventFired when
Search SubmittedThe visitor submits a search query.
Search RefinedThe visitor changes or re-runs the active query (e.g. typing more, hitting search again).
Search EmptyThe query returns zero results.
Search ImpressionA search results surface is rendered to the visitor.
Filter ViewedA filter / refinement UI is presented to the visitor.
Filter ConvertedThe visitor commits a filter change that re-runs the search.
Facet AppliedA facet value is toggled (subset of Filter Converted that names the facet).
Object ViewedA search result card / object enters the visitor's viewport.
Object ClickedThe visitor clicks a search result.
Object Clicked Outside SearchThe visitor clicks a Seekora-rendered object outside a search results context (e.g. a recommendation strip on a PDP).
Object ConvertedThe visitor performs a high-intent action on an object surfaced via search (e.g. add to cart, purchase).
Object Converted Outside SearchSame as above, but the object originated outside a search context.
Product AddedThe visitor adds a product to cart.
Product List ViewedA product list (category page, results page) renders for the visitor.
Order CompletedThe visitor finishes checkout.
Suggestion ImpressionA query suggestion is rendered to the visitor.
Suggestion ClickedThe visitor clicks a query suggestion.
Cart ViewedThe visitor views their cart.

Every search-attributed event (anything in the Search / Filter / Object / Suggestion families) should carry a stable search_id in properties so the downstream funnel can stitch the journey. See "Best practices" below.

Direct REST examples

The following single-event payload is the minimal verified shape that the ingest accepts. Replace <your-events-write-key>, <your-anonymous-id>, and <unique-uuid-per-event> with your values:

curl -X POST 'https://analytics-ingest.seekora.ai/v1/b' \
-H 'Content-Type: application/json' \
--data-raw '{
"writeKey": "<your-events-write-key>",
"batch": [
{
"type": "track",
"event": "Search Submitted",
"anonymousId": "<your-anonymous-id>",
"messageId": "<unique-uuid-per-event>",
"timestamp": "2026-05-31T14:50:00.000Z",
"properties": {
"search_id": "<search-uuid>",
"query": "shoes",
"num_found": 12,
"surface": "results_page"
},
"writeKey": "<your-events-write-key>"
}
],
"sentAt": "2026-05-31T14:50:00.500Z"
}'

A successful response is {"ok":true,"okEvents":1,"receivedEvents":1} with HTTP 200.

The same envelope works for every event in the catalog — only event and properties change. Three more examples below; each is a standalone batch you can POST identically.

Search Impression

{
"writeKey": "<your-events-write-key>",
"batch": [
{
"type": "track",
"event": "Search Impression",
"anonymousId": "<your-anonymous-id>",
"messageId": "<unique-uuid-per-event>",
"timestamp": "2026-05-31T14:50:01.000Z",
"properties": {
"search_id": "<search-uuid>",
"object_ids": ["prod_1001", "prod_1002", "prod_1003"]
},
"writeKey": "<your-events-write-key>"
}
],
"sentAt": "2026-05-31T14:50:01.500Z"
}

Object Clicked

{
"writeKey": "<your-events-write-key>",
"batch": [
{
"type": "track",
"event": "Object Clicked",
"anonymousId": "<your-anonymous-id>",
"messageId": "<unique-uuid-per-event>",
"timestamp": "2026-05-31T14:50:03.000Z",
"properties": {
"search_id": "<search-uuid>",
"object_id": "prod_1001",
"position": 1
},
"writeKey": "<your-events-write-key>"
}
],
"sentAt": "2026-05-31T14:50:03.500Z"
}

Order Completed

{
"writeKey": "<your-events-write-key>",
"batch": [
{
"type": "track",
"event": "Order Completed",
"anonymousId": "<your-anonymous-id>",
"userId": "user_8294",
"messageId": "<unique-uuid-per-event>",
"timestamp": "2026-05-31T14:50:05.000Z",
"properties": {
"search_id": "<search-uuid>",
"order_id": "order_55821",
"revenue": 199.99,
"currency": "USD",
"products": [{ "object_id": "prod_1001", "quantity": 1, "price": 199.99 }]
},
"writeKey": "<your-events-write-key>"
}
],
"sentAt": "2026-05-31T14:50:05.500Z"
}

In production you'll typically batch many events into one request — see "Best practices" below.

SDK examples

@seekora-ai/search-sdk (TypeScript)

The search SDK exports a track* helper per canonical event name. Pass a ctx object with your writeKey, ingestHost, and (optionally) userId; the helpers fill in type, messageId, timestamp, and the canonical event name for you.

import {
trackSearchSubmitted,
trackObjectClicked,
} from '@seekora-ai/search-sdk';

const ctx = {
writeKey: 'YOUR_WRITE_KEY',
ingestHost: 'https://analytics-ingest.seekora.ai',
userId: undefined, // set when the visitor is logged in
};

// Fire when the visitor submits a search:
trackSearchSubmitted(ctx, {
search_id: 'search-abc-001',
query: 'wireless headphones',
num_found: 47,
surface: 'search-page',
});

// Fire on result click:
trackObjectClicked(ctx, {
search_id: 'search-abc-001',
object_id: 'prod_1001',
position: 1,
});

@seekora-ai/ui-sdk-react (TSX)

If you're rendering Seekora's <SearchProvider> / <SearchBox> / <Hits> family, the SDK fires Search Submitted, Search Refined, Search Empty, Search Impression, Object Viewed, Object Clicked, and Suggestion Impression / Clicked automatically. Wire it once at the provider boundary:

import { SearchProvider, SearchBox, Hits } from '@seekora-ai/ui-sdk-react';

export function StorefrontSearch() {
return (
<SearchProvider
trackContext={{
writeKey: 'YOUR_WRITE_KEY',
ingestHost: 'https://analytics-ingest.seekora.ai',
}}
>
<SearchBox />
<Hits />
</SearchProvider>
);
}

Cart / checkout events (Product Added, Cart Viewed, Order Completed) live outside the search surface — fire those yourself from your checkout flow using the search-sdk track* helpers.

Best practices

  • Always batch. Buffer events client-side and send via /v1/b. Browser CORS is only enabled on /v1/b.
  • Set sentAt close to actual send time. Ingest uses the gap between sentAt and event timestamp to correct for client clock drift.
  • Always carry search_id. Every Search-family, Filter-family, Object-family, and Suggestion-family event should reuse the same search_id across the journey so the downstream funnel stitches correctly.
  • messageId must be unique per event. Downstream dedup uses it — reusing one collapses two events into one.
  • Don't invent event names. Anything outside the canonical catalog above is dropped silently after ingest validation.
  • One writeKey per tenant. Do not share writeKeys across orgs.