Back to all resources
ContX IQ ContX IQ Json

ContX IQ: Paginated Query Execution with page_token and page_size

Walk a large CIQ result set across multiple /contx-iq/v1/execute calls using the page_token and page_size request fields. Demonstrates client-side pagination of an authorized read query.

ContX IQ: Paginated Query Execution with page_token and page_size

This example demonstrates pagination on the CIQ Execute endpoint.

Request fields:

- page_token (integer, optional): Page index. Any value below 1 returns page 1.

- page_size (integer, optional): Result set size. Default is 100.

Pagination model:

- The client increments page_token by page_size between calls (page 1 -> token 0 or 1, page 2 -> token = page_size, page 3 -> token = 2 * page_size, ...).

- When fewer than page_size records come back, you've hit the last page.

- Pagination applies to the records inside data[]; partial filters and authorization are re-evaluated on every call.

When to use:

- Knowledge Queries that may return hundreds or thousands of nodes/relationships.

- Streaming-style consumers that prefer fixed-size chunks.

Use case

Scenario: A back-office UI lists every car license alice owns, two rows at a time.

Graph structure:

- Person(alice) -[OWNS]-> Car(kitt) -[HAS]-> LicenseNumber("KITT 0001")

- Person(alice) -[OWNS]-> Car(caddilacv16) -[HAS]-> LicenseNumber("CADV16-007")

- Person(alice) -[OWNS]-> Car(skodaOctavia) -[HAS]-> LicenseNumber("OCT-2021-XX")

Walk:

1. Call /execute with page_token=0, page_size=2 -> returns kitt + caddilacv16.

2. Call /execute with page_token=2, page_size=2 -> returns skodaOctavia (one record => last page).

3. The UI knows there are no more rows and stops paging.

Tip: Use page_size to bound memory and latency, not to limit returned data. Authorization is applied to every record on every call.

ikg

Requirements

Prerequisites:

- ServiceAccount credentials: For creating the policy and Knowledge Query.

- AppAgent credentials: For ingesting the graph and executing the query.

- Bearer token for the subject (alice) when subject type is not _Application.

Required API access:

- POST /capture/v1/nodes/ and /capture/v1/relationships/

- POST /configs/v1/authorization-policies

- POST /configs/v1/knowledge-queries

- POST /contx-iq/v1/execute (called once per page)

Steps

Step 1: Capture the Graph

- Action: POST Person (alice), her Cars, and their LicenseNumber nodes, plus OWNS and HAS relationships.

- Result: Three car/license rows for alice to page through.

Step 2: Create a Read-Only Policy

- Action: POST a policy authorizing the Person -> Car -> LicenseNumber traversal, filtered to the subject.

- Result: Policy ID.

Step 3: Create the Knowledge Query

- Action: POST a Knowledge Query that reads car.external_id and ln.property.number.

- Result: Knowledge Query ID.

Step 4: Execute Page 1

- Authentication: AppAgent credential.

- Action: POST to /contx-iq/v1/execute with page_token=0 and page_size=2.

- Result: First 2 records.

Step 5: Execute Page 2

- Action: POST to /contx-iq/v1/execute with page_token=2 and page_size=2.

- Result: Remaining record(s). Fewer than page_size means this is the last page.

Step 1

Capture the Person (alice), her Cars, and their 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": "name",
          "value": "Alice Smith"
        }
      ]
    },
    {
      "external_id": "kitt",
      "type": "Car",
      "properties": [
        {
          "type": "model",
          "value": "Firebird"
        }
      ]
    },
    {
      "external_id": "caddilacv16",
      "type": "Car",
      "properties": [
        {
          "type": "model",
          "value": "V16"
        }
      ]
    },
    {
      "external_id": "skodaOctavia",
      "type": "Car",
      "properties": [
        {
          "type": "model",
          "value": "Octavia"
        }
      ]
    },
    {
      "external_id": "ln-kitt-0001",
      "type": "LicenseNumber",
      "properties": [
        {
          "type": "number",
          "value": "KITT 0001"
        }
      ]
    },
    {
      "external_id": "ln-cad-007",
      "type": "LicenseNumber",
      "properties": [
        {
          "type": "number",
          "value": "CADV16-007"
        }
      ]
    },
    {
      "external_id": "ln-oct-2021",
      "type": "LicenseNumber",
      "properties": [
        {
          "type": "number",
          "value": "OCT-2021-XX"
        }
      ]
    }
  ]
}

Capture the relationships: alice OWNS each Car, and each Car HAS its LicenseNumber.

POST https://eu.api.indykite.com/capture/v1/relationships/Json
{
  "relationships": [
    {
      "source": {
        "external_id": "alice",
        "type": "Person"
      },
      "target": {
        "external_id": "kitt",
        "type": "Car"
      },
      "type": "OWNS"
    },
    {
      "source": {
        "external_id": "alice",
        "type": "Person"
      },
      "target": {
        "external_id": "caddilacv16",
        "type": "Car"
      },
      "type": "OWNS"
    },
    {
      "source": {
        "external_id": "alice",
        "type": "Person"
      },
      "target": {
        "external_id": "skodaOctavia",
        "type": "Car"
      },
      "type": "OWNS"
    },
    {
      "source": {
        "external_id": "kitt",
        "type": "Car"
      },
      "target": {
        "external_id": "ln-kitt-0001",
        "type": "LicenseNumber"
      },
      "type": "HAS"
    },
    {
      "source": {
        "external_id": "caddilacv16",
        "type": "Car"
      },
      "target": {
        "external_id": "ln-cad-007",
        "type": "LicenseNumber"
      },
      "type": "HAS"
    },
    {
      "source": {
        "external_id": "skodaOctavia",
        "type": "Car"
      },
      "target": {
        "external_id": "ln-oct-2021",
        "type": "LicenseNumber"
      },
      "type": "HAS"
    }
  ]
}

Step 2

Read-only CIQ Policy authorizing the alice -> Car -> LicenseNumber traversal (subject filtered by subject_external_id).

policy.jsonJson
{
  "meta": {
    "policy_version": "1.0-ciq"
  },
  "subject": {
    "type": "Person"
  },
  "condition": {
    "cypher": "MATCH (subject:Person)-[:OWNS]->(car:Car)-[:HAS]->(ln:LicenseNumber)",
    "filter": [
      {
        "operator": "=",
        "attribute": "subject.external_id",
        "value": "$subject_external_id"
      }
    ]
  },
  "allowed_reads": {
    "nodes": [
      "car",
      "car.*",
      "ln",
      "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": "Authorize the Person -> Car -> LicenseNumber traversal so a read Knowledge Query can return each car and its license number for the subject.",
  "display_name": "policy - person owns cars with license numbers",
  "name": "policy-person-owns-car-licenses",
  "policy": "{\"meta\":{\"policy_version\":\"1.0-ciq\"},\"subject\":{\"type\":\"Person\"},\"condition\":{\"cypher\":\"MATCH (subject:Person)-[:OWNS]->(car:Car)-[:HAS]->(ln:LicenseNumber)\",\"filter\":[{\"operator\":\"=\",\"attribute\":\"subject.external_id\",\"value\":\"$subject_external_id\"}]},\"allowed_reads\":{\"nodes\":[\"car\",\"car.*\",\"ln\",\"ln.*\"]}}",
  "status": "ACTIVE",
  "tags": []
}

Step 3

Knowledge Query that returns car.external_id and ln.property.number with no filter - pagination will limit how many records come back per call.

knowledge_query.jsonJson
{
  "nodes": [
    "car.external_id",
    "ln.property.number"
  ]
}

Request to create the Knowledge Query.

POST https://eu.api.indykite.com/configs/v1/knowledge-queriesJson
{
  "project_id": "your_project_gid",
  "description": "Return every car license-number tuple this subject is authorized to see. Designed for pagination via page_token / page_size on /contx-iq/v1/execute.",
  "display_name": "knowledge query - paginated car licenses",
  "name": "kq-paginated-licenses",
  "policy_id": "your_policy_gid",
  "query": "{\"nodes\":[\"car.external_id\",\"ln.property.number\"]}",
  "status": "ACTIVE"
}

Step 4

Page 1: page_token=0, page_size=2 - returns the first two records.

POST https://eu.api.indykite.com/contx-iq/v1/executeJson
{
  "id": "your_query_gid_or_name",
  "input_params": {
    "subject_external_id": "alice"
  },
  "page_token": 0,
  "page_size": 2
}

Page 1 response - 2 records.

response_page_1.jsonJson
{
  "data": [
    {
      "nodes": {
        "car.external_id": "kitt",
        "ln.property.number": "KITT 0001"
      }
    },
    {
      "nodes": {
        "car.external_id": "caddilacv16",
        "ln.property.number": "CADV16-007"
      }
    }
  ]
}

Step 5

Page 2: advance page_token to 2 (page_size). Same page_size keeps result chunks consistent.

POST https://eu.api.indykite.com/contx-iq/v1/executeJson
{
  "id": "your_query_gid_or_name",
  "input_params": {
    "subject_external_id": "alice"
  },
  "page_token": 2,
  "page_size": 2
}

Page 2 response - 1 record. Fewer than page_size, so this is the last page.

response_page_2.jsonJson
{
  "data": [
    {
      "nodes": {
        "car.external_id": "skodaOctavia",
        "ln.property.number": "OCT-2021-XX"
      }
    }
  ]
}