Skip to main content
method
POST
/signals
Authentication: Required (if enabled) Ingests an ordered sequence of entity–attribute–value triples. Unlike POST /signal, this endpoint calls ingest_sequence() internally, which analyses co-occurrence across adjacent signals: two entities that share the same attribute value in consecutive signals get an edge created (or incremented) between them.
Use this endpoint when you need a connected graph via HTTP. POST /signal inserts isolated nodes — it never creates edges.

Why this endpoint exists

POST /signal calls ingest() — a single-signal path that stores nodes but never creates edges. Edges are the backbone of queries like strongest_path, intersect, and retract. If you ingest signals one-by-one via HTTP, these queries always return found: false. POST /signals fixes this by accepting a batch and delegating to ingest_sequence(), the same function used by the CLI kremis ingest command.

Request Body

{
  "signals": [
    { "entity_id": 1, "attribute": "name", "value": "Alice" },
    { "entity_id": 2, "attribute": "name", "value": "Bob" }
  ]
}
FieldTypeRequiredConstraintsDescription
signalsarrayYesMax 10,000 itemsOrdered list of signals.
signals[].entity_idinteger (u64)YesEntity identifier.
signals[].attributestringYesMax 256 bytes, non-empty, no control charactersAttribute name.
signals[].valuestringYesMax 64 KB, non-empty, no control characters except \n, \r, \tAttribute value.
An empty array {"signals": []} is a valid no-op.

Response

{
  "success": true,
  "ingested": 2,
  "node_ids": [9876543210, 1234567890],
  "error": null
}
FieldTypeDescription
successbooleanWhether all signals were ingested.
ingestedintegerNumber of signals processed.
node_idsarray of integerNode IDs assigned to each entity, in order.
errorstring or nullError message (if failed).
If any signal in the batch is invalid (empty attribute, oversized value), the entire request is rejected with 400. No signals are ingested.
Each node accepts at most 4,096 distinct (attribute, value) properties (MAX_PROPERTIES_PER_NODE). A batch that would push any node past this cap is rejected atomically with 400 and error: "Ingest failed: Property limit exceeded ..." — no signals are ingested. Re-sending an already-stored pair is idempotent and never counts against the cap.

Example

curl -X POST http://localhost:8080/signals \
     -H "Authorization: Bearer your-api-key" \
     -H "Content-Type: application/json" \
     -d '{
       "signals": [
         {"entity_id": 1, "attribute": "name", "value": "Alice"},
         {"entity_id": 2, "attribute": "name", "value": "Bob"}
       ]
     }'
Expected response:
{
  "success": true,
  "ingested": 2,
  "node_ids": [0, 1],
  "error": null
}
After this call, GET /status shows edge_count >= 1, and POST /query with strongest_path from Alice’s node to Bob’s node returns found: true.

Limits

ConstraintValue
Max signals per request10,000 (MAX_SEQUENCE_LENGTH)
Max request body2 MB
Max attribute length256 bytes
Max value length64 KB (65,536 bytes)
Max distinct properties per node4,096 (MAX_PROPERTIES_PER_NODE)
Attribute charactersNo control characters
Value charactersNo control characters except \n, \r, \t
Last modified on June 14, 2026