Seedy Pea

seedypea-client

seedypea-client is the browser package. It collects events, binds declarative HTML attributes, manages local tactic state, and includes small web components.

import { init, pageView, track } from "seedypea-client";

init({ endpoint: "/sp/events" });
pageView();
track("AddToCart", { item: "pants" });

Modes

Edge Mode

Edge mode sends same-origin requests to your edge collector. The seedypea edge helper owns the first-party HttpOnly anonymous id.

init({ endpoint: "/sp/events" });

If writeKey is omitted, init() defaults to edge mode. When using edge mode, pass a non-default endpoint.

Direct Mode

Direct mode sends straight to Seedy Pea. The browser stores an anonymous id in localStorage.

init({
  projectId: "proj_...",
  writeKey: "wk_..."
});

Direct mode defaults to https://app.seedy-pea.com/v1/events.

API

init(config)

Initializes the client.

const result = init({
  endpoint: "/sp/events",
  mode: "edge"
});

result.mode;
result.anonymousId;

In direct mode, projectId and writeKey are required. In edge mode, endpoint is required unless you deliberately override the hosted default.

track(type, detail)

Sends one event.

await track("ProductViewed", {
  sku: "sku_123",
  path: location.pathname
});

type must be a non-empty string. Public event types accepted by the HTTP API must match ^[A-Za-z][A-Za-z0-9_.]{0,62}$.

pageView(detail)

Sends PageViewed with page metadata.

await pageView({ surface: "homepage" });

The default detail includes path, href, title, and referrer.

identify(ids, traits)

Sends Identify.

await identify(
  { email: "ada@example.com" },
  { plan: "founder" }
);

bind(root, options)

Binds elements with sp-event or sp-tactic.

<button sp-event="PersonaSelected" sp-prop-persona="developer">
  Developer
</button>
import { bind } from "seedypea-client";

const cleanup = bind(document, { observe: true });

Attributes:

Attribute Description
sp-event Event type to track.
sp-tactic Registered tactic name to run.
sp-on DOM event name. Defaults to click.
sp-prop-* String props passed as event detail or tactic props.
sp-prevent-default true or false. Tactics on forms submit-prevent by default.
sp-requires-consent Suppresses the action locally unless consent for that category is granted.

When consent suppresses an action, the client sends no event. Trusted sp.event.suppressed receipts are substrate-emitted only.

tactics

Local tactic registry and profile-aware state.

import { tactics, track } from "seedypea-client";

tactics.register("lead-capture", async ({ props }) => {
  await track("LeadCaptureOpened", props);
});

tactics.open("lead-capture");
tactics.dismiss("lead-capture");

const unsubscribe = tactics.subscribe("lead-capture", state => {
  console.log(state.open, state.goal_reached);
});

Profile state can be pushed into the tactic layer:

tactics.setProfile({
  fold: { known: true },
  segments: [{ segment_name: "high_intent" }],
  tactics: {
    "lead-capture": {
      trigger: "ExitIntentDetected",
      goal: "LeadEmailCaptured",
      once: true,
      goal_reached: false
    }
  }
});

detectExitIntent(callback, options)

Runs a callback when the pointer leaves through the top of the viewport.

const cleanup = detectExitIntent(detail => {
  tactics.open("lead-capture");
}, { threshold: 8, once: true });

trackInteractionSignal(options)

Samples local browser interaction for a short window and emits one coarse InteractionSignalObserved fact — a likelihood plus bucketed interaction evidence.

const signal = trackInteractionSignal({ sampleMs: 4000 });
// end the window early if needed:
signal.stop();
await signal.done;

Options: root (target to observe, defaults to the document), sampleMs (sampling window), eventType (override the emitted type), and detail (extra event detail). Returns a controller with stop() and a done promise that resolves to the ingest response.

It is evidence, not a verdict: it sends no raw pointer trails and does not prove personhood — treat the result as weak browser-interaction evidence the profile fold can seed from, not proof.

forget(options)

Deletes the current anonymous profile and clears local client state.

await forget();

In edge mode, the default endpoint is /sp/profile/delete. In direct mode, it is /v1/profile/delete.

Consent starts denied for every category.

await consent.grant("analytics");
consent.allows("analytics");
await consent.revoke("analytics");
consent.state();

Grant and revoke also track sp.consent.granted and sp.consent.revoked.

Utilities

Function Description
getAnonymousId() Returns the local anonymous id in direct mode; returns null in edge mode.
reset() Clears local anonymous id, consent cache, and initialization state.