Back to all resources
ContX IQ ContX IQ Json

ContX IQ: Delete Nodes and Their Relationships via Authorized Query

Demonstrates authorized node deletion in the IndyKite Knowledge Graph. When a node is deleted, all its relationships are automatically removed. This example shows deleting Contract nodes for a specific Person.

ContX IQ: Delete Nodes and Their Relationships via Authorized Query

This example demonstrates DELETE operations in ContX IQ:

Key concepts:

- Deleting a node automatically removes all relationships connected to that node

- The policy uses allowed_deletes.nodes to specify which node types can be deleted

- Authorization ensures only permitted applications can perform deletions

Workflow:

1. Capture data including Contract nodes

2. Link Application to Company (for authorization)

3. Create delete policy (allowed_deletes.nodes: Contract)

4. Execute delete query to remove Contract nodes

Important: Node deletion is cascading - when a Contract is deleted, its COVERS and ACCEPTED relationships are also removed.

Use case

Scenario: A person wants to cancel all their vehicle rental contracts.

Operation: Delete all Contract nodes associated with a specific Person.

Graph change:

Before:

- Person(Ryan) -[ACCEPTED]-> Contract1 -[COVERS]-> Vehicle(Car1)

- Person(Ryan) -[ACCEPTED]-> Contract2 -[COVERS]-> Vehicle(Car2)

After deletion:

- Person(Ryan) (no contracts)

- Vehicle(Car1) (still exists, but no Contract relationship)

- Vehicle(Car2) (still exists, but no Contract relationship)

Cascade behavior: Deleting Contract1 automatically removes:

- The ACCEPTED relationship from Person to Contract1

- The COVERS relationship from Contract1 to Vehicle

ikg

Requirements

Prerequisites:

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

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

Required API access:

- POST /capture/v1/nodes/ and /capture/v1/relationships/ (initial data)

- POST /configs/v1/authorization-policies (create policies)

- POST /configs/v1/knowledge-queries (create queries)

- POST /contx-iq/v1/execute (run queries)

Steps

Step 1: Ingest Graph Data with Contracts

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

- Action: POST nodes and relationships including Contract nodes to be deleted later

- Result: Graph with Person -> Contract -> Vehicle structure

Step 2: Create Application Linking Policy

- Authentication: ServiceAccount credential (Bearer token)

- Action: POST policy allowing Application to create HAS_AGREEMENT_WITH

- Key detail: $_appId filter auto-resolves to calling Application's ID

- Result: Policy ID returned

Step 3: Create Application Linking Query

- Authentication: ServiceAccount credential (Bearer token)

- Action: POST write query for Application -> Company relationship

- Result: Query ID returned

Step 4: Execute Application Linking

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

- Action: Execute query to connect Application to Company

- Result: Application authorized for subsequent operations

Step 5: Create Node Delete Policy

- Authentication: ServiceAccount credential (Bearer token)

- Action: POST policy with allowed_deletes.nodes containing Contract

- This authorizes queries to delete Contract-type nodes

- Result: Policy ID returned

Step 6: Create Delete Query

- Authentication: ServiceAccount credential (Bearer token)

- Action: POST query that identifies and deletes Contract nodes

- Query can use parameters to specify which contracts to delete

- Result: Query ID returned

Step 7: Execute Deletion

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

- Action: Execute the delete query

- Result: Contract nodes and their relationships removed from graph

Step 8: Cleanup

- Action: DELETE queries and policies (the deleted nodes remain gone)

Step 1a

Capture nodes including Contract nodes that will be deleted later. Also creates Person, Vehicle, Company, and LicenseNumber nodes.

POST https://eu.api.indykite.com/capture/v1/nodes/Json
{
  "nodes": [
    {
      "external_id": "alice",
      "is_identity": true,
      "type": "Person",
      "properties": [
        {
          "type": "email",
          "value": "alice@email.com"
        },
        {
          "type": "given_name",
          "value": "Alice"
        },
        {
          "type": "last_name",
          "value": "Smith"
        }
      ]
    },
    {
      "external_id": "ryan",
      "is_identity": true,
      "type": "Person",
      "properties": [
        {
          "type": "email",
          "value": "ryan@yahoo.co.uk"
        },
        {
          "type": "given_name",
          "value": "ryan"
        },
        {
          "type": "last_name",
          "value": "mushu"
        }
      ]
    },
    {
      "external_id": "tilda",
      "is_identity": true,
      "type": "Person",
      "properties": [
        {
          "type": "email",
          "value": "tilda@yahoo.co.uk"
        },
        {
          "type": "given_name",
          "value": "tilda"
        },
        {
          "type": "last_name",
          "value": "mushu"
        }
      ]
    },
    {
      "external_id": "cb123",
      "type": "PaymentMethod",
      "properties": [
        {
          "type": "payment_name",
          "value": "Credit Card"
        }
      ]
    },
    {
      "external_id": "kl123",
      "type": "PaymentMethod",
      "properties": [
        {
          "type": "payment_name",
          "value": "Klarna"
        }
      ]
    },
    {
      "external_id": "ct123",
      "type": "Contract",
      "properties": [
        {
          "type": "category",
          "value": "Insurance"
        },
        {
          "type": "status",
          "value": "Active"
        },
        {
          "type": "number",
          "value": "hfgrten123",
          "metadata": {
            "assurance_level": 3,
            "source": "BRREG"
          }
        }
      ]
    },
    {
      "external_id": "ct234",
      "type": "Contract",
      "properties": [
        {
          "type": "category",
          "value": "Insurance"
        },
        {
          "type": "status",
          "value": "Active"
        },
        {
          "type": "number",
          "value": "hfgrten234",
          "metadata": {
            "assurance_level": 3,
            "source": "BRREG"
          }
        }
      ]
    },
    {
      "external_id": "ct985",
      "type": "Contract",
      "properties": [
        {
          "type": "category",
          "value": "Insurance"
        },
        {
          "type": "status",
          "value": "Active"
        },
        {
          "type": "number",
          "value": "hfgrten985",
          "metadata": {
            "assurance_level": 3,
            "source": "BRREG"
          }
        }
      ]
    },
    {
      "external_id": "car1",
      "type": "Vehicle",
      "properties": [
        {
          "type": "category",
          "value": "Car"
        },
        {
          "type": "is_active",
          "value": true
        },
        {
          "type": "vin",
          "value": "rtfhcnvjt471"
        }
      ]
    },
    {
      "external_id": "car2",
      "type": "Vehicle",
      "properties": [
        {
          "type": "category",
          "value": "Car"
        },
        {
          "type": "is_active",
          "value": true
        },
        {
          "type": "vin",
          "value": "kdcbfrt178"
        }
      ]
    },
    {
      "external_id": "truck1",
      "type": "Vehicle",
      "properties": [
        {
          "type": "category",
          "value": "Truck"
        },
        {
          "type": "is_active",
          "value": true
        },
        {
          "type": "vin",
          "value": "sncnrkcldp"
        }
      ]
    },
    {
      "external_id": "license1",
      "type": "LicenseNumber",
      "properties": [
        {
          "type": "status",
          "value": "Active"
        },
        {
          "type": "number",
          "value": "AX123456",
          "metadata": {
            "assurance_level": 3,
            "source": "BRREG"
          }
        }
      ]
    },
    {
      "external_id": "license2",
      "type": "LicenseNumber",
      "properties": [
        {
          "type": "status",
          "value": "Active"
        },
        {
          "type": "number",
          "value": "OL123456",
          "metadata": {
            "assurance_level": 3,
            "source": "BRREG"
          }
        }
      ]
    },
    {
      "external_id": "license3",
      "type": "LicenseNumber",
      "properties": [
        {
          "type": "status",
          "value": "Active"
        },
        {
          "type": "number",
          "value": "VN123456",
          "metadata": {
            "assurance_level": 3,
            "source": "BRREG"
          }
        }
      ]
    },
    {
      "external_id": "company1",
      "type": "Company",
      "properties": [
        {
          "type": "name",
          "value": "Company1"
        },
        {
          "type": "registration",
          "value": "256314523"
        }
      ]
    },
    {
      "external_id": "company2",
      "type": "Company",
      "properties": [
        {
          "type": "name",
          "value": "Company2"
        },
        {
          "type": "registration",
          "value": "942365123"
        }
      ]
    },
    {
      "external_id": "application1",
      "type": "Application",
      "properties": [
        {
          "type": "name",
          "value": "Application"
        }
      ]
    },
    {
      "external_id": "application2",
      "type": "Application",
      "properties": [
        {
          "type": "name",
          "value": "Application2"
        }
      ]
    }
  ]
}

Step 1b

Capture relationships: Person -[ACCEPTED]-> Contract -[COVERS]-> Vehicle. These relationships will be cascade-deleted when the Contract is deleted.

POST https://eu.api.indykite.com/capture/v1/relationships/Json
{
  "relationships": [
    {
      "source": {
        "external_id": "ryan",
        "type": "Person"
      },
      "target": {
        "external_id": "cb123",
        "type": "PaymentMethod"
      },
      "type": "HAS"
    },
    {
      "source": {
        "external_id": "tilda",
        "type": "Person"
      },
      "target": {
        "external_id": "kl123",
        "type": "PaymentMethod"
      },
      "type": "HAS"
    },
    {
      "source": {
        "external_id": "alice",
        "type": "Person"
      },
      "target": {
        "external_id": "cb123",
        "type": "PaymentMethod"
      },
      "type": "HAS"
    },
    {
      "source": {
        "external_id": "ryan",
        "type": "Person"
      },
      "target": {
        "external_id": "ct123",
        "type": "Contract"
      },
      "type": "ACCEPTED"
    },
    {
      "source": {
        "external_id": "tilda",
        "type": "Person"
      },
      "target": {
        "external_id": "ct234",
        "type": "Contract"
      },
      "type": "ACCEPTED"
    },
    {
      "source": {
        "external_id": "alice",
        "type": "Person"
      },
      "target": {
        "external_id": "ct985",
        "type": "Contract"
      },
      "type": "ACCEPTED"
    },
    {
      "source": {
        "external_id": "ct123",
        "type": "Contract"
      },
      "target": {
        "external_id": "car1",
        "type": "Vehicle"
      },
      "type": "COVERS"
    },
    {
      "source": {
        "external_id": "ct985",
        "type": "Contract"
      },
      "target": {
        "external_id": "car1",
        "type": "Vehicle"
      },
      "type": "COVERS"
    },
    {
      "source": {
        "external_id": "ct234",
        "type": "Contract"
      },
      "target": {
        "external_id": "truck1",
        "type": "Vehicle"
      },
      "type": "COVERS"
    },
    {
      "source": {
        "external_id": "car1",
        "type": "Vehicle"
      },
      "target": {
        "external_id": "license1",
        "type": "LicenseNumber"
      },
      "type": "HAS"
    },
    {
      "source": {
        "external_id": "truck1",
        "type": "Vehicle"
      },
      "target": {
        "external_id": "license2",
        "type": "LicenseNumber"
      },
      "type": "HAS"
    },
    {
      "source": {
        "external_id": "car2",
        "type": "Vehicle"
      },
      "target": {
        "external_id": "license3",
        "type": "LicenseNumber"
      },
      "type": "HAS"
    },
    {
      "source": {
        "external_id": "company1",
        "type": "Company"
      },
      "target": {
        "external_id": "car1",
        "type": "Vehicle"
      },
      "type": "OWNS"
    },
    {
      "source": {
        "external_id": "company1",
        "type": "Company"
      },
      "target": {
        "external_id": "car2",
        "type": "Vehicle"
      },
      "type": "OWNS"
    },
    {
      "source": {
        "external_id": "company1",
        "type": "Company"
      },
      "target": {
        "external_id": "truck1",
        "type": "Vehicle"
      },
      "type": "OWNS"
    },
    {
      "source": {
        "external_id": "application1",
        "type": "Application"
      },
      "target": {
        "external_id": "company1",
        "type": "Company"
      },
      "type": "HAS_AGREEMENT_WITH"
    },
    {
      "source": {
        "external_id": "application2",
        "type": "Application"
      },
      "target": {
        "external_id": "company1",
        "type": "Company"
      },
      "type": "HAS_AGREEMENT_WITH"
    }
  ]
}

Step 2a

Policy JSON allowing _Application to create HAS_AGREEMENT_WITH relationships. The $_appId filter auto-populates with the calling Application's ID.

policy.jsonJson
{
  "meta": {
    "policy_version": "1.0-ciq"
  },
  "subject": {
    "type": "_Application"
  },
  "condition": {
    "cypher": "MATCH (subject:_Application) MATCH (company:Company)-[r2:OWNS]->(vehicle:Vehicle)",
    "filter": [
      {
        "operator": "AND",
        "operands": [
          {
            "attribute": "subject.external_id",
            "operator": "=",
            "value": "$_appId"
          },
          {
            "attribute": "company.external_id",
            "operator": "=",
            "value": "$companyID"
          }
        ]
      }
    ]
  },
  "allowed_upserts": {
    "relationships": {
      "relationship_types": [
        {
          "type": "HAS_AGREEMENT_WITH",
          "source_node_label": "_Application",
          "target_node_label": "Company"
        }
      ]
    }
  },
  "allowed_reads": {
    "nodes": [
      "company.*",
      "subject.*"
    ]
  }
}

Step 2b

POST request to create the application linking policy.

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\":\"_Application\"},\"condition\":{\"cypher\":\"MATCH (subject:_Application) MATCH (company:Company)-[r2:OWNS]->(vehicle:Vehicle)\",\"filter\":[{\"operator\":\"AND\",\"operands\":[{\"attribute\":\"subject.external_id\",\"operator\":\"=\",\"value\":\"$_appId\"},{\"attribute\":\"company.external_id\",\"operator\":\"=\",\"value\":\"$companyID\"}]}]},\"allowed_upserts\":{\"relationships\":{\"relationship_types\":[{\"type\":\"HAS_AGREEMENT_WITH\",\"source_node_label\":\"_Application\",\"target_node_label\":\"Company\"}]}},\"allowed_reads\":{\"nodes\":[\"company.*\",\"subject.*\"]}}",
  "status": "ACTIVE",
  "tags": []
}

Step 2c

GET request to verify the policy was created.

GET https://eu.api.indykite.com/configs/v1/authorization-policies/{policy_id}Json
{
  "id": "your_policy_configuration_gid"
}

Step 3a

Write query JSON for creating Application -> Company relationship.

knowledge_query.jsonJson
{
  "nodes": [
    "subject.external_id"
  ],
  "relationships": [
    "r1"
  ],
  "upsert_relationships": [
    {
      "name": "r1",
      "source": "subject",
      "target": "company",
      "type": "HAS_AGREEMENT_WITH"
    }
  ]
}

Step 3b

POST request to create the linking query.

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\":[\"subject.external_id\"],\"relationships\":[\"r1\"],\"upsert_relationships\":[{\"name\":\"r1\",\"source\":\"subject\",\"target\":\"company\",\"type\":\"HAS_AGREEMENT_WITH\"}]}",
  "status": "ACTIVE"
}

Step 3c

GET request to verify the query was created.

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

Step 4a

Execute the linking query. Establishes Application's authorization context.

POST https://eu.api.indykite.com/contx-iq/v1/executeJson
{
  "id": "ciq_query_gid",
  "input_params": {
    "companyID": "company1"
  }
}

Step 4b

Response confirming the Application -> Company link was created.

response.jsonJson
{
  "data": [
    {
      "nodes": {
        "subject.external_id": "application_external_id"
      },
      "relationships": {
        "r1": {
          "Id": 1152932499723124700,
          "ElementId": "5:3a2b09d5-2923-45d7-8453-8b1c698427b0:1152932499723124736",
          "StartId": 0,
          "StartElementId": "4:3a2b09d5-2923-45d7-8453-8b1c698427b0:0",
          "EndId": 15,
          "EndElementId": "4:3a2b09d5-2923-45d7-8453-8b1c698427b0:15",
          "Type": "HAS_AGREEMENT_WITH",
          "Props": {
            "create_time": "2025-06-09T15:12:46.374Z",
            "id": "48BJHS2CTFKcVpD4cUF8IA",
            "update_time": "2025-06-09T15:12:46.374Z"
          }
        }
      }
    },
    {
      "nodes": {
        "subject.external_id": "application_external_id"
      },
      "relationships": {
        "r1": {
          "Id": 1152932499723124700,
          "ElementId": "5:3a2b09d5-2923-45d7-8453-8b1c698427b0:1152932499723124736",
          "StartId": 0,
          "StartElementId": "4:3a2b09d5-2923-45d7-8453-8b1c698427b0:0",
          "EndId": 15,
          "EndElementId": "4:3a2b09d5-2923-45d7-8453-8b1c698427b0:15",
          "Type": "HAS_AGREEMENT_WITH",
          "Props": {
            "create_time": "2025-06-09T15:12:46.374Z",
            "id": "48BJHS2CTFKcVpD4cUF8IA",
            "update_time": "2025-06-09T15:12:46.374Z"
          }
        }
      }
    },
    {
      "nodes": {
        "subject.external_id": "application_external_id"
      },
      "relationships": {
        "r1": {
          "Id": 1152932499723124700,
          "ElementId": "5:3a2b09d5-2923-45d7-8453-8b1c698427b0:1152932499723124736",
          "StartId": 0,
          "StartElementId": "4:3a2b09d5-2923-45d7-8453-8b1c698427b0:0",
          "EndId": 15,
          "EndElementId": "4:3a2b09d5-2923-45d7-8453-8b1c698427b0:15",
          "Type": "HAS_AGREEMENT_WITH",
          "Props": {
            "create_time": "2025-06-09T15:12:46.374Z",
            "id": "48BJHS2CTFKcVpD4cUF8IA",
            "update_time": "2025-06-09T15:12:46.374Z"
          }
        }
      }
    }
  ]
}

Step 5a

Policy JSON with allowed_deletes.nodes directive. Specifies that Contract nodes can be deleted by authorized queries.

policy.jsonJson
{
  "meta": {
    "policy_version": "1.0-ciq"
  },
  "subject": {
    "type": "_Application"
  },
  "condition": {
    "cypher": "MATCH (subject : _Application)-[r1:HAS_AGREEMENT_WITH]->(company:Company)-[r2:OWNS]->(vehicle:Vehicle)<-[r3:COVERS]-(contract:Contract)<-[r4:ACCEPTED]-(person:Person)",
    "filter": [
      {
        "operator": "AND",
        "operands": [
          {
            "attribute": "subject.external_id",
            "operator": "=",
            "value": "$_appId"
          },
          {
            "attribute": "person.external_id",
            "operator": "=",
            "value": "$personID"
          }
        ]
      }
    ]
  },
  "allowed_deletes": {
    "nodes": [
      "contract"
    ]
  },
  "allowed_reads": {
    "nodes": [
      "person.*"
    ]
  }
}

Step 5b

POST request to create the node delete policy.

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\":\"_Application\"},\"condition\":{\"cypher\":\"MATCH (subject : _Application)-[r1:HAS_AGREEMENT_WITH]->(company:Company)-[r2:OWNS]->(vehicle:Vehicle)<-[r3:COVERS]-(contract:Contract)<-[r4:ACCEPTED]-(person:Person)\",\"filter\":[{\"operator\":\"AND\",\"operands\":[{\"attribute\":\"subject.external_id\",\"operator\":\"=\",\"value\":\"$_appId\"},{\"attribute\":\"person.external_id\",\"operator\":\"=\",\"value\":\"$personID\"}]}]},\"allowed_deletes\":{\"nodes\":[\"contract\"]},\"allowed_reads\":{\"nodes\":[\"person.*\"]}}",
  "status": "ACTIVE",
  "tags": []
}

Step 5c

GET request to verify the policy was created.

GET https://eu.api.indykite.com/configs/v1/authorization-policies/{policy_id}Json
{
  "id": "your_policy_configuration_gid"
}

Step 6a

Delete query JSON. Identifies Contract nodes to delete (e.g., by Person email or Contract ID). All relationships to/from deleted nodes are automatically removed.

knowledge_query.jsonJson
{
  "nodes": [
    "person.external_id"
  ],
  "delete_nodes": [
    "contract"
  ]
}

Step 6b

POST request to create the delete query.

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\":[\"person.external_id\"],\"delete_nodes\":[\"contract\"]}",
  "status": "ACTIVE"
}

Step 6c

GET request to verify the query was created.

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

Step 7a

Execute the delete query. Removes Contract nodes and their ACCEPTED/COVERS relationships from the graph.

POST https://eu.api.indykite.com/contx-iq/v1/executeJson
{
  "id": "knowledge_query_gid",
  "input_params": {
    "personID": "tilda"
  }
}

Step 7b

Response confirming the Contract nodes were deleted. The Person and Vehicle nodes remain, but are no longer connected via contracts.

response.jsonJson
{
  "data": [
    {
      "nodes": {
        "person.external_id": "tilda"
      }
    },
    {
      "nodes": {
        "person.external_id": "tilda"
      }
    },
    {
      "nodes": {
        "person.external_id": "tilda"
      }
    }
  ]
}

Step 8a

DELETE request to remove the queries.

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

Step 8b

DELETE request to remove the policies.

DELETE https://eu.api.indykite.com/configs/v1/authorization-policies/{policy_id}Json
{
  "id": "your_policy_configuration_gid"
}

ikg