Integration job verification
Verify the one integration job your application test exercised, product events or feature flags, without needing a test error, trace, log or replay.
On this page
After your agent wires Anectico into an application and runs a controlled test, it asks one question about the job it integrated: did the selected events, or the selected flag exposures, arrive as expected? Integration job verification answers that question and nothing else. A product-event integration needs no test error, trace, log or session replay to verify, and a verified job says nothing about those signals either.
It is a read: it sends nothing, changes nothing, stores no report, runs no model and is never rerun for you. Your agent decides when to call it and when to retry.
| Surface | How |
|---|---|
| REST | POST /api/v1/instrumentation/verify with the JSON request as the body |
| MCP | execute_read_action with action verify_integration and the JSON request as arguments |
| CLI | anectico doctor integration --file request.json (plus the usual --project and --json) |
Project-bound keys may omit project_id; naming a different project is refused. The CLI refuses a
file whose project_id contradicts --project.
Jobs
job selects exactly one job, and each job accepts only its own fields:
job |
Fields | Checked by |
|---|---|---|
product_events |
samples, optional tracking_plan_id + tracking_plan_revision |
Integration event verification |
flags |
exposures, optional compare_local_remote |
Feature-flag exposure verification |
Every request also carries start_time and end_time: the event-time window [start, end),
millisecond precision, at most one hour. The selection fields have exactly the meaning, bounds and
presence rules of the read that checks them, so session_id: "" still checks for an empty session
and an omitted session_id skips the check.
These are refused with 400 before anything is read: a missing or unknown job; a field of the other
job (for example exposures on a product_events request, even as an empty list); an unknown
field; a field given twice; a body org_id; and, for the flags job, an exposure whose
expected_evaluation_status is not evaluated or whose expected_is_fallback is not false.
The flags job verifies successful application use; to verify that your application recorded a
fallback or an evaluation error, use feature-flag exposure verification directly.
errors, traces_logs, replay, surveys and experiments are not jobs here yet and are refused
by name. For an error or trace story, use anectico doctor instrumentation as described in
verify your setup.
Product events
{
"project_id": "2d3c9f8e-1a1b-4c5d-9e0f-123456789abc",
"job": "product_events",
"start_time": "2026-10-15T09:00:00Z",
"end_time": "2026-10-15T10:00:00Z",
"tracking_plan_id": "c0ffee00-1234-4abc-8def-0123456789ab",
"tracking_plan_revision": "3",
"samples": [
{
"event": "checkout_completed",
"message_id": "test-run-42-checkout",
"source_id": "7f1e2d3c-4b5a-4968-8776-5544332211aa",
"distinct_id": "test-person-42",
"session_id": "test-session-42"
}
]
}
Feature flags
{
"job": "flags",
"start_time": "2026-10-15T09:00:00Z",
"end_time": "2026-10-15T10:00:00Z",
"compare_local_remote": false,
"exposures": [
{
"message_id": "test-run-42-flag",
"source_id": "7f1e2d3c-4b5a-4968-8776-5544332211aa",
"flag_key": "checkout_v2",
"expected_value": true,
"expected_flag_version": "2026-10-15T09:30:12.123456Z",
"evaluation_mode": "remote",
"test_case_id": "case-42",
"expected_evaluation_status": "evaluated",
"expected_is_fallback": false,
"expected_input_context": { "basis": "explicit_inputs", "required_inputs_present": true },
"distinct_id": "test-person-42"
}
]
}
compare_local_remote defaults to false: the answer then makes no claim that local and remote
evaluation agree, and carries no comparison. With true, each flag key and test case must have one
local and one remote exposure that are comparable and record the same value.
The answer
| Field | Meaning |
|---|---|
verdict |
verified_sample when every required check is observed_match; failed when a required check is mismatch or not_observed; undetermined otherwise. |
checks |
The required checks in the job's order, each with check, required, state, reason and a selector (index into your samples or exposures, and the event, message_id, source_id, flag_key, test_case_id and property key it is about). |
population |
Counts of your selected samples or exposures: requested, observed, not_observed, ambiguous, undetermined, verified, failed. basis is explicitly_selected_application_samples. |
start_time, end_time |
The window as requested. |
effective_start_time, effective_end_time |
The window the read actually covered after readable history; empty when nothing was read. |
checked_at |
When the answer was released. |
sources |
The read behind the job (product_events or flag_exposures) with its read_cutoff, start and finish clocks, limit_reached and limit_reason. |
upstream_completeness |
Always unknown. |
limitations |
Plain statements of what this answer does not establish. |
links |
Always empty: there is no proof page for this read. |
evidence |
The full answer of the read that checked the job, under product_events or flags, exactly as that read returns it. |
The check names are:
product_events:tracking_plan(when a plan is pinned), then per sampleevent,distinct_id,session_id(when requested),groups(the account, when requested),identity,plan_declaration(with a plan) and onepropertyper declared or requested key, thenread_budget.flags: per exposureevent,distinct_id,session_id(when requested),identity,flag_key,value,definition_version,evaluation_mode,test_case_id,evaluation_status,is_fallback,input_basis,required_inputs_present,input_binding; withcompare_local_remote, onelocal_remote_comparisonper flag key and test case; thenread_budget.
States keep the meaning of the read that produced them: observed_match, mismatch,
not_observed, withheld, unavailable, ambiguous, and not_comparable for a comparison
that could not be established (its reason is the first reason the flag read gave, such as
missing_inputs). read_budget is unavailable with a limit_… reason when the read reached a
limit. A withheld, unavailable, ambiguous or not-comparable required check never verifies, and an
observed event survives as its own fact next to it.
Scopes
| Scope | What it needs it for |
|---|---|
query:read |
Required for every job. (MCP also needs mcp:read.) |
analytics:read |
Required for both jobs. |
flags:read |
Required for the flags job. |
persons:read |
The identity check. Without it identity is withheld. |
tracking_plans:read |
The tracking_plan and plan_declaration checks. Without them they are withheld. |
agents:content:read |
property checks and the recorded flag fields. Without it they are withheld. |
A job's required scope that is missing refuses the request (403) before anything is read. If a scope, the project's content policy or the selected data changes while the read runs, no answer is released: the call fails and you start a fresh read.
Limits
The window is at most one hour, with at most 20 samples or exposures and at most 20 property
checks per sample. Every other limit is the limit of the read behind the job and is returned under
evidence. The whole call stops after 20 seconds. Normal rate limits and agent budgets apply;
there is no separate fee.
Two limits of the shared product-event read are listed in every answer's limitations:
- The read is not narrowed by date: it reads every retained event of the selected names in the
project, so a project with millions of them can reach a read limit and report
unavailablefor an event that exists. Select fewer distinct event names per call in that case. - Two sources that send the same event name, timestamp, distinct ID and message ID keep one stored
event, so verifying one source can report
not_observedbecause the other source's copy was kept. Use a uniquemessage_idper source in tests.
Retrying
A call that fails with 429 (another verification for the project is running), 409 or 503 released
no answer; retry the same request shortly. A not_observed or unavailable check right after
the test usually means the event has not been read yet: wait briefly and verify again with the same
request. Do not resend events to make them appear.
What a verified sample does not prove
- That other events, flags, people or application paths are instrumented: only the selected samples of this one job were checked.
- That errors, traces, logs or replay work: they were neither required nor checked.
- That everything the application emitted arrived: SDK configuration, sampling and client-side loss are not observed, and an accepted intake request is not readable evidence.
- That a person saw anything, or that the application authenticated its own inputs.
The stages between your code and stored evidence, how long to wait before trusting a negative answer, and what has and has not been run are on What integration verification proves and what it does not.