Back to all resources
ContX IQ ContX IQ Python

ContX IQ: Query License Numbers a Person Can Access

Query the IndyKite Knowledge Graph (IKG) to retrieve all vehicle license numbers that a specific person is authorized to view based on their contractual relationships.

ContX IQ: Query License Numbers a Person Can Access

This example demonstrates how to:

1. Create an authorization policy that defines read access to LicenseNumber nodes

2. Create a query that retrieves license numbers based on the policy

3. Execute the query to get results filtered by authorization rules

The policy grants a Person access to LicenseNumber nodes when a path exists: Person -> ACCEPTED -> Contract -> COVERS -> Vehicle -> HAS -> LicenseNumber.

Use case

Scenario: Ryan (a Person node) has accepted Contract1, which covers Vehicle "Car1" owned by Company1. Car1 has LicenseNumber "ABC-123".

Goal: Ryan wants to retrieve all license numbers for vehicles he is contractually allowed to use.

Expected result: The query returns "ABC-123" because the relationship path exists:

Ryan -> ACCEPTED -> Contract1 -> COVERS -> Car1 -> HAS -> ABC-123

ikg

Requirements

Prerequisites:

- ServiceAccount credentials: Created in IndyKite Hub for your organization (used for policy/query configuration)

- AppAgent credentials: Created in IndyKite Hub for your Project/Application (used for data ingestion and query execution)

Required API access:

- POST /capture/v1/nodes/ (capture nodes)

- POST /capture/v1/relationships/ (capture relationships)

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

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

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

Steps

Step 1: Ingest Graph Data

- Authentication: AppAgent credential as API key (header: X-IK-ClientKey)

- Action: POST nodes and relationships to build the graph

- Result: Person, Contract, Vehicle, LicenseNumber, and Company nodes created with relationships

Step 2: Create Authorization Policy

- Authentication: ServiceAccount credential as Bearer token

- Action: POST policy configuration to /configs/v1/authorization-policies

- Policy logic: Grants READ access to LicenseNumber nodes when the subject (_Application) connects through the relationship path

- Key parameter: Filter uses $_appId (auto-populated system variable for the calling Application's ID)

- Result: Policy ID returned for reference

Step 3: Create ContX IQ Query

- Authentication: ServiceAccount credential as Bearer token

- Action: POST query configuration to /configs/v1/knowledge-queries

- Query logic: Traverses from Person (matched by email) through relationships to return LicenseNumber values

- Result: Query ID returned for reference

Step 4: Execute Query

- Authentication: AppAgent credential as API key (header: X-IK-ClientKey)

- Action: POST to /contx-iq/v1/execute with query ID and input parameters

- Input parameter: email (e.g., "ryan@example.com")

- Result: Array of license numbers the person can access

Step 5: Cleanup

- Action: DELETE the query and policy configurations

- Note: This does not delete ingested graph data

Step 1a

Capture nodes into the IKG. Creates Person, Company, Vehicle, Contract, LicenseNumber, and PaymentMethod nodes with their properties.

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 between nodes. Creates ACCEPTED, COVERS, OWNS, HAS, and HAS_AGREEMENT_WITH relationship types.

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

Authorization policy JSON. Defines that subjects matching the _Application type can READ LicenseNumber nodes when connected via Person -> Contract -> Vehicle -> LicenseNumber path.

policy.jsonJson
{
  "meta": {
    "policy_version": "1.0-ciq"
  },
  "subject": {
    "type": "_Application"
  },
  "condition": {
    "cypher": "MATCH (subject:_Application) MATCH (person:Person)-[r1:ACCEPTED]->(contract:Contract)-[r2:COVERS]->(vehicle:Vehicle)-[r3:HAS]->(ln:LicenseNumber)",
    "filter": [
      {
        "operator": "AND",
        "operands": [
          {
            "attribute": "person.property.email",
            "operator": "=",
            "value": "$person_email"
          },
          {
            "attribute": "subject.external_id",
            "operator": "=",
            "value": "$_appId"
          }
        ]
      }
    ]
  },
  "allowed_reads": {
    "nodes": [
      "ln.property.number",
      "ln.property.transferrable"
    ],
    "relationships": []
  }
}

Step 2b

POST request body to create the authorization policy. Wraps the policy JSON with location (project ID) and metadata.

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 (person:Person)-[r1:ACCEPTED]->(contract:Contract)-[r2:COVERS]->(vehicle:Vehicle)-[r3:HAS]->(ln:LicenseNumber)\",\"filter\":[{\"operator\":\"AND\",\"operands\":[{\"attribute\":\"person.property.email\",\"operator\":\"=\",\"value\":\"$person_email\"},{\"attribute\":\"subject.external_id\",\"operator\":\"=\",\"value\":\"$_appId\"}]}]},\"allowed_reads\":{\"nodes\":[\"ln.property.number\",\"ln.property.transferrable\"],\"relationships\":[]}}",
  "status": "ACTIVE",
  "tags": []
}

Step 2b (Python)

Python SDK equivalent: Creates the same authorization policy using the IndyKite Python client library.

create_policy.pyPython

import http.client

conn = http.client.HTTPSConnection("eu.api.indykite.com")

payload = "{"description": "",
  "display_name": "",
  "name": "",
  "policy": "",
  "project_id": "",
  "status": "ACTIVE",
  "tags": [
    ""
  ]}"
headers = {
    'Content-Type': "application/json",
    'Authorization': "YOUR_SECRET_TOKEN"
}

conn.request("POST", "/configs/v1/authorization-policies", payload, headers)
res = conn.getresponse()
data = res.read()

Step 2c

GET request to verify the policy was created. Returns the full policy configuration including system-generated ID and timestamps.

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

Step 2c (Python)

Python SDK equivalent: Reads the policy configuration to verify creation.

read_policy.pyPython

import http.client

conn = http.client.HTTPSConnection("eu.api.indykite.com")
headers = { 'Authorization': "YOUR_SECRET_TOKEN" }
conn.request("GET", "/configs/v1/authorization-policies/{{id}}", headers=headers)
res = conn.getresponse()
data = res.read()

Step 3a

Knowledge query JSON. Defines a Cypher-like query that: (1) matches a Person by email input parameter, (2) traverses to LicenseNumber nodes via contracts, (3) returns license number values.

knowledge_query.jsonJson
{
  "nodes": [
    "ln.property.number"
  ],
  "filter": {
    "attribute": "ln.property.number",
    "operator": "=",
    "value": "$ln_number"
  }
}

Step 3b

POST request body to create the knowledge query. Links the query to the authorization policy created in Step 2.

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\":[\"ln.property.number\"],\"filter\":{\"attribute\":\"ln.property.number\",\"operator\":\"=\",\"value\":\"$ln_number\"}}",
  "status": "ACTIVE"
}

Step 3b (Python)

Python SDK equivalent: Creates the knowledge query using the IndyKite Python client library.

create_knowledge_query.pyPython

import http.client

conn = http.client.HTTPSConnection("eu.api.indykite.com")

payload = "{"description": "",
  "display_name": "",
  "name": "",
  "policy_id": "",
  "project_id": "",
  "query": "",
  "status": "ACTIVE"}"

headers = {
    'Content-Type': "application/json",
    'Authorization': "YOUR_SECRET_TOKEN"
}

conn.request("POST", "/configs/v1/knowledge-queries", payload, headers)

res = conn.getresponse()
data = res.read()

Step 3c

GET request to verify the query was created. Returns the full query configuration including system-generated ID.

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

Step 3c (Python)

Python SDK equivalent: Reads the query configuration to verify creation.

read_knowledge_query.pyPython

import http.client

conn = http.client.HTTPSConnection("eu.api.indykite.com")

headers = { 'Authorization': "YOUR_SECRET_TOKEN" }

conn.request("GET", "/configs/v1//knowledge-queries/{id}", headers=headers)

res = conn.getresponse()
data = res.read()

print(data.decode("utf-8"))

Step 4a

Execute query request. Provides the query ID and input parameters (email). The system evaluates authorization and returns only permitted results.

POST https://eu.api.indykite.com/contx-iq/v1/executeJson
{
  "id": "knowledge_query_gid",
  "input_params": {
    "ln_number": "AX123456",
    "person_email": "ryan@yahoo.co.uk"
  }
}

Step 4a (Python)

Python SDK equivalent: Executes the ContX IQ query with the same parameters.

execute_query.pyPython

import http.client

conn = http.client.HTTPSConnection("eu.api.indykite.com")

payload = "{"id": "knowledge_query_gid",
  "input_params": {"ln_number": "AX123456","person_email": "ryan@yahoo.co.uk"}
 }"

headers = {
    'Content-Type': "application/json",
    'Authorization': "YOUR_SECRET_TOKEN"
}

conn.request("POST", "/contx-iq/v1/execute", payload, headers)

res = conn.getresponse()
data = res.read()

Step 4b

Expected response. Contains an array of license number strings that the queried person is authorized to access.

response.jsonJson
{
  "data": [
    {
      "nodes": {
        "ln.property.number": "AX123456"
      }
    }
  ]
}

Step 5a

DELETE request to remove the knowledge query. Use the query ID returned from Step 3.

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

Step 5a (Python)

Python SDK equivalent: Deletes the knowledge query configuration.

delete_knowledge_query.pyPython

import http.client

conn = http.client.HTTPSConnection("eu.api.indykite.com)

headers = { 'Authorization': "Bearer ..." }

conn.request("DELETE", "/configs/v1/knowledge-queries/{id}", headers=headers)

res = conn.getresponse()
data = res.read()

print(data.decode("utf-8"))

Step 5b

DELETE request to remove the authorization policy. Use the policy ID returned from Step 2.

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

Step 5b (Python)

Python SDK equivalent: Deletes the authorization policy configuration.

delete_policy.pyPython

import http.client

conn = http.client.HTTPSConnection("eu.api.indykite.com")

headers = { 'Authorization': "Bearer ...", 'Content-Type': "application/json" }

conn.request("DELETE", "/configs/v1/authorization-policies/{id}", headers=headers)

res = conn.getresponse()
data = res.read()