Back to all resources
ContX IQ ContX IQ Json

ContX IQ: Manage Node Property Metadata

Add, update, and query metadata on node properties. Metadata provides additional context like timestamps, sources, confidence scores, or audit information for individual property values.

ContX IQ: Manage Node Property Metadata

This example demonstrates property-level metadata management:

What is property metadata?

Additional attributes attached to individual property values, not the node itself.

Example:

Node: Person(name: "Alice")

Property metadata for "name":

- source: "HR_System"

- confidence: 0.95

- last_verified: "2024-01-15"

Operations demonstrated:

1. Add metadata to existing property

2. Create new property with metadata

3. Query/filter by metadata values

Use case

Scenario: Track data provenance and quality scores for identity attributes.

Initial state:

Person(email: "alice@example.com")

After metadata upsert:

Person(email: "alice@example.com")

└── metadata:

- source: "email_verification_service"

- verified: true

- verification_date: "2024-01-15"

Use cases for metadata:

- Data lineage: Track where data came from

- Quality scores: Confidence levels for ML-derived data

- Audit trail: When was data last verified

- Compliance: GDPR-related tracking

Query by metadata:

"Find all Person nodes where email.verified = true"

ikg

Requirements

Prerequisites:

- ServiceAccount credentials: For creating policies and queries (Bearer token)

- AppAgent credentials: For data ingestion and query execution (X-IK-ClientKey)

- User access token: For authorized user operations

Metadata structure:

- Attached to individual properties, not nodes

- Key-value pairs with any JSON-compatible values

- Queryable/filterable in ContX IQ

Steps

Step 1: Ingest Base Graph Data

- Authentication: AppAgent credential (X-IK-ClientKey header)

- Action: POST nodes and relationships

- Result: Base graph ready for metadata operations

Step 2: Create Metadata Upsert Policy

- Authentication: ServiceAccount credential (Bearer token)

- Action: POST policy allowing:

- READ on subject nodes

- UPSERT on property metadata

- READ on nodes with metadata

- Result: Policy ID returned

Step 3: Create Metadata Upsert Query

- Authentication: ServiceAccount credential (Bearer token)

- Action: POST query that:

- Adds metadata to existing property (e.g., email.source)

- Creates new property with metadata

- Result: Query ID returned

Step 4: Execute Metadata Upsert

- Authentication: AppAgent credential + User token

- Action: POST to /contx-iq/v1/execute

- Result: Properties now have metadata attached

Step 5: Create Metadata Filter Query

- Authentication: ServiceAccount credential (Bearer token)

- Action: POST query that filters by metadata values

- Example: Find nodes where property.metadata.source = "HR_System"

- Result: Query ID returned

Step 6: Execute Metadata Filter Query

- Authentication: AppAgent credential + User token

- Action: POST to /contx-iq/v1/execute

- Result: Only nodes matching metadata filter returned

Step 1

Capture the nodes needed for this use case.

POST https://eu.api.indykite.com/capture/v1/nodes/Json
{
  "nodes": [
    {
      "external_id": "alice",
      "type": "Person",
      "is_identity": true,
      "properties": [
        {
          "type": "email",
          "value": "alice@email.com"
        },
        {
          "type": "name",
          "value": "Alice Smith"
        }
      ]
    },
    {
      "external_id": "satchmo",
      "type": "Person",
      "is_identity": true,
      "properties": [
        {
          "type": "email",
          "value": "satchmo@demo.com"
        },
        {
          "type": "name",
          "value": "Louis Armstrong"
        }
      ]
    },
    {
      "external_id": "karel",
      "type": "Person",
      "is_identity": true,
      "properties": [
        {
          "type": "email",
          "value": "karel@demo.com"
        },
        {
          "type": "name",
          "value": "Karel Plihal"
        }
      ]
    },
    {
      "external_id": "kitt",
      "type": "Car",
      "is_identity": false,
      "properties": [
        {
          "type": "manufacturer",
          "value": "pontiac"
        },
        {
          "type": "model",
          "value": "Firebird"
        }
      ]
    },
    {
      "external_id": "caddilacv16",
      "type": "Car",
      "is_identity": false,
      "properties": [
        {
          "type": "manufacturer",
          "value": "Caddilac"
        },
        {
          "type": "model",
          "value": "V-16"
        }
      ]
    },
    {
      "external_id": "harmonika",
      "type": "Bus",
      "is_identity": false,
      "properties": [
        {
          "type": "manufacturer",
          "value": "Ikarus"
        },
        {
          "type": "model",
          "value": "280"
        }
      ]
    },
    {
      "external_id": "ln-xxx",
      "type": "LicenseNumber",
      "is_identity": false,
      "properties": [
        {
          "type": "license",
          "value": "ln-xxx-value"
        }
      ]
    },
    {
      "external_id": "ln-yyy",
      "type": "LicenseNumber",
      "is_identity": false,
      "properties": [
        {
          "type": "license",
          "value": "ln-yyy-value"
        }
      ]
    },
    {
      "external_id": "ln-zzz",
      "type": "LicenseNumber",
      "is_identity": false,
      "properties": [
        {
          "type": "license",
          "value": "ln-zzz-value"
        }
      ]
    }
  ]
}

Capture the relationships needed for this use case.

POST https://eu.api.indykite.com/capture/v1/relationships/Json
{
  "relationships": [
    {
      "source": {
        "external_id": "alice",
        "type": "Person"
      },
      "target": {
        "external_id": "kitt",
        "type": "Car"
      },
      "type": "OWNS",
      "properties": [
        {
          "type": "weight",
          "value": 1
        }
      ]
    },
    {
      "source": {
        "external_id": "alice",
        "type": "Person"
      },
      "target": {
        "external_id": "caddilacv16",
        "type": "Car"
      },
      "type": "OWNS",
      "properties": [
        {
          "type": "weight",
          "value": 2
        }
      ]
    },
    {
      "source": {
        "external_id": "satchmo",
        "type": "Person"
      },
      "target": {
        "external_id": "caddilacv16",
        "type": "Car"
      },
      "type": "OWNS"
    },
    {
      "source": {
        "external_id": "karel",
        "type": "Person"
      },
      "target": {
        "external_id": "harmonika",
        "type": "Bus"
      },
      "type": "OWNS"
    },
    {
      "source": {
        "external_id": "kitt",
        "type": "Car"
      },
      "target": {
        "external_id": "ln-xxx",
        "type": "LicenseNumber"
      },
      "type": "HAS",
      "properties": [
        {
          "type": "weight",
          "value": 3
        }
      ]
    },
    {
      "source": {
        "external_id": "caddilacv16",
        "type": "Car"
      },
      "target": {
        "external_id": "ln-zzz",
        "type": "LicenseNumber"
      },
      "type": "HAS"
    },
    {
      "source": {
        "external_id": "harmonika",
        "type": "Bus"
      },
      "target": {
        "external_id": "ln-yyy",
        "type": "LicenseNumber"
      },
      "type": "HAS"
    }
  ]
}

Step 2

Create a CIQ Policy which designates the Subject node, the cypher, the nodes allowed to be upserted and the nodes allowed to be read.

policy.jsonJson
{
  "meta": {
    "policy_version": "1.0-ciq"
  },
  "subject": {
    "type": "Person"
  },
  "condition": {
    "cypher": "MATCH (subject:Person)-[:OWNS]->(car:Car)-[:HAS]->(ln:LicenseNumber)",
    "filter": [
      {
        "operator": "AND",
        "operands": [
          {
            "attribute": "subject.external_id",
            "operator": "=",
            "value": "$subject_external_id"
          },
          {
            "attribute": "$token.sub",
            "operator": "=",
            "value": "$token_sub"
          }
        ]
      }
    ]
  },
  "allowed_reads": {
    "nodes": [
      "car",
      "ln",
      "car.*",
      "ln.*"
    ]
  },
  "allowed_upserts": {
    "nodes": {
      "existing_nodes": [
        "ln"
      ]
    }
  }
}

Request to create the CIQ Policy configuration using REST.

POST https://eu.api.indykite.com/configs/v1/authorization-policiesJson
{
  "project_id": "your_project_gid",
  "description": "description of policy",
  "display_name": "policy name",
  "name": "policy-name",
  "policy": "{\"meta\":{\"policy_version\":\"1.0-ciq\"},\"subject\":{\"type\":\"Person\"},\"condition\":{\"cypher\":\"MATCH (subject:Person)-[:OWNS]->(car:Car)-[:HAS]->(ln:LicenseNumber)\",\"filter\":[{\"operator\":\"AND\",\"operands\":[{\"attribute\":\"subject.external_id\",\"operator\":\"=\",\"value\":\"$subject_external_id\"},{\"attribute\":\"$token.sub\",\"operator\":\"=\",\"value\":\"$token_sub\"}]}]},\"allowed_reads\":{\"nodes\":[\"car\",\"ln\",\"car.*\",\"ln.*\"]},\"allowed_upserts\":{\"nodes\":{\"existing_nodes\":[\"ln\"]}}}",
  "status": "ACTIVE",
  "tags": []
}

Request to read the CIQ Policy configuration using REST.

policy_request.jsonJson
{
  "id": "your_policy_configuration_gid"
}

Step 3

Create a CIQ Query in the context of the policy to upsert and retrieve properties with metadata on a node.

knowledge_query.jsonJson
{
  "nodes": [
    "car",
    "ln.property.status",
    "ln.property.status.metadata.source",
    "ln.property.status.metadata.assurance_level",
    "ln.property.status.metadata.somethingImportant",
    "ln.property.license",
    "ln.property.license.metadata.source"
  ],
  "relationships": [],
  "upsert_nodes": [
    {
      "name": "ln",
      "properties": [
        {
          "type": "status",
          "value": "$status",
          "metadata": [
            {
              "type": "source",
              "value": "$token.iss"
            },
            {
              "type": "assurance_level",
              "value": 2
            },
            {
              "type": "somethingImportant",
              "value": "supercoolvalue"
            }
          ]
        },
        {
          "type": "license",
          "value": "$ln_number",
          "metadata": [
            {
              "type": "source",
              "value": "The government"
            }
          ]
        }
      ]
    }
  ],
  "filter": {
    "attribute": "ln.external_id",
    "operator": "=",
    "value": "$ln_external_id"
  }
}

Request to create a CIQ Query configuration using REST.

POST https://eu.api.indykite.com/configs/v1/knowledge-queriesJson
{
  "project_id": "your_project_gid",
  "description": "description of knowledge query",
  "display_name": "knowledge query name",
  "name": "knowledge-query-name",
  "policy_id": "your_policy_gid",
  "query": "{\"nodes\":[\"car\",\"ln.property.status\",\"ln.property.status.metadata.source\",\"ln.property.status.metadata.assurance_level\",\"ln.property.status.metadata.somethingImportant\",\"ln.property.license\",\"ln.property.license.metadata.source\"],\"relationships\":[],\"upsert_nodes\":[{\"name\":\"ln\",\"properties\":[{\"type\":\"status\",\"value\":\"$status\",\"metadata\":[{\"type\":\"source\",\"value\":\"$token.iss\"},{\"type\":\"assurance_level\",\"value\":2},{\"type\":\"somethingImportant\",\"value\":\"supercoolvalue\"}]},{\"type\":\"license\",\"value\":\"$ln_number\",\"metadata\":[{\"type\":\"source\",\"value\":\"The government\"}]}]}],\"filter\":{\"attribute\":\"ln.external_id\",\"operator\":\"=\",\"value\":\"$ln_external_id\"}}",
  "status": "ACTIVE"
}

Read the CIQ Query Configuration.

GET https://eu.api.indykite.com/configs/v1/knowledge-queries/{id}Json
{
  "id": "your_knowledge_query_configuration_gid"
}

Step 4

Run a CIQ Execution to get the newly created property information.

POST https://eu.api.indykite.com/contx-iq/v1/executeJson
{
  "id": "knowledge_query_gid",
  "input_params": {
    "subject_external_id": "alice",
    "token_sub": "alice_user_external_id",
    "status": "Valid",
    "ln_number": "ln-xxx-value",
    "ln_external_id": "ln-xxx"
  },
  "page_token": 1
}

CIQ Execution response.

response.jsonJson
{
  "data": [
    {
      "nodes": {
        "car": {
          "Id": 58,
          "ElementId": "4:a5c213aa-aa4b-4be5-a17a-a677a80ee634:58",
          "Labels": [
            "Unique",
            "Resource",
            "Car"
          ],
          "Props": {
            "_service": "capture-api",
            "create_time": "2025-08-13T17:19:43.014Z",
            "external_id": "kitt",
            "id": "twixV9zqQniA201VtZdzxw",
            "type": "Car",
            "update_time": "2025-08-13T17:20:00.141Z"
          }
        },
        "ln.property.license": "ln-xxx-value",
        "ln.property.license.metadata.source": "The government",
        "ln.property.status": "Valid",
        "ln.property.status.metadata.assurance_level": 2,
        "ln.property.status.metadata.somethingImportant": "supercoolvalue",
        "ln.property.status.metadata.source": "https://issuer_url"
      }
    }
  ]
}

Step 5

Create a CIQ Query in the context of the policy to retrieve property information according to a filter on one of the newly created metadata.

knowledge_query.jsonJson
{
  "nodes": [
    "car",
    "ln.property.status",
    "ln.property.status.metadata.source",
    "ln.property.status.metadata.assurance_level"
  ],
  "relationships": [],
  "filter": {
    "attribute": "ln.property.status.metadata.assurance_level",
    "operator": ">=",
    "value": 2
  }
}

Request to create a CIQ Query configuration using REST.

POST https://eu.api.indykite.com/configs/v1/knowledge-queriesJson
{
  "project_id": "your_project_gid",
  "description": "description of knowledge query",
  "display_name": "knowledge query name",
  "name": "knowledge-query-name",
  "policy_id": "your_policy_gid",
  "query": "{\"nodes\":[\"car\",\"ln.property.status\",\"ln.property.status.metadata.source\",\"ln.property.status.metadata.assurance_level\"],\"relationships\":[],\"filter\":{\"attribute\":\"ln.property.status.metadata.assurance_level\",\"operator\":\">=\",\"value\":2}}",
  "status": "ACTIVE"
}

Read the CIQ Query Configuration.

GET https://eu.api.indykite.com/configs/v1/knowledge-queries/{id}Json
{
  "id": "your_knowledge_query_configuration_gid"
}

Step 6

Run a CIQ Execution to read the data.

POST https://eu.api.indykite.com/contx-iq/v1/executeJson
{
  "id": "knowledge_query_gid",
  "input_params": {
    "subject_external_id": "alice",
    "token_sub": "alice_user_external_id"
  },
  "page_token": 1
}

CIQ Execution response.

response.jsonJson
{
  "data": [
    {
      "nodes": {
        "car": {
          "Id": 16,
          "ElementId": "4:a5c213aa-aa4b-4be5-a17a-a677a80ee634:16",
          "Labels": [
            "Unique",
            "Resource",
            "Car"
          ],
          "Props": {
            "_service": "capture-api",
            "create_time": "2025-08-13T14:18:57.565Z",
            "external_id": "kitt",
            "id": "ylK0vV4_T2CeCUxpolR7aw",
            "type": "Car",
            "update_time": "2025-08-13T14:18:57.565Z"
          }
        },
        "ln.property.status": "Valid",
        "ln.property.status.metadata.assurance_level": 2,
        "ln.property.status.metadata.source": "https://issuer_url"
      }
    }
  ]
}