Back to all resources
ContX IQContX IQJson

ContX IQ: Loyalty Program - Retrieve Payment Method from License Plate

Real-world loyalty program scenario: Given a vehicle's license plate number, retrieve the credit card associated with an authorized user. Demonstrates multi-condition authorization with consent validation and loyalty plan membership checks.

ContX IQ: Loyalty Program - Retrieve Payment Method from License Plate

This example demonstrates a complex real-world authorization scenario for loyalty programs:

Input: License plate number

Output: Credit card external_id (for authorized requests only)

Authorization conditions checked (all expressed as graph patterns in the policy cypher):

1. The Application node named by $app_external_id USES a ConsentPayment that the person GRANTED

2. The person HAS the PaymentMethod, IS_MEMBER of a Loyalty plan, and OWNS a car that HAS a LicenseNumber

3. The query filter matches the LicenseNumber's number property against $license

This showcases ContX IQ's ability to enforce business rules through graph relationships.

Use case

Scenario: A parking loyalty app wants to auto-charge a registered payment method when a vehicle arrives.

Business rules enforced by the policy:

1. License plate registration: the car owner's car must HAVE a LicenseNumber in the graph

2. Loyalty membership: the owner must be a member (IS_MEMBER) of a Loyalty plan

3. Consent: the owner must have GRANTED a ConsentPayment that the requesting Application USES, and that consent must be GRANTED to the PaymentMethod

Graph structure (ingested in step 1):

Person(ole) -[OWNS]-> Car(carOle) -[HAS]-> LicenseNumber(licenseOle, number AL98745)

Person(ole) -[IS_MEMBER]-> Loyalty(loyalty1); Person(alice) -[IS_MEMBER]-> Loyalty(loyalty1)

Person(ole) -[HAS]-> PaymentMethod(cb2563)

Person(ole) -[GRANTED]-> ConsentPayment(consent1) -[GRANTED]-> PaymentMethod(cb2563)

Application(applicationParking) -[USES]-> ConsentPayment(consent1); Company(companyParking) -[OWNS]-> Application(applicationParking)

Query flow:

1. The policy cypher matches the Application ($app_external_id) that USES a ConsentPayment GRANTED by a person who HAS a PaymentMethod

2. The same person must be IS_MEMBER of a Loyalty and OWNS a car that HAS a LicenseNumber

3. The query filter keeps the LicenseNumber whose number equals $license

4. If the pattern matches, paymentmethod.external_id is returned (cb2563)

ikg

Requirements

Prerequisites:

- ServiceAccount credentials: For creating policies and queries (Bearer token)

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

Required API access:

- POST /capture/v1/nodes/ and /capture/v1/relationships/ (loyalty data)

- POST /configs/v1/authorization-policies (create policy)

- POST /configs/v1/knowledge-queries (create query)

- POST /contx-iq/v1/execute (run query)

Steps

Step 1: Ingest Loyalty Program Graph Data

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

- Action: POST nodes: Person (alice, ole), PaymentMethod (cb2563), Car (carOle), LicenseNumber (licenseOle), Loyalty (loyalty1), ConsentPayment (consent1), Company (companyParking), Application (applicationParking)

- Action: POST relationships: OWNS, HAS, IS_MEMBER, GRANTED, USES

- Result: Complete loyalty program graph ready for queries

Step 2: Create Loyalty Authorization Policy

- Authentication: ServiceAccount credential (Bearer token)

- Action: POST policy with subject type _Application and cypher:

- MATCH (app:Application)-[:USES]->(consentpayment:ConsentPayment)<-[:GRANTED]-(person:Person)-[:HAS]->(paymentmethod:PaymentMethod)

- MATCH (person)-[:IS_MEMBER]->(loyalty:Loyalty)

- MATCH (person)-[:OWNS]->(car:Car)-[:HAS]->(ln:LicenseNumber)

- Filter: app.external_id = $app_external_id AND subject.external_id = $_appId

- Allowed reads: ln.*, app.*, paymentmethod.external_id

- Result: Policy ID returned

Step 3: Create Loyalty Query

- Authentication: ServiceAccount credential (Bearer token)

- Action: POST query that returns paymentmethod.external_id, with filter ln.property.number = $license

- Input parameters: $license (string), $app_external_id (policy filter)

- Result: Query ID returned

Step 4: Execute Loyalty Query

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

- Action: POST to /contx-iq/v1/execute with input_params license = AL98745 and app_external_id = applicationParking

- Result: paymentmethod.external_id = cb2563 (if the pattern matches) or an empty data array (if not)

Step 5: Cleanup

- Action: DELETE query and policy configurations

Step 1a

Capture loyalty program nodes: Person (ole, the car owner, and alice), Car, LicenseNumber, Loyalty, ConsentPayment, PaymentMethod, Company and Application.

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": "ole",
      "is_identity": true,
      "type": "Person",
      "properties": [
        {
          "type": "email",
          "value": "ole@yahoo.co.uk"
        },
        {
          "type": "given_name",
          "value": "ole"
        },
        {
          "type": "last_name",
          "value": "einar"
        }
      ]
    },
    {
      "external_id": "cb2563",
      "type": "PaymentMethod",
      "properties": [
        {
          "type": "payment_name",
          "value": "Credit Card Parking"
        },
        {
          "type": "preference",
          "value": "Pay as you go"
        }
      ]
    },
    {
      "external_id": "carOle",
      "type": "Car",
      "properties": [
        {
          "type": "category",
          "value": "Car"
        },
        {
          "type": "is_active",
          "value": true
        },
        {
          "type": "vin",
          "value": "pcfjnm78"
        }
      ]
    },
    {
      "external_id": "licenseOle",
      "type": "LicenseNumber",
      "properties": [
        {
          "type": "status",
          "value": "Active"
        },
        {
          "type": "number",
          "value": "AL98745",
          "metadata": {
            "assurance_level": 3,
            "source": "BRREG"
          }
        }
      ]
    },
    {
      "external_id": "loyalty1",
      "type": "Loyalty",
      "properties": [
        {
          "type": "name",
          "value": "Parking Loyalty Plan"
        }
      ]
    },
    {
      "external_id": "consent1",
      "type": "ConsentPayment",
      "properties": [
        {
          "type": "name",
          "value": "Consent Parking"
        }
      ]
    },
    {
      "external_id": "companyParking",
      "type": "Company",
      "properties": [
        {
          "type": "name",
          "value": "City Parking Inc"
        }
      ]
    },
    {
      "external_id": "applicationParking",
      "type": "Application",
      "properties": [
        {
          "type": "name",
          "value": "City Mall Parking"
        }
      ]
    }
  ]
}

Step 1b

Capture loyalty relationships: Person -[OWNS]-> Car, Car -[HAS]-> LicenseNumber, Person -[IS_MEMBER]-> Loyalty, Person -[GRANTED]-> ConsentPayment -[GRANTED]-> PaymentMethod, Person -[HAS]-> PaymentMethod, Application -[USES]-> ConsentPayment, Company -[OWNS]-> Application.

POST https://eu.api.indykite.com/capture/v1/relationships/Json
{
  "relationships": [
    {
      "source": {
        "external_id": "ole",
        "type": "Person"
      },
      "target": {
        "external_id": "cb2563",
        "type": "PaymentMethod"
      },
      "type": "HAS"
    },
    {
      "source": {
        "external_id": "ole",
        "type": "Person"
      },
      "target": {
        "external_id": "carOle",
        "type": "Car"
      },
      "type": "OWNS"
    },
    {
      "source": {
        "external_id": "ole",
        "type": "Person"
      },
      "target": {
        "external_id": "loyalty1",
        "type": "Loyalty"
      },
      "type": "IS_MEMBER"
    },
    {
      "source": {
        "external_id": "alice",
        "type": "Person"
      },
      "target": {
        "external_id": "loyalty1",
        "type": "Loyalty"
      },
      "type": "IS_MEMBER"
    },
    {
      "source": {
        "external_id": "ole",
        "type": "Person"
      },
      "target": {
        "external_id": "consent1",
        "type": "ConsentPayment"
      },
      "type": "GRANTED"
    },
    {
      "source": {
        "external_id": "carOle",
        "type": "Car"
      },
      "target": {
        "external_id": "licenseOle",
        "type": "LicenseNumber"
      },
      "type": "HAS"
    },
    {
      "source": {
        "external_id": "consent1",
        "type": "ConsentPayment"
      },
      "target": {
        "external_id": "cb2563",
        "type": "PaymentMethod"
      },
      "type": "GRANTED"
    },
    {
      "source": {
        "external_id": "companyParking",
        "type": "Company"
      },
      "target": {
        "external_id": "applicationParking",
        "type": "Application"
      },
      "type": "OWNS"
    },
    {
      "source": {
        "external_id": "applicationParking",
        "type": "Application"
      },
      "target": {
        "external_id": "consent1",
        "type": "ConsentPayment"
      },
      "type": "USES"
    }
  ]
}

Step 2a

Policy JSON with multi-condition authorization expressed in the cypher: (1) the Application named by $app_external_id USES a ConsentPayment GRANTED by the person who HAS the PaymentMethod, (2) the person IS_MEMBER of a Loyalty, (3) the person OWNS a car that HAS a LicenseNumber. Grants READ on ln.*, app.* and paymentmethod.external_id.

policy.jsonJson
{
  "meta": {
    "policy_version": "1.0-ciq"
  },
  "subject": {
    "type": "_Application"
  },
  "condition": {
    "cypher": "MATCH (subject:_Application) MATCH (app:Application)-[:USES]->(consentpayment:ConsentPayment)<-[:GRANTED]-(person:Person)-[:HAS]->(paymentmethod:PaymentMethod) MATCH (person)-[:IS_MEMBER]->(loyalty:Loyalty) MATCH (person)-[:OWNS]->(car:Car)-[:HAS]->(ln:LicenseNumber)",
    "filter": [
      {
        "operator": "AND",
        "operands": [
          {
            "attribute": "app.external_id",
            "operator": "=",
            "value": "$app_external_id"
          },
          {
            "attribute": "subject.external_id",
            "operator": "=",
            "value": "$_appId"
          }
        ]
      }
    ]
  },
  "allowed_reads": {
    "nodes": [
      "ln.*",
      "app.*",
      "paymentmethod.external_id"
    ]
  }
}

Step 2b

POST request to create the loyalty authorization policy.

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\":\"_Application\"},\"condition\":{\"cypher\":\"MATCH (subject:_Application) MATCH (app:Application)-[:USES]->(consentpayment:ConsentPayment)<-[:GRANTED]-(person:Person)-[:HAS]->(paymentmethod:PaymentMethod) MATCH (person)-[:IS_MEMBER]->(loyalty:Loyalty) MATCH (person)-[:OWNS]->(car:Car)-[:HAS]->(ln:LicenseNumber)\",\"filter\":[{\"operator\":\"AND\",\"operands\":[{\"attribute\":\"app.external_id\",\"operator\":\"=\",\"value\":\"$app_external_id\"},{\"attribute\":\"subject.external_id\",\"operator\":\"=\",\"value\":\"$_appId\"}]}]},\"allowed_reads\":{\"nodes\":[\"ln.*\",\"app.*\",\"paymentmethod.external_id\"]}}",
  "status": "ACTIVE",
  "tags": []
}

Step 2c

GET request to verify the policy was created.

GET https://eu.api.indykite.com/configs/v1/authorization-policies/{policy_id}Json
{
  "id": "your_policy_configuration_gid"
}

Step 3a

Query JSON that returns paymentmethod.external_id, filtered on ln.property.number = $license; the graph traversal itself comes from the policy cypher.

knowledge_query.jsonJson
{
  "nodes": [
    "paymentmethod.external_id"
  ],
  "filter": {
    "attribute": "ln.property.number",
    "operator": "=",
    "value": "$license"
  }
}

Step 3b

POST request to create the loyalty query.

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\":[\"paymentmethod.external_id\"],\"filter\":{\"attribute\":\"ln.property.number\",\"operator\":\"=\",\"value\":\"$license\"}}",
  "status": "ACTIVE"
}

Step 3c

GET request to verify the query was created.

GET https://eu.api.indykite.com/configs/v1/knowledge-queries/{query_id}Json
{
  "id": "your_knowledge_query_configuration_gid"
}

Step 4a

Execute the loyalty query with a license plate value. The system checks all authorization conditions before returning the credit card ID.

POST https://eu.api.indykite.com/contx-iq/v1/executeJson
{
  "id": "knowledge_query_gid",
  "input_params": {
    "license": "AL98745",
    "app_external_id": "applicationParking"
  }
}

Step 4b

Response containing the payment method external_id (cb2563). Empty data array if the graph pattern does not match (e.g., no consent GRANTED to the Application, no loyalty membership, or no LicenseNumber with that number).

response.jsonJson
{
  "data": [
    {
      "nodes": {
        "paymentmethod.external_id": "cb2563"
      }
    }
  ]
}

Step 5a

DELETE request to remove the query.

DELETE https://eu.api.indykite.com/configs/v1/knowledge-queries/{query_id}Json
{
  "id": "your_knowledge_query_configuration_gid"
}

Step 5b

DELETE request to remove the policy.

DELETE https://eu.api.indykite.com/configs/v1/authorization-policies/{policy_id}Json
{
  "id": "your_policy_configuration_gid"
}