Data Security

Control which rows and columns each user can see, with row-level and column-level security policies.

Project availability: ✅ Classic projects • ✅ Agentic projects

Wren AI's data security controls which rows and columns each user can see in their answers. The Data Security API manages the same settings as the Data Security page in Wren AI, so you can keep access rules in version control and apply them to another project the same way every time.

Data security is made up of three building blocks:

  • Session Properties: named values such as region or role that describe a user
  • Policies: row-level security (RLS) policies filter rows, and column-level security (CLS) policies hide columns, based on a user's session properties
  • Assigned Values: the value of each session property for a project member or a group

Endpoints Overview

Session Properties

  • GET /v2/projects/{projectId}/data_security/session_properties: List session properties.
  • POST /v2/projects/{projectId}/data_security/session_properties: Create a session property.
  • GET /v2/projects/{projectId}/data_security/session_properties/{id}: Get a session property.
  • PATCH /v2/projects/{projectId}/data_security/session_properties/{id}: Update a session property's description.
  • DELETE /v2/projects/{projectId}/data_security/session_properties/{id}: Delete a session property and its assigned values.

Policies

  • GET /v2/projects/{projectId}/data_security/policies: List RLS and CLS policies.
  • POST /v2/projects/{projectId}/data_security/policies: Create a policy.
  • GET /v2/projects/{projectId}/data_security/policies/{id}: Get a policy.
  • PATCH /v2/projects/{projectId}/data_security/policies/{id}: Update a policy.
  • DELETE /v2/projects/{projectId}/data_security/policies/{id}: Delete a policy.

Assigned Values

  • GET /v2/projects/{projectId}/data_security/member_values: List the values assigned to members.
  • PATCH /v2/projects/{projectId}/data_security/member_values: Set values for up to 500 members.
  • GET /v2/projects/{projectId}/data_security/group_values: List the values assigned to groups.
  • PATCH /v2/projects/{projectId}/data_security/group_values: Set values for up to 500 groups.
  • GET /v2/projects/{projectId}/data_security/effective_values?email=: Show the values that apply to one user, and where each comes from.

Export and Import

  • GET /v2/projects/{projectId}/data_security: Export the whole setup as one document.
  • POST /v2/projects/{projectId}/data_security/import: Apply an export to a project, as a dry run by default.

The Data Security API requires the Enterprise plan. On other plans every endpoint returns 403 PLAN_NOT_SUPPORTED. Reading works with a project API key or an organization API key.

⚠️

Changes need an organization API key

Creating, updating and deleting requires an organization API key. A project API key gets 403 ACTION_FORBIDDEN.

Basic Usage

This example creates a region property, limits orders and accounts to EMEA rows, and assigns a value to one member.

Create a Session Property

Session property names use letters, numbers and underscores.

Request — POST /v2/projects/1/data_security/session_properties

{
  "name": "region",
  "type": "STRING",
  "description": "Sales region"
}

Response

{
  "id": 12,
  "name": "region",
  "type": "STRING",
  "description": "Sales region",
  "usedByPolicies": [],
  "createdAt": "2026-10-16T08:00:00.000Z",
  "updatedAt": "2026-10-16T08:00:00.000Z"
}

Create an RLS Policy

The condition refers to the session property as @region.

Request — POST /v2/projects/1/data_security/policies

{
  "name": "emea_accounts_only",
  "type": "RLS",
  "models": ["orders", "accounts"],
  "condition": "@region = 'EMEA'",
  "sessionProperties": [
    { "name": "region", "isRequired": true }
  ]
}

Create a CLS Policy

A CLS policy uses one session property. In this example, users whose role is not finance cannot see gross_margin.

Request — POST /v2/projects/1/data_security/policies

{
  "name": "hide_margin",
  "type": "CLS",
  "columns": [{ "model": "orders", "column": "gross_margin" }],
  "sessionProperties": [
    {
      "name": "role",
      "isRequired": false,
      "defaultValue": "viewer",
      "operator": "EQUALS",
      "comparisonValue": "finance"
    }
  ]
}

Assign Values

null clears a value, and properties you leave out stay as they are.

Request — PATCH /v2/projects/1/data_security/member_values

{
  "members": [
    { "email": "[email protected]", "properties": { "region": "EMEA" } },
    { "email": "[email protected]", "properties": { "region": null } }
  ]
}

Copying a Setup to Another Project

Policies refer to models, columns and session properties by name, so an export from one project works on any project with the same models. This is how a setup moves from QA to production.

  1. Export the source project with GET /data_security.
  2. Send the export to the target project's POST /data_security/import and review the plan.
  3. Send it again with "dryRun": false to apply it.

See Import data security setup for the details.

When Changes Apply

Creating, updating or deleting a policy redeploys the project before the request returns. A failed redeploy is not reported on these endpoints, so deploy the project again if a change does not take effect. Import reports it in deploy.status. Assigned values apply to users signed in to Wren AI. Requests made with an API key pass session property values in the X-Wren-Session-Properties header instead.

Errors

Errors return a code and an error message. Some codes also include fields that name what went wrong, such as models on UNKNOWN_MODEL or path on INVALID_INPUT.

StatusCodeWhen
400INVALID_INPUTAn id or a field in the request is invalid, or the body is not valid JSON.
400INVALID_POLICYA policy is missing a required field.
400UNKNOWN_MODEL / UNKNOWN_COLUMN / UNKNOWN_SESSION_PROPERTYA name in the request does not exist in the project.
400INVALID_CONDITIONThe RLS condition is not valid. error includes the engine's reason.
400COLUMN_ALREADY_PROTECTEDAnother CLS policy already protects the column.
400INVALID_PROPERTY_VALUEA value does not match its session property's type.
400IMMUTABLE_FIELDThe request changes a field that cannot be changed.
400TOO_MANY_ENTRIESThe request has too many entries.
403PLAN_NOT_SUPPORTEDThe organization is not on the Enterprise plan.
403ACTION_FORBIDDENThe action needs an organization API key.
404RESOURCE_NOT_FOUNDThe project, or the item in this project, does not exist.
404NOT_A_MEMBERAn email or group is not in the project.
405METHOD_NOT_ALLOWEDThe endpoint does not support the HTTP method.
409DUPLICATED_SESSION_PROPERTY_NAME / DUPLICATED_POLICY_NAMEThe name is already used.
409IN_USEA policy still uses the session property.
409TYPE_MISMATCHOn import, an item in the file has a different type from the one in the project.
409AMBIGUOUS_MEMBERAn email matches more than one user.
409PROJECT_NOT_DEPLOYEDThe project has not been deployed yet. Deploy it before adding policies or importing.
413INVALID_INPUTThe request body is larger than 1 MB, or 10 MB on import.

How to create organization API keys

Visit Organization API Keys to learn more.