Ingest Endpoints

POST /v1/ingest/sync

Submit a connector sync batch. This is the primary ingest endpoint.

The server runs the sync protocol: validates referential integrity, diffs against the current graph state for this connector, and commits the delta atomically (INV-C01).

Request Body

{
  "connector_id": "my-connector",
  "sync_id": "sync-2024-01-15-001",
  "entities": [
    {
      "entity_type": "host",
      "entity_key": "web-01",
      "entity_class": "Host",
      "display_name": "Web Server 01",
      "properties": {
        "state": "running",
        "region": "us-east-1",
        "cpu_count": 8
      }
    },
    {
      "entity_type": "service",
      "entity_key": "nginx-web-01",
      "entity_class": "Service",
      "display_name": "Nginx on web-01"
    }
  ],
  "relationships": [
    {
      "from_type": "host",
      "from_key": "web-01",
      "verb": "RUNS",
      "to_type": "service",
      "to_key": "nginx-web-01"
    }
  ]
}

Request Fields

FieldTypeRequiredDescription
connector_idstringYesIdentifies the connector. Used for source-scoped diff.
sync_idstringYesUnique ID for this sync run. Included in source tracking.
entitiesarrayYesEntities to upsert. Can be empty.
relationshipsarrayYesRelationships to upsert. Can be empty.

Entity Fields

FieldTypeRequiredDescription
entity_typestringYesType identifier (snake_case, e.g., "host")
entity_keystringYesSource-system unique key
entity_classstringYesClass from the known classes list
display_namestringNoHuman-readable name
propertiesobjectNoFlat key-value property bag

Relationship Fields

FieldTypeRequiredDescription
from_typestringYesSource entity type
from_keystringYesSource entity key
verbstringYesRelationship verb from the known verbs list
to_typestringYesTarget entity type
to_keystringYesTarget entity key
propertiesobjectNoFlat key-value property bag on the edge

Response (200 OK)

{
  "sync_id": "sync-2024-01-15-001",
  "entities_created": 2,
  "entities_updated": 0,
  "entities_unchanged": 0,
  "entities_deleted": 0,
  "relationships_created": 1,
  "relationships_updated": 0,
  "relationships_unchanged": 0,
  "relationships_deleted": 0
}

Error: Dangling Relationship (500)

If a relationship references an entity that doesn't exist in the batch or the current graph:

{
  "error": "DanglingRelationship",
  "message": "relationship from host:web-01 RUNS service:ghost — to_id not found in batch or graph"
}

Example

curl -X POST http://localhost:7700/v1/ingest/sync \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer your-key' \
  -d '{
    "connector_id": "my-connector",
    "sync_id": "sync-001",
    "entities": [
      {"entity_type": "host", "entity_key": "web-01", "entity_class": "Host",
       "display_name": "Web Server 01", "properties": {"state": "running"}}
    ],
    "relationships": []
  }'

POST /v1/ingest/write

Direct write batch — bypass the sync protocol. Use this when you want to write entities without source-scoped diffing.

Unlike /v1/ingest/sync, this endpoint does not:

  • Diff against previous state from the same connector
  • Delete entities not present in the batch

It simply upserts all provided entities and relationships.

Request Body

{
  "write_id": "write-001",
  "entities": [
    {
      "entity_type": "host",
      "entity_key": "h1",
      "entity_class": "Host",
      "display_name": "Server 1"
    }
  ],
  "relationships": []
}

Response (200 OK)

{
  "write_id": "write-001",
  "entities_written": 1,
  "relationships_written": 0
}

When to Use Write vs. Sync

Use CaseEndpoint
Connector that owns its entities/v1/ingest/sync
One-time bulk import/v1/ingest/write
Incremental updates (no deletions)/v1/ingest/write
Full re-sync with deletion of departed entities/v1/ingest/sync