Back to all resources
EnvironmentEnvironmentJson

Data Residency: Composite-Database Project with Location-Routed Ingestion

Set up a project backed by a customer-hosted composite database, create the application hierarchy and credentials, then ingest nodes routed to specific locations and relationships stored in the global database.

Data Residency: Composite-Database Project with Location-Routed Ingestion

This example walks through the full data-residency flow end to end:

1. Create a Project connected to your composite database (composite_db_name + alias_mapping)

2. Poll the project until ikg_status is ACTIVE

3. Create an Application, an Application Agent, and Application Agent Credentials

4. Upsert nodes with a per-node location so each node's data is stored in its constituent database

5. Upsert relationships with use_global_db so cross-location connections land in the global database

The result is one logical Identity Knowledge Graph whose records physically live in the locations you chose.

Use case

Scenario: Your application serves customers in two jurisdictions and each customer's

personal data must stay in its region, while shared infrastructure records and the

relationships connecting everything remain globally reachable:

- Person(alice) stored in the "east" location database

- Person(karel) stored in the "west" location database

- Device(hq-badge-printer) stored in the "global" database

- Car(car1) has no location and goes to the default database

- OWNS / USES relationships stored in the global database, connecting nodes across locations

Applications keep querying a single graph; the composite database handles the placement.

Note on reads: ContX IQ queries and KBAC/AuthZEN checks with 2.0-kbac policies execute

against the default constituent only (db_connection.name - the global database in this

example). Decisions that traverse proxy nodes and relationships keep working; the located

property data (alice's and karel's personal details) is not visible to those queries or

policy conditions. To evaluate authorization inside a location constituent, author the

policy as 3.0-kbac with USE graph.byName() routing - see resources authz-7 and authz-8.

Requirements

Prerequisites:

- ServiceAccount credentials: Created in IndyKite Hub for your Organization

- Organization ID: Your IndyKite organization identifier

- A customer-hosted Neo4j deployment where the composite database and its constituent

databases (db1, db2, db3 in this example) already exist. IndyKite connects to them

and configures routing - it does not create the databases. Composite databases are

available for customer-hosted IKGs only.

Required API access:

- POST /configs/v1/projects (create project)

- GET /configs/v1/projects/{id} (poll ikg_status)

- POST /configs/v1/applications (create application)

- POST /configs/v1/application-agents (create agent)

- POST /configs/v1/application-agent-credentials (generate credentials)

- POST /capture/v1/nodes (upsert located nodes)

- POST /capture/v1/relationships (upsert relationships to the global DB)

Steps

Step 1: Create the Project with the composite connection

- Authentication: ServiceAccount credential as Bearer token in Authorization header

- Action: POST to /configs/v1/projects with db_connection carrying composite_db_name and alias_mapping

- Input: alias_mapping maps logical locations to constituent databases (global=db1&east=db2&west=db3);

db_connection.name is the default constituent used for nodes without a location

- Result: Project ID returned; IKG provisioning starts asynchronously

Step 2: Poll the Project until the IKG is ACTIVE

- Authentication: ServiceAccount credential as Bearer token

- Action: GET /configs/v1/projects/{id} until ikg_status is ACTIVE

- Result: Response echoes db_connection.composite_db_name and alias_mapping; PENDING means

provisioning is still running, FAILED means it did not complete

Step 3: Create an Application

- Authentication: ServiceAccount credential as Bearer token

- Action: POST to /configs/v1/applications with the Project ID from Step 1

- Result: Application ID returned

Step 4: Create an Application Agent

- Authentication: ServiceAccount credential as Bearer token

- Action: POST to /configs/v1/application-agents with the Application ID from Step 3

- Result: Application Agent ID returned

Step 5: Generate Application Agent Credentials

- Authentication: ServiceAccount credential as Bearer token

- Action: POST to /configs/v1/application-agent-credentials with the Agent ID from Step 4

- Result: Credential JSON containing the API key (X-IK-ClientKey value); save it securely,

it cannot be retrieved again

Step 6: Upsert nodes with locations

- Authentication: AppAgent credential as API key (header: X-IK-ClientKey)

- Action: POST to /capture/v1/nodes with an optional location per node (a key of alias_mapping)

- Result: Each located node's full data is stored in its constituent database and a lightweight

proxy node (external_id, type, location only) is upserted into the global database;

nodes without a location go to the default database

Step 7: Upsert relationships into the global database

- Authentication: AppAgent credential as API key (header: X-IK-ClientKey)

- Action: POST to /capture/v1/relationships with use_global_db set to true

- Result: Relationships are created in the global database between proxy nodes, connecting

entities whose data lives in different locations

Step 1

POST request to create a project connected to a customer-hosted composite database. The composite database (ikcomposite) and its constituents (db1, db2, db3) must already exist in Neo4j. The alias_mapping keys (global, east, west) are the location values used later in Capture API requests.

POST https://eu.api.indykite.com/configs/v1/projectsJson
{
  "db_connection": {
    "url": "neo4j://your-neo4j-host:7687",
    "username": "neo4j",
    "password": "example-password",
    "name": "db1",
    "composite_db_name": "ikcomposite",
    "alias_mapping": "global=db1&east=db2&west=db3"
  },
  "description": "Project with a customer-hosted composite database for data residency",
  "display_name": "Data residency project",
  "name": "residency-project",
  "organization_id": "gid-of-organization",
  "region": "europe-west1"
}

Step 2

GET request to poll the project until ikg_status is ACTIVE. Provisioning runs asynchronously; do not ingest data before the IKG is active. Example response shown.

GET https://eu.api.indykite.com/configs/v1/projects/{id}Json
{
  "id": "gid-of-project",
  "name": "residency-project",
  "region": "europe-west1",
  "ikg_status": "ACTIVE",
  "db_connection": {
    "url": "neo4j://your-neo4j-host:7687",
    "username": "neo4j",
    "name": "db1",
    "composite_db_name": "ikcomposite",
    "alias_mapping": "global=db1&east=db2&west=db3"
  }
}

Step 3

POST request to create an application within the project. The application represents your software system that will use IndyKite services.

POST https://eu.api.indykite.com/configs/v1/applicationsJson
{
  "description": "Application description",
  "display_name": "Application name",
  "name": "app-name",
  "project_id": "gid-of-project"
}

Step 4

POST request to create an application agent. The agent is the authenticated identity that your application uses to call IndyKite APIs.

POST https://eu.api.indykite.com/configs/v1/application-agentsJson
{
  "api_permissions": [
    "Authorization",
    "Capture",
    "ContXIQ",
    "EntityMatching"
  ],
  "application_id": "gid-of-application",
  "description": "App Agent description",
  "display_name": "App Agent name",
  "name": "app-agent-name"
}

Step 5

POST request to generate credentials for the application agent. The response contains the API key to use in the X-IK-ClientKey header. Save this securely - it cannot be retrieved again.

POST https://eu.api.indykite.com/configs/v1/application-agent-credentialsJson
{
  "application_agent_id": "gid-of-app-agent",
  "display_name": "AppAgent Credentials name",
  "expire_time": "2026-12-31T12:34:56-01:00"
}

Step 6

POST request to upsert nodes with per-node locations. alice is stored in the east constituent, karel in west, the shared device in global, and car1 (no location) in the default database. Proxy nodes for all located nodes are automatically upserted into the global database.

POST https://eu.api.indykite.com/capture/v1/nodesJson
{
  "nodes": [
    {
      "external_id": "alice",
      "is_identity": true,
      "type": "Person",
      "location": "east",
      "properties": [
        {
          "type": "email",
          "value": "alice@email.com"
        },
        {
          "type": "given_name",
          "value": "Alice"
        }
      ]
    },
    {
      "external_id": "karel",
      "is_identity": true,
      "type": "Person",
      "location": "west",
      "properties": [
        {
          "type": "email",
          "value": "karel@email.com"
        },
        {
          "type": "given_name",
          "value": "Karel"
        }
      ]
    },
    {
      "external_id": "hq-badge-printer",
      "is_identity": false,
      "type": "Device",
      "location": "global",
      "properties": [
        {
          "type": "model",
          "value": "BP-200"
        }
      ]
    },
    {
      "external_id": "car1",
      "is_identity": false,
      "type": "Car",
      "properties": [
        {
          "type": "manufacturer",
          "value": "Pontiac"
        }
      ]
    }
  ]
}

Step 7

POST request to upsert relationships with use_global_db. The OWNS and USES relationships are stored in the global database, connecting nodes whose full data lives in different location databases.

POST https://eu.api.indykite.com/capture/v1/relationshipsJson
{
  "use_global_db": true,
  "relationships": [
    {
      "source": {
        "external_id": "alice",
        "type": "Person"
      },
      "target": {
        "external_id": "car1",
        "type": "Car"
      },
      "type": "OWNS"
    },
    {
      "source": {
        "external_id": "karel",
        "type": "Person"
      },
      "target": {
        "external_id": "hq-badge-printer",
        "type": "Device"
      },
      "type": "USES"
    }
  ]
}

API Endpoints

/configs/v1/projects
/configs/v1/applications
/configs/v1/application-agents
/configs/v1/application-agent-credentials
/capture/v1/nodes
/capture/v1/relationships
View OpenAPI docs

Tags

Data ResidencyComposite DatabaseEnvironment SetupData IngestionCapture API