External Monitoring Events
When part of your integration pipeline runs outside epilot โ typically an integration middleware that receives events from an ERP, transforms them, and forwards them to epilot โ you can push that external system's own monitoring events into epilot. This makes the Integration Hub the central monitoring point for the whole pipeline, and lets the Event Trace span both systems: a single business operation is visible as one timeline, from the moment it leaves the ERP, through the middleware, until epilot updates the entity.
Topology
This is for the case where epilot still does the bulk of the work (mapping, entity/metering updates). The middleware does two independent things:
- forwards the inbound data event to the standard inbound endpoint, and
- separately pushes its own monitoring events (its processing steps) to the endpoint below.
The two halves are linked into one trace by a shared correlation_id.
How the trace is linkedโ
Each monitoring event is a span. Spans are grouped into a trace by correlation_id โ the id of one business operation. To get an end-to-end trace, your middleware mints one unique correlation_id per operation and uses the same value in both places:
- as
correlation_idon every span it pushes toPOST โฆ/monitoring/external-events, and - as the per-event
correlation_idon the event it forwards to the inbound endpoint (see Correlating the forwarded event).
epilot propagates that correlation_id across all of its own processing spans automatically, so both systems' spans end up under the same trace.
Pushing spansโ
POST /v2/integrations/{integrationId}/monitoring/external-events
Requires the integration:manage permission. The organization is derived from your token; the integration is validated to belong to it. Send a batch of spans (max 100):
{
"events": [
{
"correlation_id": "bp-8f3a2c", // REQUIRED โ the trace id (unique per business operation)
"level": "success", // REQUIRED โ success | error | warning | info
"use_case_slug": "business_partner", // REQUIRED โ a use case configured on the integration
"occurred_at": "2026-07-20T09:00:00.120Z", // REQUIRED โ ISO 8601; the step's time
"message": "Received BusinessPartnerUpdate from SAP IDoc 4711", // REQUIRED โ human-readable
"detail": { "step": "ingest", "erp_doc_id": "4711" } // optional โ free-form context
}
]
}
The span contractโ
| Field | Required | Notes |
|---|---|---|
correlation_id | yes | The trace id. Use one unique value per business operation, shared with the forwarded event. |
level | yes | success | error | warning | info. Drives coloring, stats, and alerting. |
use_case_slug | yes | A use case configured on the integration. Resolved to the internal use case; unknown slugs are still accepted and grouped under the raw slug. |
occurred_at | yes | ISO 8601 timestamp of when the step happened (external clock). |
message | yes | Shown in the trace and event tables. |
detail | no | Free-form JSON for step-specific context (step, http_status, reason, โฆ). |
You do not send a code: epilot assigns a deterministic, level-derived code (EXTERNAL_ERROR / EXTERNAL_WARNING / EXTERNAL_INFO / EXTERNAL_SUCCESS). This marks the span as external in every breakdown and cannot be confused with epilot's own codes. Span identity and de-duplication are the external system's responsibility โ epilot ingests each span as sent.
Spans are validated individually; invalid spans are reported per item and do not fail the rest of the batch:
// 202
{ "accepted": 2, "rejected": 1, "results": [ { "index": 2, "status": "rejected", "reason": "missing correlation_id" } ] }
Accepted spans flow onto the same monitoring stream as epilot's own events, so they show up in the monitoring stats, the event tables, the trace, digests, and โ for error spans โ the same real-time and threshold alerts as epilot-produced errors (subject to the integration's notification configuration).
Correlating the forwarded eventโ
So the middleware's spans and epilot's own processing spans land in one trace, put the same id on the event you forward to the inbound endpoint. correlation_id may be supplied per event:
{
"meta": { "correlation_id": "batch-123" }, // batch/transport correlation (default for all events)
"events": [
{ "event_type": "UPDATE", "object_type": "business_partner", "correlation_id": "bp-8f3a2c",
"timestamp": "2026-07-20T09:00:00Z", "format": "json", "payload": "{โฆ}" }
]
}
The per-event correlation_id takes precedence over meta.correlation_id, so a single POST can carry several distinct operations and keep their traces separate. If neither is supplied, epilot mints its own id and the trace is epilot-only (nothing breaks โ you just lose the cross-system linkage).
Reading the cross-system traceโ
GET /v2/integrations/{integrationId}/monitoring/traces/{correlationId}
Requires integration:view. Returns every span sharing the correlation_id โ external and epilot alike โ in chronological order, with a rolled-up status and (when present) the original epilot inbound event as the trace "head":
// 200
{
"correlation_id": "bp-8f3a2c",
"status": "error", // rolled up: error > warning > success > info
"started_at": "2026-07-20T09:00:00.120Z",
"ended_at": "2026-07-20T09:00:03.900Z",
"span_count": 9,
"truncated": false, // true when the trace exceeds the returned window
"spans": [ /* MonitoringEventV2[] โ external spans carry an EXTERNAL_* code */ ],
"inbound_event": { /* the epilot inbound payload, when present */ }
}
External spans are distinguished from epilot spans by their EXTERNAL_* code prefix. This endpoint is the correlation-grouped counterpart to the single-event trace at GET โฆ/monitoring/events/{eventId}/associated.
tip
Keep the correlation_id stable and unique per operation. Reusing one id across unrelated operations merges them into one trace; omitting it on the forwarded event leaves epilot's processing in a separate, epilot-only trace.