ContX IQ: Create a User Node from an Access Token and Authorize Through Organization Membership
This example shows how a ContX IQ query can create graph data from the caller's access token, and how a later policy can authorize through the relationships that data is part of:
Key concepts:
1. A Knowledge Query can upsert a node whose external_id is the token's sub claim ($token.sub), so the caller becomes a User node in the graph without a separate ingest.
2. The User node is linked to an ingested UserProfile, which is the policy subject.
3. The UserProfile has a BELONGS_TO relationship with an Organization; the relationship carries a role property (Member or Admin).
4. Later policies match the token to the User node (user.external_id = $token.sub) and filter on the relationship property (bt.role).
Workflow:
1. Ingest UserProfile, Organization and Event nodes, with BELONGS_TO and PART_OF relationships
2. Execute a query with each user's token to create the User node and the HAS relationship from the UserProfile
3. Read the Events of the Organization the UserProfile belongs to (relationship role Member)
4. Upsert a new Event into that Organization (relationship role Admin)
The authorization comes from the graph: who the token maps to, which Organization the profile belongs to, and what the relationship says about that membership.
Use case
Scenario: An event platform where every user belongs to an organization, and what a user may do with the organization's events is written on the membership relationship.
Ingested graph:
- UserProfile(alice) -[BELONGS_TO {role: "Member"}]-> Organization(org1)
- UserProfile(bob) -[BELONGS_TO {role: "Admin"}]-> Organization(org1)
- Event(event_lambda) -[PART_OF]-> Organization(org1)
Linking the tokens:
- Execute with alice's token -> User(<alice token sub>) is created and UserProfile(alice) -[HAS]-> User
- Execute with bob's token -> User(<bob token sub>) is created and UserProfile(bob) -[HAS]-> User
Results:
- Read query with alice's token: the policy requires bt.role = "Member" on the BELONGS_TO relationship -> event_lambda is returned.
- Upsert query with bob's token: the policy requires bt.role = "Admin" -> Event(event14) is created and linked to org1 with PART_OF.

Requirements
Prerequisites:
- ServiceAccount credentials: For creating policies and queries (Bearer token)
- AppAgent credentials: For data ingestion and query execution (X-IK-ClientKey)
- User access tokens: one token for alice and one for bob, issued by your IdP
- Token Introspect configuration: validates those tokens on /contx-iq/v1/execute
How user tokens are used:
- Pass the user token in the Authorization header: "Bearer {user_access_token}"
- The policy and query read the token's sub claim as $token.sub
- The UserProfile is the policy subject; the User node created from the token is matched to it in the policy cypher
Steps
Step 1: Ingest Profiles, Organization and Event
- Authentication: AppAgent credential (X-IK-ClientKey header)
- Action: POST nodes: UserProfile (alice, bob), Organization (org1), Event (event_lambda)
- Action: POST relationships: BELONGS_TO (UserProfile -> Organization, with a role property), PART_OF (Event -> Organization)
- Result: Graph ready
Step 2: Create the User Creation Policy
- Authentication: ServiceAccount credential (Bearer token)
- Action: POST policy with subject type UserProfile, filter subject.external_id = $subject_external_id, allowing:
- READ on the subject node
- UPSERT User nodes
- UPSERT HAS relationships (UserProfile -> User)
- Result: Policy ID returned
Step 3: Create the User Creation Query
- Authentication: ServiceAccount credential (Bearer token)
- Action: POST query that upserts a User node with external_id $token.sub and the DigitalTwin label, sets its email from $email, and upserts the HAS relationship from the subject
- Result: Query ID returned
Step 4: Execute with alice's Token
- Authentication: AppAgent credential + alice's user token (Bearer header)
- Action: POST to /contx-iq/v1/execute with input_params subject_external_id = alice and email
- Result: User node created from the token and linked to UserProfile(alice)
Step 5: Execute with bob's Token
- Authentication: AppAgent credential + bob's user token (Bearer header)
- Action: POST to /contx-iq/v1/execute with input_params subject_external_id = bob and email
- Result: User node created from the token and linked to UserProfile(bob)
Step 6: Create the Event Read Policy
- Authentication: ServiceAccount credential (Bearer token)
- Action: POST policy with subject type UserProfile and cypher
(user:User)<-[:HAS]-(subject:UserProfile)-[bt:BELONGS_TO]->(org:Organization)<-[:PART_OF]-(event:Event)
- Filter: subject.external_id = $subject_external_id AND user.external_id = $token.sub AND bt.role = "Member"
- Allowed reads: event, event.*, org.external_id, subject.property.name
- Result: Policy ID returned
Step 7: Create the Event Read Query
- Authentication: ServiceAccount credential (Bearer token)
- Action: POST query returning event, filtered on subject.property.email = $email
- Result: Query ID returned
Step 8: Execute the Read with alice's Token
- Authentication: AppAgent credential + alice's token
- Action: POST to /contx-iq/v1/execute with input_params subject_external_id = alice and email
- Result: event_lambda returned
Step 9: Create the Event Upsert Policy
- Authentication: ServiceAccount credential (Bearer token)
- Action: POST policy with subject type UserProfile and cypher
(user:User)<-[:HAS]-(subject:UserProfile)-[bt:BELONGS_TO]->(org:Organization)
- Filter: subject.external_id = $profile_external_id AND $token.sub = $user_external_id AND bt.role = "Admin"
- Allowed upserts: Event nodes, PART_OF relationships (Event -> Organization)
- Result: Policy ID returned
Step 10: Create the Event Upsert Query
- Authentication: ServiceAccount credential (Bearer token)
- Action: POST query that upserts an Event with external_id $eventId and properties $title and $link, and the PART_OF relationship to org
- Result: Query ID returned
Step 11: Execute the Upsert with bob's Token
- Authentication: AppAgent credential + bob's token
- Action: POST to /contx-iq/v1/execute with input_params profile_external_id = bob, user_external_id = the token's sub, eventId, title, link
- Result: Event(event14) created and linked to org1
Step 1
Capture the nodes needed for this use case.
{
"nodes": [
{
"external_id": "alice",
"type": "UserProfile",
"properties": [
{
"type": "email",
"value": "alice@email.com"
}
]
},
{
"external_id": "bob",
"type": "UserProfile",
"properties": [
{
"type": "email",
"value": "bob@email.com"
}
]
},
{
"external_id": "org1",
"type": "Organization",
"properties": [
{
"type": "name",
"value": "Org1"
}
]
},
{
"external_id": "event_lambda",
"type": "Event",
"labels": [],
"is_identity": false,
"properties": [
{
"type": "title",
"value": "Event Lambda",
"metadata": {
"assurance_level": 1,
"source": "Some Source",
"verified_time": "2024-04-10T06:28:16Z"
}
},
{
"type": "startDate",
"value": "2025-01-01",
"metadata": {
"assurance_level": 1,
"source": "Some Source",
"verified_time": "2025-04-10T06:28:16Z"
}
},
{
"type": "link",
"value": "https://events.com",
"metadata": {
"assurance_level": 1,
"source": "Some Source",
"verified_time": "2025-04-10T06:28:16Z"
}
},
{
"type": "description",
"value": "Description ...",
"metadata": {
"assurance_level": 1,
"source": "Some Source",
"verified_time": "2025-04-10T06:28:16Z"
}
}
]
}
]
}Capture the relationships needed for this use case.
{
"relationships": [
{
"source": {
"external_id": "alice",
"type": "UserProfile"
},
"target": {
"external_id": "org1",
"type": "Organization"
},
"type": "BELONGS_TO",
"properties": [
{
"type": "role",
"value": "Member"
}
]
},
{
"source": {
"external_id": "bob",
"type": "UserProfile"
},
"target": {
"external_id": "org1",
"type": "Organization"
},
"type": "BELONGS_TO",
"properties": [
{
"type": "role",
"value": "Admin"
}
]
},
{
"source": {
"external_id": "event_lambda",
"type": "Event"
},
"target": {
"external_id": "org1",
"type": "Organization"
},
"type": "PART_OF"
}
]
}Step 2
Create a CIQ Policy which designates the Subject nodes can be read,
the User nodes can be upserted and relationships between User and UserProfile can be upserted.
{
"meta": {
"policy_version": "1.0-ciq"
},
"subject": {
"type": "UserProfile"
},
"condition": {
"cypher": "MATCH (subject:UserProfile)",
"filter": [
{
"operator": "=",
"attribute": "subject.external_id",
"value": "$subject_external_id"
}
]
},
"allowed_reads": {
"nodes": [
"subject",
"subject.*"
]
},
"allowed_upserts": {
"nodes": {
"node_types": [
"User"
]
},
"relationships": {
"relationship_types": [
{
"type": "HAS",
"source_node_label": "UserProfile",
"target_node_label": "User"
}
]
}
}
}Request to create the CIQ Policy configuration using REST.
{
"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\":\"UserProfile\"},\"condition\":{\"cypher\":\"MATCH (subject:UserProfile)\",\"filter\":[{\"operator\":\"=\",\"attribute\":\"subject.external_id\",\"value\":\"$subject_external_id\"}]},\"allowed_reads\":{\"nodes\":[\"subject\",\"subject.*\"]},\"allowed_upserts\":{\"nodes\":{\"node_types\":[\"User\"]},\"relationships\":{\"relationship_types\":[{\"type\":\"HAS\",\"source_node_label\":\"UserProfile\",\"target_node_label\":\"User\"}]}}}",
"status": "ACTIVE",
"tags": []
}Request to read the CIQ Policy configuration using REST.
{
"id": "your_policy_configuration_gid"
}Step 3
Create a CIQ Query in the context of the policy to retrieve and upsert the data. The labels: ["DigitalTwin"] entry creates the User as an identity node - the same result as is_identity: true in the Capture API.
{
"nodes": [
"subject",
"subject.property.email",
"user.property.email"
],
"relationships": [],
"upsert_nodes": [
{
"name": "user",
"type": "User",
"external_id": "$token.sub",
"labels": [
"DigitalTwin"
],
"properties": [
{
"type": "email",
"value": "$email"
}
]
}
],
"upsert_relationships": [
{
"name": "has",
"source": "subject",
"target": "user",
"type": "HAS"
}
]
}Request to create a CIQ Query configuration using REST.
{
"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\",\"subject.property.email\",\"user.property.email\"],\"relationships\":[],\"upsert_nodes\":[{\"name\":\"user\",\"type\":\"User\",\"external_id\":\"$token.sub\",\"labels\":[\"DigitalTwin\"],\"properties\":[{\"type\":\"email\",\"value\":\"$email\"}]}],\"upsert_relationships\":[{\"name\":\"has\",\"source\":\"subject\",\"target\":\"user\",\"type\":\"HAS\"}]}",
"status": "ACTIVE"
}Read the CIQ Query Configuration.
{
"id": "your_knowledge_query_configuration_gid"
}Step 4
Run a CIQ Execution to create a User node from the first access token and link it to the corresponding UserProfile.
{
"id": "knowledge_query_gid",
"input_params": {
"subject_external_id": "alice",
"user_external_id": "alice_user_external_id",
"email": "alice@email.com"
},
"page_token": 1
}CIQ Execution response.
{
"data": [
{
"nodes": {
"subject": {
"Id": 14,
"ElementId": "4:0f1c76a0-92f1-4474-80af-aa4c317e636a:14",
"Labels": [
"Unique",
"Resource",
"UserProfile"
],
"Props": {
"_service": "capture-api",
"create_time": "2025-06-13T16:08:25.47Z",
"external_id": "alice",
"id": "4H1gEySmTGasbkjWDyyuXg",
"type": "UserProfile",
"update_time": "2025-06-13T16:08:25.47Z"
}
},
"subject.property.email": "alice@email.com",
"user.property.email": "alice@email.com"
}
}
]
}Step 5
Run a CIQ Execution to create a User node from the second access token and link it to the corresponding UserProfile.
{
"id": "knowledge_query_gid",
"input_params": {
"subject_external_id": "bob",
"user_external_id": "bob_user_external_id",
"email": "bob@email.com"
},
"page_token": 1
}CIQ Execution response.
{
"data": [
{
"nodes": {
"subject": {
"Id": 15,
"ElementId": "4:0f1c76a0-92f1-4474-80af-aa4c317e636a:15",
"Labels": [
"Unique",
"Resource",
"UserProfile"
],
"Props": {
"_service": "capture-api",
"create_time": "2025-06-13T16:08:25.47Z",
"external_id": "bob",
"id": "lIHALUiJSSiwVCff51KxxA",
"type": "UserProfile",
"update_time": "2025-06-13T16:08:25.47Z"
}
},
"subject.property.email": "bob@email.com",
"user.property.email": "bob@email.com"
}
}
]
}Step 6
Create a CIQ Policy which designates the nodes which are allowed to read the Event nodes linked to the Organization nodes they have a relationship with, according to a specific role.
{
"meta": {
"policy_version": "1.0-ciq"
},
"subject": {
"type": "UserProfile"
},
"condition": {
"cypher": "MATCH (user:User)<-[:HAS]-(subject:UserProfile)-[bt:BELONGS_TO]->(org:Organization)<-[:PART_OF]-(event:Event)",
"filter": [
{
"operator": "AND",
"operands": [
{
"operator": "=",
"attribute": "subject.external_id",
"value": "$subject_external_id"
},
{
"operator": "=",
"attribute": "user.external_id",
"value": "$token.sub"
},
{
"operator": "=",
"attribute": "bt.role",
"value": "Member"
}
]
}
]
},
"allowed_reads": {
"nodes": [
"event",
"event.*",
"org.external_id",
"subject.property.name"
]
}
}Json to create the CIQ Policy configuration using REST.
{
"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\":\"UserProfile\"},\"condition\":{\"cypher\":\"MATCH (user:User)<-[:HAS]-(subject:UserProfile)-[bt:BELONGS_TO]->(org:Organization)<-[:PART_OF]-(event:Event)\",\"filter\":[{\"operator\":\"AND\",\"operands\":[{\"operator\":\"=\",\"attribute\":\"subject.external_id\",\"value\":\"$subject_external_id\"},{\"operator\":\"=\",\"attribute\":\"user.external_id\",\"value\":\"$token.sub\"},{\"operator\":\"=\",\"attribute\":\"bt.role\",\"value\":\"Member\"}]}]},\"allowed_reads\":{\"nodes\":[\"event\",\"event.*\",\"org.external_id\",\"subject.property.name\"]}}",
"status": "ACTIVE",
"tags": []
}Json to read the CIQ Policy configuration using REST.
{
"id": "your_policy_configuration_gid"
}Step 7
Create a CIQ Query in the context of the policy to retrieve data.
{
"nodes": [
"event"
],
"filter": {
"attribute": "subject.property.email",
"operator": "=",
"value": "$email"
}
}Json to create a CIQ Query configuration using REST.
{
"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\":[\"event\"],\"filter\":{\"attribute\":\"subject.property.email\",\"operator\":\"=\",\"value\":\"$email\"}}",
"status": "ACTIVE"
}Read the CIQ Query Configuration.
{
"id": "your_knowledge_query_configuration_gid"
}Step 8
Run a CIQ Execution to read the data.
{
"id": "knowledge_query_gid",
"input_params": {
"email": "alice@email.com",
"subject_external_id": "alice"
},
"page_token": 1
}CIQ Execution response.
{
"data": [
{
"nodes": {
"event": {
"Id": 8,
"ElementId": "4:0f1c76a0-92f1-4474-80af-aa4c317e636a:8",
"Labels": [
"Unique",
"Resource",
"Event"
],
"Props": {
"_service": "capture-api",
"create_time": "2025-06-13T16:08:11.293Z",
"external_id": "event_lambda",
"id": "ABeHsubWR7a3Yj0lOSuKRA",
"type": "Event",
"update_time": "2025-06-13T16:08:11.293Z"
}
}
}
}
]
}Step 9
Create a CIQ Policy which designates the nodes which are allowed to upsert Event nodes linked to the Organization nodes they have a relationship with, according to a specific role.
{
"meta": {
"policy_version": "1.0-ciq"
},
"subject": {
"type": "UserProfile"
},
"condition": {
"cypher": "MATCH (user:User)<-[:HAS]-(subject:UserProfile)-[bt:BELONGS_TO]->(org:Organization)",
"filter": [
{
"operator": "AND",
"operands": [
{
"operator": "=",
"attribute": "subject.external_id",
"value": "$profile_external_id"
},
{
"operator": "=",
"attribute": "$token.sub",
"value": "$user_external_id"
},
{
"operator": "=",
"attribute": "bt.role",
"value": "Admin"
}
]
}
]
},
"allowed_reads": {
"nodes": [
"org.external_id",
"subject.property.name"
]
},
"allowed_upserts": {
"nodes": {
"node_types": [
"Event"
]
},
"relationships": {
"relationship_types": [
{
"type": "PART_OF",
"source_node_label": "Event",
"target_node_label": "Organization"
}
]
}
}
}Json to create the CIQ Policy configuration using REST.
{
"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\":\"UserProfile\"},\"condition\":{\"cypher\":\"MATCH (user:User)<-[:HAS]-(subject:UserProfile)-[bt:BELONGS_TO]->(org:Organization)\",\"filter\":[{\"operator\":\"AND\",\"operands\":[{\"operator\":\"=\",\"attribute\":\"subject.external_id\",\"value\":\"$profile_external_id\"},{\"operator\":\"=\",\"attribute\":\"$token.sub\",\"value\":\"$user_external_id\"},{\"operator\":\"=\",\"attribute\":\"bt.role\",\"value\":\"Admin\"}]}]},\"allowed_reads\":{\"nodes\":[\"org.external_id\",\"subject.property.name\"]},\"allowed_upserts\":{\"nodes\":{\"node_types\":[\"Event\"]},\"relationships\":{\"relationship_types\":[{\"type\":\"PART_OF\",\"source_node_label\":\"Event\",\"target_node_label\":\"Organization\"}]}}}",
"status": "ACTIVE",
"tags": []
}Json to read the CIQ Policy configuration using REST.
{
"id": "your_policy_configuration_gid"
}Step 10
Create a CIQ Query in the context of the policy to upsert an Event node.
{
"nodes": [
"event",
"event.property.title",
"event.property.link"
],
"relationships": [],
"upsert_nodes": [
{
"name": "event",
"type": "Event",
"external_id": "$eventId",
"properties": [
{
"type": "title",
"value": "$title"
},
{
"type": "link",
"value": "$link"
}
]
}
],
"upsert_relationships": [
{
"name": "part",
"source": "event",
"target": "org",
"type": "PART_OF"
}
]
}Json to create a CIQ Query configuration using REST.
{
"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\":[\"event\",\"event.property.title\",\"event.property.link\"],\"relationships\":[],\"upsert_nodes\":[{\"name\":\"event\",\"type\":\"Event\",\"external_id\":\"$eventId\",\"properties\":[{\"type\":\"title\",\"value\":\"$title\"},{\"type\":\"link\",\"value\":\"$link\"}]}],\"upsert_relationships\":[{\"name\":\"part\",\"source\":\"event\",\"target\":\"org\",\"type\":\"PART_OF\"}]}",
"status": "ACTIVE"
}Read the CIQ Query Configuration.
{
"id": "your_knowledge_query_configuration_gid"
}Step 11
Run a CIQ Execution to upsert an Event node.
{
"id": "knowledge_query_gid",
"input_params": {
"profile_external_id": "bob",
"user_external_id": "bob_user_external_id",
"eventId": "event14",
"title": "Event in the parking lot",
"link": "https://www.example.com"
},
"page_token": 1
}CIQ Execution response.
{
"data": [
{
"nodes": {
"event": {
"Id": 24,
"ElementId": "4:0f1c76a0-92f1-4474-80af-aa4c317e636a:24",
"Labels": [
"Unique",
"Resource",
"Event"
],
"Props": {
"create_time": "2025-06-13T16:23:53.388Z",
"external_id": "event14",
"id": "sYE-gBnaRQeRO6gZdHA59A",
"type": "Event",
"update_time": "2025-06-13T16:23:53.388Z"
}
},
"event.property.link": "https://www.example.com",
"event.property.title": "Event in the parking lot"
}
}
]
}
API Endpoints
/capture/v1/nodes/capture/v1/relationships/configs/v1/authorization-policies/configs/v1/knowledge-queries/contx-iq/v1/execute