Skip to main content
The Ingestion API lets you push data into Causeloop from any source that doesn’t have a managed connector. A single HTTP call turns a raw signal into an Issue that participates in Causeloop’s pattern detection. All ingestion endpoints require a Bearer token with the ingest:write scope. See Authentication.

Endpoints at a glance

Single event

Push one raw signal. Causeloop accepts it (202) and writes it to the event log; the issue projection and any downstream extraction/clustering are processed asynchronously. Poll job_id via GET /v1/ingest/status/{job_id} if you need to confirm processing completed.
Response (202 Accepted)
Per-record validation errors are field-level, not all-or-nothing — a rejected record looks like {"index": 0, "errors": [...]}.

Single event fields

string
default:"custom_api"
The source system identifier. Use a recognized value for richer pattern matching (see Source values). Unknown values are coerced to custom_api.
string
required
Your system’s unique identifier for this event. Used for deduplication — submitting the same external_id + source combination twice updates the existing issue rather than creating a duplicate.
string
required
Short description of the issue.
string
required
ISO-8601 timestamp of when the finding actually occurred (not when it was logged) — the recurrence/hazard model depends on it. If your connector truly cannot supply this, also send occurred_at_source: "logged_at" so the degradation is explicit rather than silently wrong.
string
Full description or error detail. Can be plain text or Markdown. Formerly named body. If engine_eligible is true (the default) and narrative is omitted, the record is still accepted (202) but comes back with engine_status: "blocked_no_narrative" and a review item is auto-created — narrative-less issues never enter clustering.
string
default:"p3"
Priority level: p1 (critical), p2 (high), p3 (medium), p4 (low).
string
Link back to the issue in your source system.
boolean
default:"true"
Whether this issue should enter the clustering/hazard pipeline at all. Set false for signals you want tracked but not analyzed.
object
Optional {prior_flag, interim_patch, prior_fix_refs[]} — prior mitigation attempts on this finding.
string[]
Optional references to affected assets.
object
Optional closed-vocabulary structured attributes (same enums used by AI extraction). Fields populated here with origin: human take precedence over anything extraction would infer.
object
Free-form key/value pairs. Stored as-is and available in the issue detail view.

Batch ingest

Push up to 500 pre-normalized issue records in a single call. Each record is processed independently — a failure on one record does not abort the rest.
Response (202 Accepted)
If any records fail validation, they appear in rejected with an index and field-level errors — the batch is never all-or-nothing:

Batch record fields

IngestRecord[]
required
Array of records. Between 1 and 500 items.
Each IngestRecord:
string
required
Your system’s unique ID. Used for deduplication (with source).
string
required
Source system identifier. See Source values.
string
required
Issue title. 1–300 characters.
string
required
ISO-8601 timestamp of when the finding actually occurred — required; the recurrence/hazard model depends on it. If unavailable, also send occurred_at_source: "logged_at".
string
Full description. Formerly named body. If engine_eligible is true (default) and narrative is omitted, the record is accepted but flagged engine_status: "blocked_no_narrative" and never enters clustering until a narrative is added.
string
default:"p3"
p1 | p2 | p3 | p4
string
Team or squad responsible. Used for grouping in the dashboard.
string
URL back to the source record.
string
ISO 8601 timestamp when the issue was created in the source system.
string
ISO 8601 timestamp of the last update in the source system.
string
Associate this record with an existing connector. Optional.

Source-specific ingest

If you know the source system, use the source-specific endpoint for validated field requirements:
Supported source paths: jira, servicenow, github, pagerduty, opsgenie, linear, zendesk, custom_api.

Checking job status

Batch and source-specific ingestion is asynchronous. Poll the job endpoint to track progress:
Job status values: pendingrunningsucceeded | partial | failed.

Source values

Use recognized source values for the best pattern detection. The following values are supported natively: jira · servicenow · github · pagerduty · opsgenie · linear · zendesk · slack · email · database · custom_api Any other value is accepted but coerced to custom_api.

How ingested records become issues

When Causeloop accepts a record:
  1. The external_id + source pair is checked for duplicates. If a matching issue exists, it is updated.
  2. The record is normalized: missing fields get defaults, severity is validated, and the source is resolved.
  3. Causeloop writes an issue.ingested event to the append-only event log. The issue row itself is a projection of that event stream, rebuilt deterministically — later edits (via PATCH /v1/issues/{id}) append field_updated / attr_corrected events rather than mutating history. You can read the ordered event list at GET /v1/issues/{id}/events.
  4. If engine_eligible is true (default) and a narrative is present, the issue is picked up by extraction and, on the next engine run, by clustering — pattern membership is a run output, not something computed synchronously per-ingest. Issues without a narrative get engine_status: "blocked_no_narrative" and a review item, and never enter clustering until one is added.
Do not parse the JSON body before computing HMAC signatures for inbound webhooks — always sign the raw bytes. See Webhooks for details.

Limits