# Plan and prove product events

> Have your agent choose events from your code, send them with stable names, identities and message IDs through an anonymous visit, a login, a logout and a second person, and prove the exact samples arrived.

Canonical page: https://anectico.com/docs/instrument/product-analytics/


Product events record what people do in your application: a checkout completed, a plan upgraded, a
job finished. This page is the instrumentation workflow for your agent. It chooses the events from
your code, sends them with identifiers a test can find again, and asks Anectico whether the exact
samples of a controlled run arrived under the right person and against your tracking plan. The
mechanics of one call, sampling declarations and capture quality are in [Capture product
events](/docs/instrument/diagnostic-events); measuring the events later is in [Measure product
events](/docs/investigate/product-analytics).

## What this covers

| Path | Covered |
| --- | --- |
| Node.js server with the Anectico JavaScript SDK | Yes |
| A server in any language, over HTTP with no SDK | One tested recipe. See [Integrate without an SDK](/docs/instrument/without-an-sdk). |
| Browser, Go, Python and mobile applications | Not by this workflow. Their SDK guides still apply. |

## Ask your agent

> Find where checkout completes in my service and send a `checkout_completed` event with the cart
> value. Link a login to the earlier anonymous visit, record the account, and run it as two people.
> Then verify the exact events against tracking plan revision 3.

| Job | MCP tool or action | CLI command |
| --- | --- | --- |
| Read the pinned tracking-plan revision | `get_tracking_plan` | `anectico analytics tracking-plans get` |
| Verify the exact events, in one verdict | `verify_integration` | `anectico doctor integration` |
| Verify the exact events, with the full per-event answer | `verify_integration_events` | `anectico doctor events` |

Sending the events is your application's job, with a `server_capture` key that holds only
`analytics:write`. See [Native intake keys](/docs/instrument/native-intake-keys).

## Choose the events from your code

Your agent reads the code and picks the lines where something meaningful really happens: the handler
that completes a checkout, not the button that starts it. A good event has:

- a stable name that describes the occurrence in the past tense, such as `checkout_completed`, at
  most 200 bytes, with no identifier or personal data in the name;
- property names your team will still understand next year, and values that are not personal data.
  Email addresses and similar values are redacted on arrival. See [What Anectico redacts and
  withholds](/docs/reference/redaction);
- one name per kind of occurrence, so a count of the name means something.

If the project already has a tracking plan, the agent reads the revision it will pin and uses its
event and property names. It does not change the plan without your approval; a plan is configuration
managed as described in [Manage tracking plans](/docs/investigate/tracking-plans), and a recipe can
start one ([start from a recipe](/docs/investigate/tracking-plans#start-from-a-recipe)).

## One person through the journey

Identity comes from the server's own session, never from the request body or a header the browser
can set. A Node server answers many people at once, so the example builds one identity and one event
client per request. One shared client would put one person's events on another person.

| Moment | What the application sends | What to expect |
| --- | --- | --- |
| Anonymous visit | Events under the visitor ID from the server's session | An anonymous person |
| Login | `identify` with the stable user ID, the earlier visitor ID as the alias, and the person's properties; then the account with `group` | The earlier anonymous events join the person |
| Business action | The event, with the account recorded at event time | The event belongs to the identified person |
| Logout | The next visit gets a new visitor ID | A new anonymous person, not the one who logged out |
| Second person | The same steps under another user ID and account | A second person; the two never merge |

Use an application-owned ID such as a database user ID, not an email address. See [Identify
customers](/docs/instrument/identity).

## Message IDs, sessions and sources

- **Message ID.** Choose it before the first send and derive it from something the test controls,
  such as the request ID plus the event's purpose. An ID the application cannot reproduce selects
  nothing. A retry keeps the same ID and the same content; reusing an ID with different content
  refuses that event.
- **Session.** Send a `session_id` when the application owns the browsing session. The Node SDK's
  server events carry one session for the whole process rather than one per request, so the Node
  example declares no session and its verification checks none.
- **Source.** The admitting source comes from the capture key, never from the event. Verification
  selects an event by the source that admitted it, so the agent keeps the capture key's source ID.
- **Control events.** `identify` and `$groupidentify` update the person and the account and store no
  event row. They have no message ID a read can select, so their outcome is the acknowledgement or
  the client's drain. Verify the events that follow them.

## What acceptance means

Every capture answers with an acknowledgement per event: queued, rejected or uncertain. Queued means
the event was accepted for processing. It does not mean it is stored, readable, or that an identity
update has finished. A rejection is final. Uncertain events are retried a bounded number of times
with the same bytes and the same message ID. See [Capture
acknowledgements](/docs/reference/rest-api#capture-acknowledgements).

## Prove the exact sample

The agent selects only the samples whose delivery was queued: each with the event name, message ID,
the capture key's source ID and the declared identity, and optionally the session. With a pinned
revision it adds the plan ID and the exact revision; without a plan it can ask for named property
checks of type and presence. Then:

```bash
anectico doctor integration --file verify-job.json -o json
```

The request and answer are in [Integration job
verification](/docs/reference/integration-job-verification) and, for every field of the underlying
read, [Integration event verification](/docs/reference/integration-verification). A verified sample
means each selected event was stored once under that name, message ID and source, the declared
identity matched and resolved to a person (with `persons:read`), and the plan revision declares the
event with matching property types (with `tracking_plans:read` and `agents:content:read`). The
answer never returns a property value.

It does not mean other events, other people or other code paths are instrumented, and another
person's correct events cannot satisfy your sample. An early negative answer is usually delay.
Waiting, the bounds and two known gaps are on [What integration verification proves and what it does
not](/docs/reference/integration-verification-limits).

## Test traffic is ordinary traffic

Anectico does not yet reserve a population for verification traffic. Test events count in the
project's measurements and event allocation, and are retained, exported and erased like any other
event. Use clearly synthetic identities such as `anectico-verification-user-a`, tell the person who
owns the project, and never erase a person only to tidy up. A hand-sent event from the command line
is a pipe check, not evidence that your application sends anything.

## Not covered yet

Browser and mobile events, Go and Python servers, errors, traces, logs, replay, surveys and product
experiments. See [Ask your agent to integrate Anectico](/docs/start/ask-your-agent-to-integrate).

## Next

- [Feature flags in your application](/docs/instrument/feature-flags)
- [Integrate without an SDK](/docs/instrument/without-an-sdk)
- [Runnable integration examples](/docs/instrument/integration-examples)
