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

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.
{
"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.
{
"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.
{
"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.
{
"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.
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.
{
"id": "your_policy_configuration_gid"
}Step 2c (Python)
Python SDK equivalent: Reads the policy configuration to verify creation.
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.
{
"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.
{
"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.
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.
{
"id": "your_knowledge_query_configuration_gid"
}Step 3c (Python)
Python SDK equivalent: Reads the query configuration to verify creation.
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.
{
"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.
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.
{
"data": [
{
"nodes": {
"ln.property.number": "AX123456"
}
}
]
}Step 5a
DELETE request to remove the knowledge query. Use the query ID returned from Step 3.
{
"id": "your_knowledge_query_configuration_gid"
}Step 5a (Python)
Python SDK equivalent: Deletes the knowledge query configuration.
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.
{
"id": "your_policy_configuration_gid"
}Step 5b (Python)
Python SDK equivalent: Deletes the authorization policy configuration.
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()
API Endpoints
/capture/v1/nodes /capture/v1/relationships /configs/v1/authorization-policies /configs/v1/knowledge-queries /contx-iq/v1/execute