Outcome
Validate and enqueue a batch, returning per-event acceptance, duplicate and rejection outcomes.
Before you start
- Verify the exact identity, environment and approved organization with topolo whoami before discovering this action.
- Inspect the credential-scoped action and canonical resource discovery. Examples use synthetic resources; replace their handles only with separately verified targets.
- Validate the complete input and review the action plan. Mutation and organization-inventory actions require explicit confirmation; a successful shape check does not prove a runtime grant.
- Select a granted workspace through topolo resources. The CLI/MCP supplies canonical resource headers; workspace identity is not an arbitrary payload field.
Discover the target
topolo whoami --json
topolo apps get topolo-insights --json
topolo actions get app_topolo_insights.events.ingest --detail --json
topolo resources topolo-insights --json
Request
POST /api/events/batch · permission workspace:write · confirmation required.
Inspect schemas and test this action in the signed action console. The complete field schema is also available through CLI/MCP action discovery.
Effects and scope
- Requires workspace:write, read_write access and EVENT_INGESTION_ENABLED true. Register an active source and matching active event name/version/purpose first.
- HTTP 202 means at least one event was accepted for queueing; rejected entries can appear alongside it. HTTP 200 can mean only duplicates or rejections. Neither status alone means all events succeeded.
- Event ids deduplicate within organization/workspace; matching ids do not prove equal event payloads. Receipt state, queue delivery, archive writing and processing are separate stages.
- OccurredAt must be within the last 30 days and no more than five minutes ahead. Properties must have bounded scalar values and no disallowed personal-data keys/patterns; handler checks extend the published JSON input shape.
- Analytics events require a decisionReference. The handler checks its presence, not a live consent decision or external authority.
- Queue send failure releases queued receipts and can return an unstructured HTTP 500. Local queue tests explicitly run the real processing handler; they do not prove hosted queue delivery. Processing writes compressed R2 archives and Analytics Engine before marking activity/receipts processed.
Interpret the response
- Inspect every accepted, duplicates and rejected entry; preserve original event ids. Rejection codes are inside a successful response, not exclusively top-level HTTP errors.
- Use analytics.overview dataHealth to distinguish queued from processed; acceptance is not an analytics count, archive proof or delivery receipt.
- Check invalid_batch, occurred_at_out_of_range, properties_not_allowed, source_not_registered, event_not_registered, purpose_mismatch and consent_reference_required. Correct the specific cause before retrying a rejected event.
Worked requests and responses
Actual exported Insights worker, all nine migrated D1 scripts and isolated local R2. Auth identity/grants/archive transport, queue delivery and Analytics Engine sink are synthetic; local queue processing is called explicitly. Notify and Nexus are disabled. No live website, provider, paid usage or deployed data erasure is tested. Successful and refused captures below are separate examples. A refused response is a boundary, not a successful task.
workflow · refusal 1 · HTTP 503
Actual exported Insights worker, all nine migrated D1 scripts and isolated local R2. Auth identity/grants/archive transport, queue delivery and Analytics Engine sink are synthetic; local queue processing is called explicitly. Notify and Nexus are disabled. No live website, provider, paid usage or deployed data erasure is tested. Selected synthetic workspace: ws_insights_workflow. Fixture handles authorize no live request.
Input · payload.json:
{
"schemaVersion": 1,
"events": [
{
"eventId": "event_disabled_001",
"eventName": "page.viewed",
"source": "marketing-site",
"occurredAt": "2026-10-04T08:52:48.558Z",
"purpose": "analytics",
"authority": "first_party",
"consent": {
"capturedAt": "2026-10-04T08:52:48.558Z",
"decisionReference": "consent_fixture_001"
},
"subjectToken": "subject_fixture_001",
"sessionToken": "session_fixture_001",
"properties": {
"page": "/pricing",
"country": "GB",
"app_id": "app_example"
}
}
]
}
CLI:
topolo actions validate app_topolo_insights.events.ingest --data @payload.json --json
topolo actions plan app_topolo_insights.events.ingest --data @payload.json --confirm --json
Captured raw Worker result
{
"error": "ingestion_disabled"
}
HTTP 503. Ingestion is disabled in this runtime. Use the authorized service owner to resolve configuration; do not loop or send to another environment.
CLI/MCP adds its transport envelope; CLI --detail retains the full action result.
workflow · success 2 · HTTP 202
Actual exported Insights worker, all nine migrated D1 scripts and isolated local R2. Auth identity/grants/archive transport, queue delivery and Analytics Engine sink are synthetic; local queue processing is called explicitly. Notify and Nexus are disabled. No live website, provider, paid usage or deployed data erasure is tested. Selected synthetic workspace: ws_insights_workflow. Fixture handles authorize no live request.
Input · payload.json:
{
"schemaVersion": 1,
"events": [
{
"eventId": "event_success_001",
"eventName": "page.viewed",
"source": "marketing-site",
"occurredAt": "2026-10-04T08:52:48.558Z",
"purpose": "analytics",
"authority": "first_party",
"consent": {
"capturedAt": "2026-10-04T08:52:48.558Z",
"decisionReference": "consent_fixture_001"
},
"subjectToken": "subject_fixture_001",
"sessionToken": "session_fixture_001",
"properties": {
"page": "/pricing",
"country": "GB",
"app_id": "app_example"
}
},
{
"eventId": "event_unregistered_001",
"eventName": "page.viewed",
"source": "missing-site",
"occurredAt": "2026-10-04T08:52:48.558Z",
"purpose": "analytics",
"authority": "first_party",
"consent": {
"capturedAt": "2026-10-04T08:52:48.558Z",
"decisionReference": "consent_fixture_001"
},
"subjectToken": "subject_fixture_001",
"sessionToken": "session_fixture_001",
"properties": {
"page": "/pricing",
"country": "GB",
"app_id": "app_example"
}
},
{
"eventId": "event_undefined_001",
"eventName": "unknown.event",
"source": "marketing-site",
"occurredAt": "2026-10-04T08:52:48.558Z",
"purpose": "analytics",
"authority": "first_party",
"consent": {
"capturedAt": "2026-10-04T08:52:48.558Z",
"decisionReference": "consent_fixture_001"
},
"subjectToken": "subject_fixture_001",
"sessionToken": "session_fixture_001",
"properties": {
"page": "/pricing",
"country": "GB",
"app_id": "app_example"
}
},
{
"eventId": "event_no_consent_001",
"eventName": "page.viewed",
"source": "marketing-site",
"occurredAt": "2026-10-04T08:52:48.558Z",
"purpose": "analytics",
"authority": "first_party",
"consent": {
"capturedAt": "2026-10-04T08:52:48.558Z"
},
"subjectToken": "subject_fixture_001",
"sessionToken": "session_fixture_001",
"properties": {
"page": "/pricing",
"country": "GB",
"app_id": "app_example"
}
},
{
"eventId": "event_purpose_mismatch_001",
"eventName": "page.viewed",
"source": "marketing-site",
"occurredAt": "2026-10-04T08:52:48.558Z",
"purpose": "necessary",
"authority": "first_party",
"consent": {
"capturedAt": "2026-10-04T08:52:48.558Z",
"decisionReference": "consent_fixture_001"
},
"subjectToken": "subject_fixture_001",
"sessionToken": "session_fixture_001",
"properties": {
"page": "/pricing",
"country": "GB",
"app_id": "app_example"
}
},
{
"eventId": "event_private_property_001",
"eventName": "page.viewed",
"source": "marketing-site",
"occurredAt": "2026-10-04T08:52:48.558Z",
"purpose": "analytics",
"authority": "first_party",
"consent": {
"capturedAt": "2026-10-04T08:52:48.558Z",
"decisionReference": "consent_fixture_001"
},
"subjectToken": "subject_fixture_001",
"sessionToken": "session_fixture_001",
"properties": {
"email": "synthetic@example.com"
}
},
{
"eventId": "event_stale_001",
"eventName": "page.viewed",
"source": "marketing-site",
"occurredAt": "2020-01-01T00:00:00.000Z",
"purpose": "analytics",
"authority": "first_party",
"consent": {
"capturedAt": "2026-10-04T08:52:48.558Z",
"decisionReference": "consent_fixture_001"
},
"subjectToken": "subject_fixture_001",
"sessionToken": "session_fixture_001",
"properties": {
"page": "/pricing",
"country": "GB",
"app_id": "app_example"
}
}
]
}
CLI:
topolo actions validate app_topolo_insights.events.ingest --data @payload.json --json
topolo actions plan app_topolo_insights.events.ingest --data @payload.json --confirm --json
topolo actions call app_topolo_insights.events.ingest --data @payload.json --confirm --detail --json
MCP tool and arguments:
{
"tool": "topolo_call_action",
"arguments": {
"actionId": "app_topolo_insights.events.ingest",
"input": {
"schemaVersion": 1,
"events": [
{
"eventId": "event_success_001",
"eventName": "page.viewed",
"source": "marketing-site",
"occurredAt": "2026-10-04T08:52:48.558Z",
"purpose": "analytics",
"authority": "first_party",
"consent": {
"capturedAt": "2026-10-04T08:52:48.558Z",
"decisionReference": "consent_fixture_001"
},
"subjectToken": "subject_fixture_001",
"sessionToken": "session_fixture_001",
"properties": {
"page": "/pricing",
"country": "GB",
"app_id": "app_example"
}
},
{
"eventId": "event_unregistered_001",
"eventName": "page.viewed",
"source": "missing-site",
"occurredAt": "2026-10-04T08:52:48.558Z",
"purpose": "analytics",
"authority": "first_party",
"consent": {
"capturedAt": "2026-10-04T08:52:48.558Z",
"decisionReference": "consent_fixture_001"
},
"subjectToken": "subject_fixture_001",
"sessionToken": "session_fixture_001",
"properties": {
"page": "/pricing",
"country": "GB",
"app_id": "app_example"
}
},
{
"eventId": "event_undefined_001",
"eventName": "unknown.event",
"source": "marketing-site",
"occurredAt": "2026-10-04T08:52:48.558Z",
"purpose": "analytics",
"authority": "first_party",
"consent": {
"capturedAt": "2026-10-04T08:52:48.558Z",
"decisionReference": "consent_fixture_001"
},
"subjectToken": "subject_fixture_001",
"sessionToken": "session_fixture_001",
"properties": {
"page": "/pricing",
"country": "GB",
"app_id": "app_example"
}
},
{
"eventId": "event_no_consent_001",
"eventName": "page.viewed",
"source": "marketing-site",
"occurredAt": "2026-10-04T08:52:48.558Z",
"purpose": "analytics",
"authority": "first_party",
"consent": {
"capturedAt": "2026-10-04T08:52:48.558Z"
},
"subjectToken": "subject_fixture_001",
"sessionToken": "session_fixture_001",
"properties": {
"page": "/pricing",
"country": "GB",
"app_id": "app_example"
}
},
{
"eventId": "event_purpose_mismatch_001",
"eventName": "page.viewed",
"source": "marketing-site",
"occurredAt": "2026-10-04T08:52:48.558Z",
"purpose": "necessary",
"authority": "first_party",
"consent": {
"capturedAt": "2026-10-04T08:52:48.558Z",
"decisionReference": "consent_fixture_001"
},
"subjectToken": "subject_fixture_001",
"sessionToken": "session_fixture_001",
"properties": {
"page": "/pricing",
"country": "GB",
"app_id": "app_example"
}
},
{
"eventId": "event_private_property_001",
"eventName": "page.viewed",
"source": "marketing-site",
"occurredAt": "2026-10-04T08:52:48.558Z",
"purpose": "analytics",
"authority": "first_party",
"consent": {
"capturedAt": "2026-10-04T08:52:48.558Z",
"decisionReference": "consent_fixture_001"
},
"subjectToken": "subject_fixture_001",
"sessionToken": "session_fixture_001",
"properties": {
"email": "synthetic@example.com"
}
},
{
"eventId": "event_stale_001",
"eventName": "page.viewed",
"source": "marketing-site",
"occurredAt": "2020-01-01T00:00:00.000Z",
"purpose": "analytics",
"authority": "first_party",
"consent": {
"capturedAt": "2026-10-04T08:52:48.558Z",
"decisionReference": "consent_fixture_001"
},
"subjectToken": "subject_fixture_001",
"sessionToken": "session_fixture_001",
"properties": {
"page": "/pricing",
"country": "GB",
"app_id": "app_example"
}
}
]
},
"confirm": true
}
}
Captured raw Worker result
{
"data": {
"accepted": [
"event_success_001"
],
"duplicates": [],
"rejected": [
{
"eventId": "event_unregistered_001",
"code": "source_not_registered"
},
{
"eventId": "event_undefined_001",
"code": "event_not_registered"
},
{
"eventId": "event_no_consent_001",
"code": "consent_reference_required"
},
{
"eventId": "event_purpose_mismatch_001",
"code": "purpose_mismatch"
},
{
"eventId": "event_private_property_001",
"code": "properties_not_allowed"
},
{
"eventId": "event_stale_001",
"code": "occurred_at_out_of_range"
}
]
}
}
HTTP 202. Inspect every accepted, duplicates and rejected entry; preserve original event ids. Rejection codes are inside a successful response, not exclusively top-level HTTP errors. Use analytics.overview dataHealth to distinguish queued from processed; acceptance is not an analytics count, archive proof or delivery receipt. Check invalid_batch, occurred_at_out_of_range, properties_not_allowed, source_not_registered, event_not_registered, purpose_mismatch and consent_reference_required. Correct the specific cause before retrying a rejected event.
CLI/MCP adds its transport envelope; CLI --detail retains the full action result.
workflow · success 3 · HTTP 200
Actual exported Insights worker, all nine migrated D1 scripts and isolated local R2. Auth identity/grants/archive transport, queue delivery and Analytics Engine sink are synthetic; local queue processing is called explicitly. Notify and Nexus are disabled. No live website, provider, paid usage or deployed data erasure is tested. Selected synthetic workspace: ws_insights_workflow. Fixture handles authorize no live request.
Input · payload.json:
{
"schemaVersion": 1,
"events": [
{
"eventId": "event_success_001",
"eventName": "page.viewed",
"source": "marketing-site",
"occurredAt": "2026-10-04T08:52:48.558Z",
"purpose": "analytics",
"authority": "first_party",
"consent": {
"capturedAt": "2026-10-04T08:52:48.558Z",
"decisionReference": "consent_fixture_001"
},
"subjectToken": "subject_fixture_001",
"sessionToken": "session_fixture_001",
"properties": {
"page": "/pricing",
"country": "GB",
"app_id": "app_example"
}
}
]
}
CLI:
topolo actions validate app_topolo_insights.events.ingest --data @payload.json --json
topolo actions plan app_topolo_insights.events.ingest --data @payload.json --confirm --json
topolo actions call app_topolo_insights.events.ingest --data @payload.json --confirm --detail --json
MCP tool and arguments:
{
"tool": "topolo_call_action",
"arguments": {
"actionId": "app_topolo_insights.events.ingest",
"input": {
"schemaVersion": 1,
"events": [
{
"eventId": "event_success_001",
"eventName": "page.viewed",
"source": "marketing-site",
"occurredAt": "2026-10-04T08:52:48.558Z",
"purpose": "analytics",
"authority": "first_party",
"consent": {
"capturedAt": "2026-10-04T08:52:48.558Z",
"decisionReference": "consent_fixture_001"
},
"subjectToken": "subject_fixture_001",
"sessionToken": "session_fixture_001",
"properties": {
"page": "/pricing",
"country": "GB",
"app_id": "app_example"
}
}
]
},
"confirm": true
}
}
Captured raw Worker result
{
"data": {
"accepted": [],
"duplicates": [
"event_success_001"
],
"rejected": []
}
}
HTTP 200. Inspect every accepted, duplicates and rejected entry; preserve original event ids. Rejection codes are inside a successful response, not exclusively top-level HTTP errors. Use analytics.overview dataHealth to distinguish queued from processed; acceptance is not an analytics count, archive proof or delivery receipt. Check invalid_batch, occurred_at_out_of_range, properties_not_allowed, source_not_registered, event_not_registered, purpose_mismatch and consent_reference_required. Correct the specific cause before retrying a rejected event.
CLI/MCP adds its transport envelope; CLI --detail retains the full action result.
workflow · refusal 4 · HTTP 500
Actual exported Insights worker, all nine migrated D1 scripts and isolated local R2. Auth identity/grants/archive transport, queue delivery and Analytics Engine sink are synthetic; local queue processing is called explicitly. Notify and Nexus are disabled. No live website, provider, paid usage or deployed data erasure is tested. Selected synthetic workspace: ws_insights_workflow. Fixture handles authorize no live request.
Input · payload.json:
{
"schemaVersion": 1,
"events": [
{
"eventId": "event_queue_retry_001",
"eventName": "page.viewed",
"source": "marketing-site",
"occurredAt": "2026-10-04T08:52:48.558Z",
"purpose": "analytics",
"authority": "first_party",
"consent": {
"capturedAt": "2026-10-04T08:52:48.558Z",
"decisionReference": "consent_fixture_001"
},
"subjectToken": "subject_fixture_001",
"sessionToken": "session_fixture_001",
"properties": {
"page": "/pricing",
"country": "GB",
"app_id": "app_example"
}
}
]
}
CLI:
topolo actions validate app_topolo_insights.events.ingest --data @payload.json --json
topolo actions plan app_topolo_insights.events.ingest --data @payload.json --confirm --json
Captured raw Worker result
Internal Server Error
HTTP 500. The observed queue send failure returned unstructured HTTP 500. Preserve request id and original event ids, inspect processing state and resolve queue availability before a bounded retry.
CLI/MCP adds its transport envelope; CLI --detail retains the full action result.
workflow · success 5 · HTTP 202
Actual exported Insights worker, all nine migrated D1 scripts and isolated local R2. Auth identity/grants/archive transport, queue delivery and Analytics Engine sink are synthetic; local queue processing is called explicitly. Notify and Nexus are disabled. No live website, provider, paid usage or deployed data erasure is tested. Selected synthetic workspace: ws_insights_workflow. Fixture handles authorize no live request.
Input · payload.json:
{
"schemaVersion": 1,
"events": [
{
"eventId": "event_queue_retry_001",
"eventName": "page.viewed",
"source": "marketing-site",
"occurredAt": "2026-10-04T08:52:48.558Z",
"purpose": "analytics",
"authority": "first_party",
"consent": {
"capturedAt": "2026-10-04T08:52:48.558Z",
"decisionReference": "consent_fixture_001"
},
"subjectToken": "subject_fixture_001",
"sessionToken": "session_fixture_001",
"properties": {
"page": "/pricing",
"country": "GB",
"app_id": "app_example"
}
}
]
}
CLI:
topolo actions validate app_topolo_insights.events.ingest --data @payload.json --json
topolo actions plan app_topolo_insights.events.ingest --data @payload.json --confirm --json
topolo actions call app_topolo_insights.events.ingest --data @payload.json --confirm --detail --json
MCP tool and arguments:
{
"tool": "topolo_call_action",
"arguments": {
"actionId": "app_topolo_insights.events.ingest",
"input": {
"schemaVersion": 1,
"events": [
{
"eventId": "event_queue_retry_001",
"eventName": "page.viewed",
"source": "marketing-site",
"occurredAt": "2026-10-04T08:52:48.558Z",
"purpose": "analytics",
"authority": "first_party",
"consent": {
"capturedAt": "2026-10-04T08:52:48.558Z",
"decisionReference": "consent_fixture_001"
},
"subjectToken": "subject_fixture_001",
"sessionToken": "session_fixture_001",
"properties": {
"page": "/pricing",
"country": "GB",
"app_id": "app_example"
}
}
]
},
"confirm": true
}
}
Captured raw Worker result
{
"data": {
"accepted": [
"event_queue_retry_001"
],
"duplicates": [],
"rejected": []
}
}
HTTP 202. Inspect every accepted, duplicates and rejected entry; preserve original event ids. Rejection codes are inside a successful response, not exclusively top-level HTTP errors. Use analytics.overview dataHealth to distinguish queued from processed; acceptance is not an analytics count, archive proof or delivery receipt. Check invalid_batch, occurred_at_out_of_range, properties_not_allowed, source_not_registered, event_not_registered, purpose_mismatch and consent_reference_required. Correct the specific cause before retrying a rejected event.
CLI/MCP adds its transport envelope; CLI --detail retains the full action result.
permission-refusals · refusal 6 · HTTP 403
Actual exported Insights worker, all nine migrated D1 scripts and isolated local R2. Auth identity/grants/archive transport, queue delivery and Analytics Engine sink are synthetic; local queue processing is called explicitly. Notify and Nexus are disabled. No live website, provider, paid usage or deployed data erasure is tested. Selected synthetic workspace: ws_insights_merge. Fixture handles authorize no live request.
Input · payload.json:
{
"schemaVersion": 1,
"events": [
{
"eventId": "evt_0001_newsletter",
"eventName": "newsletter.subscribed",
"source": "marketing-site",
"occurredAt": "2026-08-11T09:30:00.000Z",
"purpose": "analytics",
"authority": "first_party",
"consent": {
"decisionReference": "consent_decision_0001",
"capturedAt": "2026-08-11T09:29:58.000Z"
},
"properties": {
"plan": "starter",
"referrer": "newsletter-footer"
}
}
]
}
CLI:
topolo actions validate app_topolo_insights.events.ingest --data @payload.json --json
topolo actions plan app_topolo_insights.events.ingest --data @payload.json --confirm --json
Captured raw Worker result
{
"success": false,
"error": {
"code": "action_permission_required",
"message": "Action \"events.ingest\" requires permission \"workspace:write\".",
"action": "events.ingest"
}
}
HTTP 403. The credential lacks the declared action permission. Inspect credential-scoped discovery in the same approved context; workspace role does not replace the permission.
CLI/MCP adds its transport envelope; CLI --detail retains the full action result.
Verify the outcome
- Inspect every accepted, duplicates and rejected entry; preserve original event ids. Rejection codes are inside a successful response, not exclusively top-level HTTP errors.
- Use analytics.overview dataHealth to distinguish queued from processed; acceptance is not an analytics count, archive proof or delivery receipt.
- Check invalid_batch, occurred_at_out_of_range, properties_not_allowed, source_not_registered, event_not_registered, purpose_mismatch and consent_reference_required. Correct the specific cause before retrying a rejected event.
Errors and recovery
ingestion_disabled(HTTP 503): Ingestion is disabled in this runtime. Use the authorized service owner to resolve configuration; do not loop or send to another environment.Internal Server Error(HTTP 500): The observed queue send failure returned unstructured HTTP 500. Preserve request id and original event ids, inspect processing state and resolve queue availability before a bounded retry.action_permission_required(HTTP 403): The credential lacks the declared action permission. Inspect credential-scoped discovery in the same approved context; workspace role does not replace the permission.
Only observed refusals are listed. This does not promise that external dependencies or every invalid payload have been exhaustively exercised.
Recovery boundary
No per-event removal action is published. After an uncertain send, inspect receipts indirectly through analytics and repeat the same intended ids only under the existing deduplication boundary; changing ids can duplicate activity. Organization erasure is broad and irreversible, not an ingestion rollback.
Related actions
- app_topolo_insights.analytics.overview: Read workspace-scoped analytics with a bounded time window, optional source and dimension drill-downs.
- app_topolo_insights.sources.list: List the selected workspace’s registered analytics sources.
- app_topolo_insights.event_definitions.list: Read the selected workspace’s governed event name/version/purpose declarations.
Evidence and limits
Actual exported Insights worker, all nine migrated D1 scripts and isolated local R2. Auth identity/grants/archive transport, queue delivery and Analytics Engine sink are synthetic; local queue processing is called explicitly. Notify and Nexus are disabled. No live website, provider, paid usage or deployed data erasure is tested.