User Context
Scope data to tenants and users with context filters and row-level security.
Why you need user context
Section titled “Why you need user context”In most production applications, multiple tenants (organisations, teams, or users) share the same database. Without proper scoping, a query from one tenant could return another tenant’s data.
User context solves this by attaching context to every conversation. Inconvo uses this context to automatically apply filters on every query, ensuring data isolation at the query level.
Defining context fields
Section titled “Defining context fields”Context fields are defined in your agent’s configuration. Each field represents a key that will be passed when creating a conversation. Common examples include:
organisationId— scope data to a specific organisationteamId— scope data to a team within an organisationuserId— scope data to a specific user
You can define context fields in the Inconvo dashboard under your agent’s Configure page, or via the CLI.
Context filters on tables
Section titled “Context filters on tables”Context filters are set on a per-table basis, where a mapping is made between a column in the table and a key in the context object.
For example, if you have a table orders with a column organisation_id which is your tenant identifier, set the following context filter on the orders table:
WHERE orders.organisation_id = userContext.organisationId;Passing userContext when creating conversations
Section titled “Passing userContext when creating conversations”When creating a conversation via the API, pass the userContext object in the request body. The values you provide will be used to evaluate context filters on every query within that conversation.
import Inconvo from "@inconvoai/node";
const inconvo = new Inconvo({ apiKey: process.env.INCONVO_API_KEY,});
const conversation = await inconvo.agents.conversations.create( process.env.INCONVO_AGENT_ID, { userIdentifier: "user_123", userContext: { organisationId: 1, }, },);Within this conversation, data returned from the orders table will be scoped to the organisation with an id of 1:
WHERE orders.organisation_id = 1;Row-level security patterns
Section titled “Row-level security patterns”Single-tenant filter
Section titled “Single-tenant filter”The simplest pattern — set a context filter on every queryable table with the tenant identifier:
WHERE orders.organisation_id = userContext.organisationId;WHERE users.organisation_id = userContext.organisationId;WHERE products.organisation_id = userContext.organisationId;Multi-level scoping
Section titled “Multi-level scoping”Combine multiple context fields for finer-grained access:
-- Organisation-level tablesWHERE orders.organisation_id = userContext.organisationId;
-- Team-level tablesWHERE projects.team_id = userContext.teamId;User-level scoping
Section titled “User-level scoping”For tables that should only return rows belonging to a specific user:
WHERE notes.user_id = userContext.userId;