Back to all resources
TrustScore Trust Score Json

Trust Score: Read Per-Dimension Scores from a LicenseNumber for Document Vetting

Configure a Trust Score Profile that weights dimensions unevenly for LicenseNumber nodes (validity heaviest, completeness lightest), capture two license numbers with different metadata quality, and run a CIQ query that returns the full per-dimension breakdown - not just the final score - so a vetting UI can show exactly why a document is or isn't trustworthy.

Trust Score: Read Per-Dimension Scores from a LicenseNumber for Document Vetting

Trust Score doesn't have to be a single number. Each dimension is a separately-readable property on the _TrustScore node, so a CIQ query can return the whole breakdown.

Pipeline:

1. Trust Score Profile (REST): node_classification = "LicenseNumber". Weights are uneven (each between 0 and 1) - validity = 1, verification = 0.6, freshness = 0.3, origin = 0.3, completeness = 0.3 - because an expired or unsourced licence should drag the final score down hard. Schedule is THREE_HOURS (allowed: THREE_HOURS, SIX_HOURS, TWELVE_HOURS, DAILY).

2. Capture (REST): Ingest the Person (alice), her Cars, and two LicenseNumber nodes. Each LicenseNumber carries:

- issued_on and valid_until as plain properties (string values) -> feed the validity dimension.

- a number property whose metadata object carries the documented capture.Metadata fields verified_time -> verification and source -> origin.

- freshness uses node + property update times; completeness reflects how fully the node is populated.

3. After scoring, each LicenseNumber has a _TrustScore node with both _final_score and one property per configured dimension. Scoring is asynchronous: it runs on the profile schedule (THREE_HOURS minimum) and there is no REST endpoint to trigger it on demand. Until the first pass completes, ln.trust_score.* reads as null.

4. CIQ read (POST /contx-iq/v1/execute): the policy's allowed_reads lists ln.trust_score.*, so the Knowledge Query can return _final_score AND every dimension. The UI uses those for the per-dimension bars - once scoring has run.

Result: the same node carries both the IKG facts (number, issued_on, ...) and the computed trustworthiness - visible side-by-side in one response.

Use case

Scenario: A claims-processing console renders one row per car license number with a trust bar broken into "validity / verification / freshness / origin / completeness" colored cells. Caseworkers spot at a glance why a low score is low - a 1.0 validity but 0.5 verification is a different problem from 0.0 validity (expired).

Captured license numbers:

- LicenseNumber(ln-kitt-0001) - issued_on 2025-09-01, valid_until 2030-09-01 (properties); number.metadata.verified_time 2026-04-12, source "state-dmv". Recently issued, currently valid, verified by an official authority.

- LicenseNumber(ln-cad-007) - issued_on 2018-02-15, valid_until 2024-02-15 (properties); number.metadata.source "user-self-attested", no verified_time. Already expired, never independently verified.

After the next scheduled pass:

- ln-kitt-0001 _TrustScore: { _final_score: 0.91, validity: 1.0, verification: 1.0, freshness: 0.92, origin: 1.0, completeness: 0.6 }

- ln-cad-007 _TrustScore: { _final_score: 0.34, validity: 0.0, verification: 0.5, freshness: 0.41, origin: 0.3, completeness: 0.5 }

The CIQ query returns both rows in one execute call so the UI can sort, filter, or page through them with no client-side combination needed.

ikg

Requirements

Prerequisites:

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

- AppAgent credentials: For Capture and CIQ execute.

- 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 comes from input_params.subject_external_id.

- This example captures its own Person -[OWNS]-> Car -[HAS]-> LicenseNumber sub-graph (alice owns kitt and caddilacv16) so the caseworker can traverse to the documents under review.

Required API access:

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

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

- POST /configs/v1/authorization-policies

- POST /configs/v1/knowledge-queries

- POST /contx-iq/v1/execute

Steps

Step 1: Create the LicenseNumber Trust Score Profile

- Schedule: THREE_HOURS (allowed values: THREE_HOURS, SIX_HOURS, TWELVE_HOURS, DAILY).

- Weights (each between 0 and 1): validity 1 dominates _final_score; verification 0.6 is the next strongest signal; freshness, origin, completeness each weight 0.3. Adjust the ratios to change behavior.

Step 2: Capture the Graph

- Ingest Person (alice), her Cars, and two LicenseNumber nodes, plus OWNS and HAS relationships.

- issued_on and valid_until are plain properties; the number property's metadata object carries the documented fields verified_time and source. Capture the same fields whether the documents are "good" or "bad" - let the dimensions render the difference.

Step 3: Create the CIQ Policy

- subject = Person (filtered by $subject_external_id), traversal goes Person -[OWNS]-> Car -[HAS]-> LicenseNumber.

- allowed_reads uses the wildcard ln.trust_score.* so every dimension property is exposed without naming each one.

Step 4: Create the Knowledge Query

- Pull car.external_id, ln.property.number, ln.trust_score._final_score, and each dimension explicitly. (You can also write ln.trust_score.* in the policy and pick fields one by one in the Knowledge Query.)

Step 5: Execute (note the async timing)

- Pass input_params.subject_external_id (alice). The traversal returns one row per license number immediately, but ln.trust_score.* will be null until the profile's first scoring pass has run. Trust scoring is asynchronous and schedule-driven (THREE_HOURS minimum); there is no REST endpoint to trigger it on demand.

Step 6: Confirm scoring has run

- GET /configs/v1/trust-score-profiles/{id} and check last_run_id / last_run_start_time / last_run_end_time. Empty last_run_id means scoring hasn't happened yet.

Step 7: Re-execute and Render

- After the scheduled pass, the SAME execute returns populated ln.trust_score.* values. Render the bars from the per-dimension values; sort or filter by _final_score.

Step 1

Trust Score Profile for LicenseNumber (flat REST body). Validity weight 1.0 is the heaviest because an expired licence should fail the vetting even if everything else is rich; other dimensions are weighted lower (all between 0 and 1). schedule is one of THREE_HOURS/SIX_HOURS/TWELVE_HOURS/DAILY.

POST https://eu.api.indykite.com/configs/v1/trust-score-profilesJson
{
  "project_id": "your_project_gid",
  "name": "licensenumber-vetting-profile",
  "display_name": "LicenseNumber Vetting Profile",
  "description": "Trust Score profile for LicenseNumber nodes used in document vetting. Validity carries the heaviest weight (1.0) because an expired licence number should drag the final score down hard even if the rest of the metadata is rich. Weights must be between 0 and 1.",
  "node_classification": "LicenseNumber",
  "schedule": "THREE_HOURS",
  "dimensions": [
    {
      "name": "VALIDITY",
      "weight": 1
    },
    {
      "name": "VERIFICATION",
      "weight": 0.6
    },
    {
      "name": "FRESHNESS",
      "weight": 0.3
    },
    {
      "name": "ORIGIN",
      "weight": 0.3
    },
    {
      "name": "COMPLETENESS",
      "weight": 0.3
    }
  ]
}

Step 2

Capture Person (alice), her Cars, and two LicenseNumber nodes. issued_on/valid_until are plain properties (feed the validity dimension); the number property's metadata object carries verified_time (verification) and source (origin) - the documented capture.Metadata fields.

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"
        }
      ]
    },
    {
      "external_id": "kitt",
      "type": "Car",
      "properties": [
        {
          "type": "model",
          "value": "Firebird"
        }
      ]
    },
    {
      "external_id": "caddilacv16",
      "type": "Car",
      "properties": [
        {
          "type": "model",
          "value": "V-16"
        }
      ]
    },
    {
      "external_id": "ln-kitt-0001",
      "type": "LicenseNumber",
      "properties": [
        {
          "type": "number",
          "value": "KITT 0001",
          "metadata": {
            "verified_time": "2026-04-12T10:00:00Z",
            "source": "state-dmv"
          }
        },
        {
          "type": "issued_on",
          "value": "2025-09-01"
        },
        {
          "type": "valid_until",
          "value": "2030-09-01"
        }
      ]
    },
    {
      "external_id": "ln-cad-007",
      "type": "LicenseNumber",
      "properties": [
        {
          "type": "number",
          "value": "CADV16-007",
          "metadata": {
            "source": "user-self-attested"
          }
        },
        {
          "type": "issued_on",
          "value": "2018-02-15"
        },
        {
          "type": "valid_until",
          "value": "2024-02-15"
        }
      ]
    }
  ]
}

Capture the relationships: alice OWNS each Car, and each Car HAS its LicenseNumber - the path the policy traverses.

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": "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"
    }
  ]
}

Step 3

CIQ Policy. allowed_reads exposes ln.trust_score.* - wildcard means every property on the attached _TrustScore node is readable, including ones added later when the profile gains a new dimension.

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.external_id",
      "ln.property.number",
      "ln.trust_score.*"
    ]
  }
}

Request to create the policy.

POST https://eu.api.indykite.com/configs/v1/authorization-policiesJson
{
  "project_id": "your_project_gid",
  "description": "CIQ policy used by a document-vetting console. The subject (signed-in caseworker) walks Person -> OWNS -> Car -> HAS -> LicenseNumber, and the policy exposes the full _TrustScore breakdown via ln.trust_score.* so the UI can render per-dimension bars (validity / verification / freshness / origin / completeness) alongside the final score.",
  "display_name": "policy - license number per-dimension trust score",
  "name": "policy-licensenumber-trust-dimensions",
  "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.external_id\",\"ln.property.number\",\"ln.trust_score.*\"]}}",
  "status": "ACTIVE",
  "tags": []
}

Step 4

Knowledge Query. Each dimension is enumerated explicitly so the UI can be sure each field is present in the response shape; alternatively, the same query works with ln.trust_score.* if the UI tolerates a variable set of keys.

knowledge_query.jsonJson
{
  "nodes": [
    "car.external_id",
    "ln.property.number",
    "ln.trust_score._final_score",
    "ln.trust_score.validity",
    "ln.trust_score.verification",
    "ln.trust_score.freshness",
    "ln.trust_score.origin",
    "ln.trust_score.completeness"
  ]
}

Request to create the Knowledge Query.

POST https://eu.api.indykite.com/configs/v1/knowledge-queriesJson
{
  "project_id": "your_project_gid",
  "description": "Document-vetting query. Returns the caller's cars, their license numbers, and the full Trust Score breakdown of each license number — final_score plus the five dimensions captured by the LicenseNumber Vetting Profile.",
  "display_name": "knowledge query - license number trust score breakdown",
  "name": "kq-licensenumber-trust-breakdown",
  "policy_id": "your_policy_gid",
  "query": "{\"nodes\":[\"car.external_id\",\"ln.property.number\",\"ln.trust_score._final_score\",\"ln.trust_score.validity\",\"ln.trust_score.verification\",\"ln.trust_score.freshness\",\"ln.trust_score.origin\",\"ln.trust_score.completeness\"]}",
  "status": "ACTIVE"
}

Step 5

Execute the query with input_params.subject_external_id = alice (plus X-IK-ClientKey and a bearer token, since the subject is a Person).

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

Immediate response - BEFORE the first scoring run. The graph traversal works (both rows return), but every ln.trust_score.* is null because no _TrustScore node has been computed yet. Trust scoring is asynchronous: it runs on the profile schedule (THREE_HOURS minimum), and there is no REST endpoint to trigger it on demand.

response_before_scoring.jsonJson
{
  "data": [
    {
      "nodes": {
        "car.external_id": "caddilacv16",
        "ln.property.number": "CADV16-007",
        "ln.trust_score._final_score": null,
        "ln.trust_score.validity": null,
        "ln.trust_score.verification": null,
        "ln.trust_score.freshness": null,
        "ln.trust_score.origin": null,
        "ln.trust_score.completeness": null
      }
    },
    {
      "nodes": {
        "car.external_id": "kitt",
        "ln.property.number": "KITT 0001",
        "ln.trust_score._final_score": null,
        "ln.trust_score.validity": null,
        "ln.trust_score.verification": null,
        "ln.trust_score.freshness": null,
        "ln.trust_score.origin": null,
        "ln.trust_score.completeness": null
      }
    }
  ]
}

Step 6

Confirm scoring has run before expecting values: read the profile and check last_run_id / last_run_start_time / last_run_end_time. Empty last_run_id means the first pass hasn't happened yet.

GET https://eu.api.indykite.com/configs/v1/trust-score-profiles/{id}Json
{
  "id": "your_trust_score_profile_gid"
}

Profile read response: last_run_* are populated once a scoring pass has completed.

response.jsonJson
{
  "id": "gid:AAAAExampleTrustScoreProfile",
  "name": "licensenumber-vetting-profile",
  "node_classification": "LicenseNumber",
  "schedule": "THREE_HOURS",
  "dimensions": [
    {
      "name": "VALIDITY",
      "weight": 1
    },
    {
      "name": "VERIFICATION",
      "weight": 0.6
    },
    {
      "name": "FRESHNESS",
      "weight": 0.3
    },
    {
      "name": "ORIGIN",
      "weight": 0.3
    },
    {
      "name": "COMPLETENESS",
      "weight": 0.3
    }
  ],
  "last_run_id": "gid:AAAAExampleLastRun",
  "last_run_start_time": "2026-05-20T09:00:00Z",
  "last_run_end_time": "2026-05-20T09:00:12Z"
}

Step 7

Re-run the SAME execute AFTER the scheduled scoring pass. Now ln.trust_score.* is populated: ln-kitt-0001 scores high; ln-cad-007 is dragged down by validity = 0 (expired) and origin = 0.3 (self-attested only).

POST https://eu.api.indykite.com/contx-iq/v1/executeJson
{
  "data": [
    {
      "nodes": {
        "car.external_id": "kitt",
        "ln.property.number": "KITT 0001",
        "ln.trust_score._final_score": 0.91,
        "ln.trust_score.validity": 1,
        "ln.trust_score.verification": 1,
        "ln.trust_score.freshness": 0.92,
        "ln.trust_score.origin": 1,
        "ln.trust_score.completeness": 0.6
      }
    },
    {
      "nodes": {
        "car.external_id": "caddilacv16",
        "ln.property.number": "CADV16-007",
        "ln.trust_score._final_score": 0.34,
        "ln.trust_score.validity": 0,
        "ln.trust_score.verification": 0.5,
        "ln.trust_score.freshness": 0.41,
        "ln.trust_score.origin": 0.3,
        "ln.trust_score.completeness": 0.5
      }
    }
  ]
}

API Endpoints

/configs/v1/trust-score-profiles
/capture/v1/nodes
/capture/v1/relationships
/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 Per-Dimension Score Document Vetting ln.trust_score.*