KBAC: Batch Authorization with Multiple Evaluations (Boxcarring)
This example demonstrates batch authorization using the authZEN Access Evaluations API:
What is boxcarring?
Multiple authorization questions combined into a single API request, reducing network round-trips.
Example batch request:
- "Can Alice drive Car1?"
- "Can Alice ride Bus1?"
- "Can Bob drive Car1?"
All answered in ONE API call.
API reference: https://openid.net/specs/authorization-api-1_0-03.html#name-access-evaluations-api
Request structure:
- Default subject/action can be set at the top level
- Each evaluation in the array can override defaults
- Response contains decision for each evaluation in order
Use case
Scenario: A transportation app needs to check multiple permissions when a user opens the app.
Policies defined:
1. DRIVE policy: Person can DRIVE Car if DRIVES relationship exists
2. RIDE policy: Person can RIDE Bus if HAS_TICKET relationship exists
Single batch request checks:
- Can Alice drive Car1? (uses DRIVE policy)
- Can Alice drive Car2? (uses DRIVE policy)
- Can Alice ride Bus1? (uses RIDE policy)
Response (in order):
- {decision: true}
- {decision: false}
- {decision: true}
Performance benefit: 3 authorization decisions with 1 API call instead of 3.

Requirements
Prerequisites:
- ServiceAccount credentials: For creating policies (Bearer token)
- AppAgent credentials: For data ingestion and authorization queries (X-IK-ClientKey)
Required API access:
- POST /capture/v1/nodes/ and /capture/v1/relationships/ (graph data)
- POST /configs/v1/authorization-policies (create policies - called twice)
- POST /access/v1/evaluations (batch evaluation endpoint - note plural)
Steps
Step 1: Ingest Graph Data
- Authentication: AppAgent credential (X-IK-ClientKey header)
- Action: POST nodes (Person, Car, Bus) and relationships (DRIVES, HAS_TICKET)
- Result: Graph ready for authorization queries
Step 2: Create Multiple KBAC Policies
- Authentication: ServiceAccount credential (Bearer token)
- Action: POST DRIVE policy (Person -[DRIVES]-> Car = can drive)
- Action: POST RIDE policy (Person -[HAS_TICKET]-> Bus = can ride)
- Result: Two policy IDs returned
Step 3: Run Batch Evaluation
- Authentication: AppAgent credential (X-IK-ClientKey header)
- Action: POST to /access/v1/evaluations (plural) with array of checks
- Request format:
{
subject: {type: "Person", id: "alice"}, // default subject
evaluations: [
{action: {name: "drive"}, resource: {type: "Car", id: "car1"}},
{action: {name: "drive"}, resource: {type: "Car", id: "car2"}},
{action: {name: "ride"}, resource: {type: "Bus", id: "bus1"}}
]
}
- Result: Array of decisions matching input order
Step 5: Cleanup
- Action: DELETE both policy configurations
Step 1
Capture the nodes needed for this use case.
{
"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": "ryan",
"is_identity": true,
"type": "Person",
"properties": [
{
"type": "email",
"value": "ryan@yahoo.co.uk"
},
{
"type": "given_name",
"value": "ryan"
},
{
"type": "last_name",
"value": "mushu"
}
]
},
{
"external_id": "tilda",
"is_identity": true,
"type": "Person",
"properties": [
{
"type": "email",
"value": "tilda@yahoo.co.uk"
},
{
"type": "given_name",
"value": "tilda"
},
{
"type": "last_name",
"value": "mushu"
}
]
},
{
"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": "Insurance"
},
{
"type": "status",
"value": "Active"
},
{
"type": "number",
"value": "hfgrten123",
"metadata": {
"assurance_level": 3,
"source": "BRREG"
}
}
]
},
{
"external_id": "ct234",
"type": "Contract",
"properties": [
{
"type": "category",
"value": "Insurance"
},
{
"type": "status",
"value": "Active"
},
{
"type": "number",
"value": "hfgrten234",
"metadata": {
"assurance_level": 3,
"source": "BRREG"
}
}
]
},
{
"external_id": "ct985",
"type": "Contract",
"properties": [
{
"type": "category",
"value": "Insurance"
},
{
"type": "status",
"value": "Active"
},
{
"type": "number",
"value": "hfgrten985",
"metadata": {
"assurance_level": 3,
"source": "BRREG"
}
}
]
},
{
"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",
"value": "kdcbfrt178"
}
]
},
{
"external_id": "truck1",
"type": "Vehicle",
"properties": [
{
"type": "category",
"value": "Truck"
},
{
"type": "is_active",
"value": true
},
{
"type": "vin",
"value": "sncnrkcldp"
}
]
},
{
"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": "license3",
"type": "LicenseNumber",
"properties": [
{
"type": "status",
"value": "Active"
},
{
"type": "number",
"value": "VN123456",
"metadata": {
"assurance_level": 3,
"source": "BRREG"
}
}
]
},
{
"external_id": "company1",
"type": "Company",
"properties": [
{
"type": "name",
"value": "Company1"
},
{
"type": "registration",
"value": "256314523"
}
]
},
{
"external_id": "company2",
"type": "Company",
"properties": [
{
"type": "name",
"value": "Company2"
},
{
"type": "registration",
"value": "942365123"
}
]
},
{
"external_id": "application1",
"type": "Application",
"properties": [
{
"type": "name",
"value": "Application"
}
]
},
{
"external_id": "application2",
"type": "Application",
"properties": [
{
"type": "name",
"value": "Application2"
}
]
}
]
}Capture the relationships needed for this use case.
{
"relationships": [
{
"source": {
"external_id": "ryan",
"type": "Person"
},
"target": {
"external_id": "cb123",
"type": "PaymentMethod"
},
"type": "HAS"
},
{
"source": {
"external_id": "tilda",
"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": "ryan",
"type": "Person"
},
"target": {
"external_id": "ct123",
"type": "Contract"
},
"type": "ACCEPTED"
},
{
"source": {
"external_id": "tilda",
"type": "Person"
},
"target": {
"external_id": "ct234",
"type": "Contract"
},
"type": "ACCEPTED"
},
{
"source": {
"external_id": "alice",
"type": "Person"
},
"target": {
"external_id": "ct985",
"type": "Contract"
},
"type": "ACCEPTED"
},
{
"source": {
"external_id": "ct123",
"type": "Contract"
},
"target": {
"external_id": "car1",
"type": "Vehicle"
},
"type": "COVERS"
},
{
"source": {
"external_id": "ct985",
"type": "Contract"
},
"target": {
"external_id": "car1",
"type": "Vehicle"
},
"type": "COVERS"
},
{
"source": {
"external_id": "ct234",
"type": "Contract"
},
"target": {
"external_id": "truck1",
"type": "Vehicle"
},
"type": "COVERS"
},
{
"source": {
"external_id": "car1",
"type": "Vehicle"
},
"target": {
"external_id": "license1",
"type": "LicenseNumber"
},
"type": "HAS"
},
{
"source": {
"external_id": "truck1",
"type": "Vehicle"
},
"target": {
"external_id": "license2",
"type": "LicenseNumber"
},
"type": "HAS"
},
{
"source": {
"external_id": "car2",
"type": "Vehicle"
},
"target": {
"external_id": "license3",
"type": "LicenseNumber"
},
"type": "HAS"
},
{
"source": {
"external_id": "company1",
"type": "Company"
},
"target": {
"external_id": "car1",
"type": "Vehicle"
},
"type": "OWNS"
},
{
"source": {
"external_id": "company1",
"type": "Company"
},
"target": {
"external_id": "car2",
"type": "Vehicle"
},
"type": "OWNS"
},
{
"source": {
"external_id": "company1",
"type": "Company"
},
"target": {
"external_id": "truck1",
"type": "Vehicle"
},
"type": "OWNS"
},
{
"source": {
"external_id": "application1",
"type": "Application"
},
"target": {
"external_id": "company1",
"type": "Company"
},
"type": "HAS_AGREEMENT_WITH"
},
{
"source": {
"external_id": "application2",
"type": "Application"
},
"target": {
"external_id": "company1",
"type": "Company"
},
"type": "HAS_AGREEMENT_WITH"
}
]
}Step 2
KBAC Policy which rules that a Person node can drive a car if the Person node has a relation DRIVES with a car in json format.
{
"meta": {
"policy_version": "2.0-kbac"
},
"subject": {
"type": "Person"
},
"actions": [
"CAN_DRIVE"
],
"resource": {
"type": "Car"
},
"condition": {
"cypher": "MATCH (subject:Person)-[:DRIVES]->(resource:Car)"
}
}Request to create the KBAC Policy configuration using REST.
{
"project_id": "your_project_gid",
"description": "description of policy",
"display_name": "policy name",
"name": "policy-name",
"policy": "{\"meta\":{\"policy_version\":\"2.0-kbac\"},\"subject\":{\"type\":\"Person\"},\"actions\":[\"CAN_DRIVE\"],\"resource\":{\"type\":\"Car\"},\"condition\":{\"cypher\":\"MATCH (subject:Person)-[:DRIVES]->(resource:Car)\"}}",
"status": "ACTIVE",
"tags": []
}Request to create the KBAC Policy configuration using Python.
import http.client
conn = http.client.HTTPSConnection("eu.api.indykite.com")
payload = "{"description": "",
"display_name": "",
"name": "",
"policy": "",
"project_id": "",
"status": "ACTIVE",
"tags": [
""
]}"
headers = {
'Content-Type': "application/json",
'Authorization': "YOUR_SECRET_TOKEN"
}
conn.request("POST", "/configs/v1/authorization-policies", payload, headers)
res = conn.getresponse()
data = res.read()
Request to read the KBAC Policy configuration using REST
{
"id": "your_policy_configuration_gid"
}Request to read the KBAC Policy configuration using Python.
import http.client
conn = http.client.HTTPSConnection("eu.api.indykite.com")
headers = { 'Authorization': "YOUR_SECRET_TOKEN" }
conn.request("GET", "/configs/v1/authorization-policies/{{id}}", headers=headers)
res = conn.getresponse()
data = res.read()
KBAC Policy which rules that a Person node can ride a bus if the Person node has a ticket for the bus in json format.
{
"meta": {
"policy_version": "2.0-kbac"
},
"subject": {
"type": "Person"
},
"actions": [
"CAN_RIDE"
],
"resource": {
"type": "Bus"
},
"condition": {
"cypher": "MATCH (subject)-[:HAS]->(ticket:Ticket)-[:FOR]->(resource)"
}
}Request to create the KBAC Policy configuration using REST.
{
"project_id": "your_project_gid",
"description": "description of policy",
"display_name": "policy name",
"name": "policy-name",
"policy": "{\"meta\":{\"policy_version\":\"2.0-kbac\"},\"subject\":{\"type\":\"Person\"},\"actions\":[\"CAN_RIDE\"],\"resource\":{\"type\":\"Bus\"},\"condition\":{\"cypher\":\"MATCH (subject)-[:HAS]->(ticket:Ticket)-[:FOR]->(resource)\"}}",
"status": "ACTIVE",
"tags": []
}Request to create the KBAC Policy configuration using Python.
import http.client
conn = http.client.HTTPSConnection("eu.api.indykite.com")
payload = "{"description": "",
"display_name": "",
"name": "",
"policy": "",
"project_id": "",
"status": "ACTIVE",
"tags": [
""
]}"
headers = {
'Content-Type': "application/json",
'Authorization': "YOUR_SECRET_TOKEN"
}
conn.request("POST", "/configs/v1/authorization-policies", payload, headers)
res = conn.getresponse()
data = res.read()
Request to read the KBAC Policy configuration using REST.
{
"id": "your_policy_configuration_gid"
}Request to read the KBAC Policy configuration using Python.
import http.client
conn = http.client.HTTPSConnection("eu.api.indykite.com")
headers = { 'Authorization': "YOUR_SECRET_TOKEN" }
conn.request("GET", "/configs/v1/authorization-policies/{{id}}", headers=headers)
res = conn.getresponse()
data = res.read()
Step 3
Json to run KBAC access multi-evaluations.
The elements outside evaluations are the default values.
Here we have a default value for subject and for action.
If a top-level key is designated in the evaluations array then the value of that will take precedence over the default value.
{
"subject": {
"type": "Person",
"id": "karel"
},
"action": {
"name": "CAN_DRIVE"
},
"evaluations": [
{
"subject": {
"type": "Person",
"id": "knightrider"
},
"resource": {
"type": "Car",
"id": "kitt"
}
},
{
"subject": {
"type": "Person",
"id": "knightrider"
},
"resource": {
"type": "Car",
"id": "caddilacv16"
}
},
{
"resource": {
"type": "Bus",
"id": "harmonika"
},
"action": {
"name": "CAN_RIDE"
}
},
{
"resource": {
"type": "Bus",
"id": "harmonika"
}
}
]
}Response to the KBAC Evaluations endpoint request.
{
"evaluations": [
{
"decision": true
},
{
"decision": false
},
{
"decision": true
},
{
"decision": false
}
]
}Request to run the KBAC Evaluations endpoint.
import http.client
conn = http.client.HTTPSConnection("eu.api.indykite.com")
payload = "{
"subject": {"type": "Person",
"id": "karel"},
"action": {"name": "CAN_DRIVE"},
"evaluations": [
{
"subject": {"type": "Person",
"id": "knightrider"},
"resource": {"type": "Car",
"id": "kitt"}
},
{
"subject": {"type": "Person",
"id": "knightrider"},
"resource": {"type": "Car",
"id": "caddilacv16"}
},
{
"resource": {"type": "Bus",
"id": "harmonika"},
"action": {"name": "CAN_RIDE"}
},
{
"resource": {"type": "Bus",
"id": "harmonika"}
}
]
}"
headers = {
'Content-Type': "application/json",
'X-IK-ClientKey': ""
}
conn.request("POST", "/access/v1/evaluations", payload, headers)
res = conn.getresponse()
data = res.read()
Step 5
Delete the KBAC Policies.
{
"id": "your_policy_configuration_gid"
}Request to delete the KBAC Policies.
import http.client
conn = http.client.HTTPSConnection("eu.api.indykite.com")
headers = { 'Authorization': "Bearer ...", 'Content-Type': "application/json" }
conn.request("DELETE", "/configs/v1/authorization-policies/{id}", headers=headers)
res = conn.getresponse()
data = res.read()
API Endpoints
/capture/v1/nodes /capture/v1/relationships /configs/v1/authorization-policies /kbac/v1/is-authorized