Back to all resources
ContX IQ ContX IQ Json

ContX IQ: Query External Data Sources via Data Resolver

Demonstrates fetching data from external systems (APIs, databases) during ContX IQ query execution. The External Data Resolver retrieves information not stored in the IKG, combining graph data with real-time external lookups.

ContX IQ: Query External Data Sources via Data Resolver

This example demonstrates External Data Resolver integration:

What is an External Data Resolver?

A configuration that fetches data from external systems (APIs, databases) during query execution.

Use case: Vehicle VIN numbers are stored in an external system, not the IKG.

Flow:

1. Query requests vehicle.vin property

2. IKG has Vehicle node but no VIN stored

3. External Data Resolver fetches VIN from external API

4. Query returns combined IKG + external data

Benefits:

- Keep sensitive data in external systems

- Real-time data lookups

- Reduce data duplication

- Maintain single source of truth

Use case

Scenario: A car rental app needs to display vehicle VIN numbers, but VINs are stored in a separate vehicle registry system.

Graph in IKG:

Person(Alice) -[DRIVES]-> Vehicle(Car1)

Vehicle(Car1) has: category="SUV", data_ref="ext://vehicles/car1"

External Vehicle Registry:

car1: {vin: "1HGBH41JXMN109186"}

Query execution:

1. User (Alice) requests vehicle details

2. Policy authorizes: Alice can READ vehicles she DRIVES

3. Query fetches category from IKG: "SUV"

4. Query detects data_ref, calls External Data Resolver

5. Resolver fetches VIN from external registry

6. Response: {category: "SUV", vin: "1HGBH41JXMN109186"}

The VIN never needs to be stored in the IKG - it's fetched in real-time.

ikg

Requirements

Prerequisites:

- ServiceAccount credentials: For configuration and policy creation (Bearer token)

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

- User access token: For authorized user (Alice)

- External API access: The resolver endpoint must be accessible

External Data Resolver setup:

- Configured via POST /configs/v1/external-data-resolvers

- Defines endpoint URL, authentication, and data mapping

- In this example, returns mock data: vin="vinmagic"

Steps

Step 1: Create External Data Resolver Configuration

- Authentication: ServiceAccount credential (Bearer token)

- Action: POST to /configs/v1/external-data-resolvers

- Configuration includes:

- Resolver endpoint URL

- Authentication method

- Data mapping (how to extract VIN from response)

- Result: Resolver ID returned

Step 2: Ingest Nodes with Data References

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

- Action: POST Person, Vehicle nodes

- Key detail: Vehicle node includes data_ref property pointing to external data

- Format: data_ref: "ext://resolver-id/path"

- Result: Graph ready with external data references

Step 3: Create Policy for External Data Access

- Authentication: ServiceAccount credential (Bearer token)

- Action: POST policy allowing:

- READ on Vehicle nodes through Person -[DRIVES]-> Vehicle path

- ACCESS to external data via data_ref

- Result: Policy ID returned

Step 4: Create Query Returning External Data

- Authentication: ServiceAccount credential (Bearer token)

- Action: POST query that returns vehicle.category (IKG) and vehicle.vin (external)

- The query engine automatically resolves data_ref properties

- Result: Query ID returned

Step 5: Execute Query as Authorized User

- Authentication: AppAgent credential + User token (Bearer header)

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

- Result: Combined response with IKG data + external VIN

Step 1

Create an External Data Resolver which configures the data to retrieve. In this use case, the configuration always returns "vinmagic" as a vin value.

POST https://eu.api.indykite.com/configs/v1/external-data-resolversJson
{
  "project_id": "your_project_gid",
  "description": "description of external data reference",
  "display_name": "external data name",
  "name": "externalDataResolverForCIQName",
  "headers": {},
  "method": "GET",
  "request_content_type": "JSON",
  "request_payload": "",
  "response_content_type": "JSON",
  "response_selector": ".echo",
  "url": "http://whateverUrlWithExternalValue/magic?data=vinmagic"
}

Step 2

Capture the nodes needed for this use case.

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": "bob",
      "type": "Person",
      "is_identity": true,
      "properties": [
        {
          "type": "email",
          "value": "bob@email.com"
        },
        {
          "type": "given_name",
          "value": "Bob"
        }
      ]
    },
    {
      "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": "Parking"
        },
        {
          "type": "status",
          "value": "Active"
        }
      ]
    },
    {
      "external_id": "ct234",
      "type": "Contract",
      "properties": [
        {
          "type": "category",
          "value": "Parking"
        },
        {
          "type": "status",
          "value": "Active"
        }
      ]
    },
    {
      "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",
          "external_value": "externalDataResolverForCIQName"
        }
      ]
    },
    {
      "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": "companyParking",
      "type": "Company",
      "properties": [
        {
          "type": "name",
          "value": "City Parking Inc"
        }
      ]
    }
  ]
}

Capture the relationships needed for this use case.

POST https://eu.api.indykite.com/capture/v1/relationships/Json
{
  "relationships": [
    {
      "source": {
        "external_id": "bob",
        "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": "alice",
        "type": "Person"
      },
      "target": {
        "external_id": "ct123",
        "type": "Contract"
      },
      "type": "ACCEPTED"
    },
    {
      "source": {
        "external_id": "bob",
        "type": "Person"
      },
      "target": {
        "external_id": "ct234",
        "type": "Contract"
      },
      "type": "ACCEPTED"
    },
    {
      "source": {
        "external_id": "ct123",
        "type": "Contract"
      },
      "target": {
        "external_id": "car1",
        "type": "Vehicle"
      },
      "type": "COVERS"
    },
    {
      "source": {
        "external_id": "ct234",
        "type": "Contract"
      },
      "target": {
        "external_id": "car2",
        "type": "Vehicle"
      },
      "type": "COVERS"
    },
    {
      "source": {
        "external_id": "car1",
        "type": "Vehicle"
      },
      "target": {
        "external_id": "license1",
        "type": "LicenseNumber"
      },
      "type": "HAS"
    },
    {
      "source": {
        "external_id": "car2",
        "type": "Vehicle"
      },
      "target": {
        "external_id": "license2",
        "type": "LicenseNumber"
      },
      "type": "HAS"
    },
    {
      "source": {
        "external_id": "companyParking",
        "type": "Company"
      },
      "target": {
        "external_id": "ct234",
        "type": "Contract"
      },
      "type": "OFFERS"
    },
    {
      "source": {
        "external_id": "companyParking",
        "type": "Company"
      },
      "target": {
        "external_id": "ct123",
        "type": "Contract"
      },
      "type": "OFFERS"
    }
  ]
}

Step 3

Create a CIQ Policy which designates the data to retrieve.

policy.jsonJson
{
  "meta": {
    "policy_version": "1.0-ciq"
  },
  "subject": {
    "type": "Person"
  },
  "condition": {
    "cypher": "MATCH (company:Company)-[:OFFERS]->(contract:Contract)<-[:ACCEPTED]-(subject:Person)-[:HAS]->(payment:PaymentMethod), (contract)-[:COVERS]->(vehicle:Vehicle)-[: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": [
      "vehicle",
      "ln",
      "vehicle.*",
      "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 (company:Company)-[:OFFERS]->(contract:Contract)<-[:ACCEPTED]-(subject:Person)-[:HAS]->(payment:PaymentMethod), (contract)-[:COVERS]->(vehicle:Vehicle)-[: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\":[\"vehicle\",\"ln\",\"vehicle.*\",\"ln.*\"]}}",
  "status": "ACTIVE",
  "tags": []
}

Request to read the CIQ Policy configuration using REST.

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

Step 4

Create a CIQ Query in the context of the policy to retrieve the vehicle category and the vehicle vin.

knowledge_query.jsonJson
{
  "nodes": [
    "vehicle",
    "vehicle.property.category",
    "vehicle.property.vin"
  ]
}

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\":[\"vehicle\",\"vehicle.property.category\",\"vehicle.property.vin\"]}",
  "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 5

Run a CIQ Execution to actually retrieve the data, from the Person access token.

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": {
        "vehicle": {
          "Id": 9,
          "ElementId": "4:a5c213aa-aa4b-4be5-a17a-a677a80ee634:9",
          "Labels": [
            "Unique",
            "Resource",
            "Vehicle"
          ],
          "Props": {
            "_service": "capture-api",
            "create_time": "2025-07-28T13:47:58.062Z",
            "external_id": "car2",
            "id": "sF_yDttqSKi_oUwiv7OSww",
            "type": "Vehicle",
            "update_time": "2025-07-28T13:47:58.062Z"
          }
        },
        "vehicle.property.category": "Car",
        "vehicle.property.vin": "vinmagic"
      }
    }
  ]
}