Back to all resources
TrustScore Trust Score Json

Trust Score: Filter CIQ Queries by Subject Trust Score Threshold

Configure a Trust Score Profile for Person, ingest two Persons with different metadata quality via the Capture API, let the profile compute a _TrustScore for each, then run a CIQ query that returns the caller's own profile only when their _final_score is at or above the requested threshold.

Trust Score: Filter CIQ Queries by Subject Trust Score Threshold

End-to-end demonstration that the Trust Score lives on the node as a regular property and can be filtered on inside a CIQ policy.

Pipeline:

1. Trust Score Profile (REST): Create a profile scoped to node_classification = "Person" with weighted dimensions (verification, origin, freshness).

2. Capture (REST): Ingest two Person nodes. One carries rich metadata (verified_time, source = "id-verifier.gov"); the other has only a self-attested source. The metadata is what the dimensions evaluate.

3. Scoring runs on the configured schedule: each Person gets a _TrustScore node attached via _HAS, with property _final_score plus one property per active dimension. The score IS a node in the IKG - every CIQ traversal can see it as subject.trust_score.<dim>.

4. CIQ filter (POST /contx-iq/v1/execute): the policy filter requires subject.trust_score._final_score > 0.66 (a fixed threshold baked into the policy). The high-metadata Person clears the bar; the self-attested one does not.

Effect: data quality becomes a first-class authorization input - the policy admits a subject only when its computed trust score clears the threshold.

Use case

Scenario: A KYC-style portal returns a Person's profile only if their underlying identity data clears a freshness/verification bar. The policy hardcodes the threshold at 0.66; the subject is chosen per call via input_params.subject_external_id.

Persons captured (relevant metadata only):

- Person(knightrider) - email + name carry verified_time = "2026-05-10T08:00:00Z" and source = "id-verifier.gov".

- Person(satchmo) - email + name carry source = "user-self-attested" only.

After scoring (THREE_HOURS), the IKG has:

- (knightrider) -[:_HAS]-> _TrustScore { _final_score: 0.83, verification: 1.0, origin: 0.75, freshness: 0.74 }

- (satchmo) -[:_HAS]-> _TrustScore { _final_score: 0.42, verification: 0.0, origin: 0.4, freshness: 0.85 }

CIQ executions:

- subject_external_id = knightrider -> 0.83 > 0.66 -> returns the profile, including subject.trust_score._final_score = 0.83.

- subject_external_id = satchmo -> 0.42 is not > 0.66 -> returns an empty data array. satchmo exists, but the trust threshold isn't met.

ikg

Requirements

Prerequisites:

- ServiceAccount credentials: For creating the Trust Score Profile, the CIQ policy, and the Knowledge Query.

- AppAgent credentials: For ingesting Person nodes via the Capture API and for executing the query.

- Bearer token: the subject is a Person, so /contx-iq/v1/execute needs a third-party bearer token alongside X-IK-ClientKey. The subject identity itself comes from input_params.subject_external_id.

- Patience: scores are computed on the schedule (THREE_HOURS in this example). The first run produces the initial _TrustScore nodes; subsequent runs update them.

Required API access:

- POST /configs/v1/trust-score-profiles

- POST /capture/v1/nodes

- POST /configs/v1/authorization-policies

- POST /configs/v1/knowledge-queries

- POST /contx-iq/v1/execute

Steps

Step 1: Create the Trust Score Profile for Person

- Schedule: THREE_HOURS (allowed values: THREE_HOURS, SIX_HOURS, TWELVE_HOURS, DAILY). Pick longer intervals for stable identity data.

- Dimensions: VERIFICATION, ORIGIN, FRESHNESS weighted 1 each; COMPLETENESS and VALIDITY present but weight 0 so they don't influence _final_score. Weights must be between 0 and 1.

- node_classification: "Person" - only Person nodes are scored by this profile.

Step 2: Capture Persons (with the metadata the profile cares about)

- Each scored property should carry the metadata fields the profile uses (property.metadata is a single object: source, verified_time, assurance_level, custom_metadata).

- verification dimension reads property.metadata.verified_time.

- origin dimension reads property.metadata.source.

- freshness dimension uses node + property update times.

- Two contrasting Persons make the threshold visible: a high-quality one (DMV-sourced, recently verified) and a self-attested one.

- After capture, scoring runs on the profile schedule; each Person gets a _TrustScore node and the value becomes readable as subject.trust_score.* in CIQ. There is no synchronous read - wait for the scheduled pass, then run the query below.

Step 3: Create the CIQ Policy

- The condition filter ANDs subject.external_id = $subject_external_id with subject.trust_score._final_score > 0.66 (the threshold is fixed in the policy).

- allowed_reads exposes the trust score so the response can show why the row was accepted.

Step 4: Create and Execute the Knowledge Query

- The query simply returns the subject's own fields + subject.trust_score._final_score.

- Send input_params.subject_external_id per call (knightrider clears the bar, satchmo does not).

- Bearer token + X-IK-ClientKey are required as for any Person-subject CIQ call.

Step 1

Trust Score Profile config for Person (flat REST body). node_classification controls which nodes get scored; schedule is one of THREE_HOURS/SIX_HOURS/TWELVE_HOURS/DAILY; each dimensions[].weight is between 0 and 1 (weight = 0 means computed-but-ignored).

POST https://eu.api.indykite.com/configs/v1/trust-score-profilesJson
{
  "project_id": "your_project_gid",
  "name": "person-trust-score-profile",
  "display_name": "Person Trust Score Profile",
  "description": "Trust Score profile for Person nodes. Combines verification, origin and freshness with equal weights; completeness and validity are present but weighted 0 (still computed, not used in _final_score).",
  "node_classification": "Person",
  "schedule": "THREE_HOURS",
  "dimensions": [
    {
      "name": "VERIFICATION",
      "weight": 1
    },
    {
      "name": "ORIGIN",
      "weight": 1
    },
    {
      "name": "FRESHNESS",
      "weight": 1
    },
    {
      "name": "COMPLETENESS",
      "weight": 0
    },
    {
      "name": "VALIDITY",
      "weight": 0
    }
  ]
}

Step 2

Capture two contrasting Person nodes. The metadata object on each property is the actual input to the scoring dimensions - verified_time, source, and update times.

POST https://eu.api.indykite.com/capture/v1/nodes/Json
{
  "nodes": [
    {
      "external_id": "knightrider",
      "type": "Person",
      "is_identity": true,
      "properties": [
        {
          "type": "email",
          "value": "knightrider@demo.com",
          "metadata": {
            "verified_time": "2026-05-10T08:00:00Z",
            "source": "id-verifier.gov"
          }
        },
        {
          "type": "name",
          "value": "Michael Knight",
          "metadata": {
            "verified_time": "2026-05-10T08:00:00Z",
            "source": "id-verifier.gov"
          }
        }
      ]
    },
    {
      "external_id": "satchmo",
      "type": "Person",
      "is_identity": true,
      "properties": [
        {
          "type": "email",
          "value": "satchmo@demo.com",
          "metadata": {
            "source": "user-self-attested"
          }
        },
        {
          "type": "name",
          "value": "Louis Armstrong",
          "metadata": {
            "source": "user-self-attested"
          }
        }
      ]
    }
  ]
}

Step 3

CIQ Policy. The trust score appears on the subject just like any other node attribute via subject.trust_score._final_score, so it can be ANDed into the regular filter.

policy.jsonJson
{
  "meta": {
    "policy_version": "1.0-ciq"
  },
  "subject": {
    "type": "Person"
  },
  "condition": {
    "cypher": "MATCH (subject:Person)",
    "filter": [
      {
        "operator": "AND",
        "operands": [
          {
            "operator": "=",
            "attribute": "subject.external_id",
            "value": "$subject_external_id"
          },
          {
            "operator": ">",
            "attribute": "subject.trust_score._final_score",
            "value": 0.66
          }
        ]
      }
    ]
  },
  "allowed_reads": {
    "nodes": [
      "subject.external_id",
      "subject.property.email",
      "subject.property.name",
      "subject.trust_score._final_score"
    ]
  }
}

Request to create the policy.

POST https://eu.api.indykite.com/configs/v1/authorization-policiesJson
{
  "project_id": "your_project_gid",
  "description": "CIQ policy that returns the requested Person record ONLY when its computed Trust Score _final_score is above the hardcoded threshold (0.66). The Trust Score Profile must already be active for node_classification = Person; otherwise subject.trust_score._final_score is undefined and the filter rejects every row.",
  "display_name": "policy - person with trust score filter",
  "name": "policy-trust-score-filter",
  "policy": "{\"meta\":{\"policy_version\":\"1.0-ciq\"},\"subject\":{\"type\":\"Person\"},\"condition\":{\"cypher\":\"MATCH (subject:Person)\",\"filter\":[{\"operator\":\"AND\",\"operands\":[{\"operator\":\"=\",\"attribute\":\"subject.external_id\",\"value\":\"$subject_external_id\"},{\"operator\":\">\",\"attribute\":\"subject.trust_score._final_score\",\"value\":0.66}]}]},\"allowed_reads\":{\"nodes\":[\"subject.external_id\",\"subject.property.email\",\"subject.property.name\",\"subject.trust_score._final_score\"]}}",
  "status": "ACTIVE",
  "tags": []
}

Step 4

Knowledge Query - returns the subject's own profile plus subject.trust_score._final_score.

knowledge_query.jsonJson
{
  "nodes": [
    "subject.external_id",
    "subject.property.email",
    "subject.property.name",
    "subject.trust_score._final_score"
  ]
}

Request to create the Knowledge Query.

POST https://eu.api.indykite.com/configs/v1/knowledge-queriesJson
{
  "project_id": "your_project_gid",
  "description": "Returns the requested Person's profile but ONLY if their Trust Score _final_score is above the policy threshold (0.66). The trust score itself is included in the response so you can see why a subject was accepted or excluded.",
  "display_name": "knowledge query - my profile if trustworthy",
  "name": "kq-my-profile-trusted",
  "policy_id": "your_policy_gid",
  "query": "{\"nodes\":[\"subject.external_id\",\"subject.property.email\",\"subject.property.name\",\"subject.trust_score._final_score\"]}",
  "status": "ACTIVE"
}

Execute for Knight Rider (high-quality data): input_params.subject_external_id = knightrider. His _final_score 0.83 clears the policy's 0.66 threshold.

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

Response: profile returned, _final_score = 0.83 is included as a regular field in the result row.

response_trusted.jsonJson
{
  "data": [
    {
      "nodes": {
        "subject.external_id": "knightrider",
        "subject.property.email": "knightrider@demo.com",
        "subject.property.name": "Michael Knight",
        "subject.trust_score._final_score": 0.83
      }
    }
  ]
}

Execute for Satchmo (self-attested data): input_params.subject_external_id = satchmo. His _final_score 0.42 does not clear the 0.66 threshold.

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

Response: empty data array. The Person exists but the trust score is below the threshold, so the filter drops the row.

response_rejected.jsonJson
{
  "data": []
}

API Endpoints

/configs/v1/trust-score-profiles
/capture/v1/nodes
/configs/v1/authorization-policies
/configs/v1/knowledge-queries
/contx-iq/v1/execute
View OpenAPI docs

Tags

Trust Score Trust Score Profile Capture ContX IQ Policy ContX IQ Query _TrustScore Node Data Quality subject.trust_score._final_score