Permissions API
The Permissions API provides fine-grained access control for resources using OpenFGA (Flexible Authorization). Control who can view, edit, or manage projects, dashboards, connectors, and files.
Base URL
Section titled “Base URL”https://api.querri.com/apiAuthentication
Section titled “Authentication”All Permissions API endpoints require JWT authentication. See Authentication for details.
Authorization Model
Section titled “Authorization Model”Querri uses OpenFGA for relationship-based access control with these key concepts:
Resource Types
Section titled “Resource Types”| Type | Description |
|---|---|
project | Data analysis projects |
dashboard | Visualization dashboards |
connector | Database/API connectors |
file | Uploaded or generated files |
organization | Organization-wide permissions |
Permission Levels
Section titled “Permission Levels”| Level | Projects | Dashboards | Connectors | Files |
|---|---|---|---|---|
owner | Full control, delete, share | Full control, delete, share | Full control, delete, share | Delete, share |
editor | Edit, execute, share | Edit widgets, refresh | Edit config, use | Upload versions |
viewer | View only, read results | View only | View only (creds hidden) | Download only |
Permission Inheritance
Section titled “Permission Inheritance”- Organization members inherit base permissions
- Explicit grants override inherited permissions
- Owner permissions cannot be revoked by non-owners
- Resources inherit organization visibility settings
Endpoints
Section titled “Endpoints”List Resource Permissions
Section titled “List Resource Permissions”Get all users/groups with access to a resource.
GET /api/permissions/resource/{type}/{id}Path Parameters:
| Parameter | Type | Description |
|---|---|---|
type | string | Resource type: project, dashboard, connector, file |
id | string | Resource UUID |
Example Request:
curl "https://api.querri.com/api/permissions/resource/project/proj_01ABCDEF" \ -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."Response: 200 OK
{ "resource_type": "project", "resource_id": "proj_01ABCDEF", "resource_name": "Q4 Sales Analysis", "permissions": [ { "user_id": "user_01ABCDEF", "user_email": "alice@example.com", "user_name": "Alice Johnson", "permission": "owner", "granted_at": "2025-01-10T10:00:00Z", "granted_by": "system", "source": "creator" }, { "user_id": "user_02GHIJKL", "user_email": "bob@example.com", "user_name": "Bob Smith", "permission": "editor", "granted_at": "2025-01-12T14:30:00Z", "granted_by": "user_01ABCDEF", "source": "explicit" }, { "user_id": "user_03MNOPQR", "user_email": "charlie@example.com", "user_name": "Charlie Brown", "permission": "viewer", "granted_at": "2025-01-13T09:15:00Z", "granted_by": "user_01ABCDEF", "source": "explicit" } ], "group_permissions": [ { "group_id": "group_01ABCDEF", "group_name": "Analytics Team", "permission": "editor", "member_count": 8, "granted_at": "2025-01-11T10:00:00Z" } ], "inherited_permissions": [ { "source": "organization", "permission": "viewer", "user_count": 42 } ], "total_users_with_access": 51}Permission Required: resource:read on the specific resource, or org admin.
Grant Permission
Section titled “Grant Permission”Grant access to a user or group for a resource.
POST /api/permissions/resource/{type}/{id}Headers:
Authorization: Bearer {JWT_TOKEN}Content-Type: application/jsonRequest Body (Grant to User):
{ "user_id": "user_02GHIJKL", "permission": "editor", "notify": true, "message": "Sharing Q4 analysis project with you for review"}Request Body (Grant to Group):
{ "group_id": "group_01ABCDEF", "permission": "viewer", "notify": true}Request Body (Grant to Organization):
{ "organization_id": "org_01ABCDEF", "permission": "viewer", "notify": false}Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
user_id | string | Conditional | User to grant access (mutually exclusive with group_id) |
group_id | string | Conditional | Group to grant access |
organization_id | string | Conditional | Make accessible to entire org |
permission | string | Yes | Permission level: owner, editor, viewer |
notify | boolean | No | Send email notification (default: true) |
message | string | No | Custom message in notification |
expires_at | string | No | Expiration timestamp (ISO 8601) |
Response: 201 Created
{ "grant_id": "grant_01ABCDEF", "resource_type": "project", "resource_id": "proj_01ABCDEF", "user_id": "user_02GHIJKL", "permission": "editor", "granted_by": "user_01ABCDEF", "granted_at": "2025-01-15T10:00:00Z", "expires_at": null, "fga_warrant": { "object": "project:proj_01ABCDEF", "relation": "editor", "subject": "user:user_02GHIJKL" }}Permission Required: resource:share or owner permission on the resource.
Error Responses:
// 400 Bad Request - Invalid permission level{ "error": "validation_error", "message": "Invalid permission level for this resource type", "valid_permissions": ["owner", "editor", "viewer"]}
// 409 Conflict - Permission already exists{ "error": "conflict", "message": "User already has 'editor' permission on this resource", "existing_permission": "editor"}
// 403 Forbidden - Cannot grant owner permission{ "error": "forbidden", "message": "Only resource owners can grant owner permissions"}Revoke Permission
Section titled “Revoke Permission”Remove a user’s or group’s access to a resource.
DELETE /api/permissions/resource/{type}/{id}Query Parameters:
| Parameter | Type | Description |
|---|---|---|
user_id | string | User to revoke access from |
group_id | string | Group to revoke access from |
Example Request:
curl -X DELETE "https://api.querri.com/api/permissions/resource/project/proj_01ABCDEF?user_id=user_02GHIJKL" \ -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."Response: 204 No Content
Permission Required: resource:share or owner permission on the resource.
Error Responses:
// 403 Forbidden - Cannot revoke owner permission{ "error": "forbidden", "message": "Cannot revoke owner permission from resource creator"}
// 404 Not Found - No permission to revoke{ "error": "not_found", "message": "User does not have explicit permission on this resource"}Update Permission
Section titled “Update Permission”Change an existing permission level.
PATCH /api/permissions/resource/{type}/{id}Request Body:
{ "user_id": "user_02GHIJKL", "permission": "viewer", "notify": true}Response: 200 OK
Returns updated permission grant object.
This is equivalent to revoking the old permission and granting the new one.
Check User Permission
Section titled “Check User Permission”Check if a user has a specific permission on a resource.
GET /api/permissions/checkQuery Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
resource_type | string | Yes | Resource type |
resource_id | string | Yes | Resource UUID |
permission | string | Yes | Permission to check |
user_id | string | No | User to check (defaults to current user) |
Example Request:
curl "https://api.querri.com/api/permissions/check?resource_type=project&resource_id=proj_01ABCDEF&permission=editor" \ -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."Response: 200 OK
{ "resource_type": "project", "resource_id": "proj_01ABCDEF", "user_id": "user_02GHIJKL", "permission": "editor", "allowed": true, "reason": "explicit_grant", "granted_by": "user_01ABCDEF", "granted_at": "2025-01-12T14:30:00Z"}If Permission Denied:
{ "resource_type": "project", "resource_id": "proj_01ABCDEF", "user_id": "user_03MNOPQR", "permission": "editor", "allowed": false, "reason": "insufficient_permission", "current_permission": "viewer"}List Organization Users
Section titled “List Organization Users”Get users in the organization for sharing UI.
GET /api/permissions/organization/usersQuery Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
search | string | - | Search by name or email |
page | integer | 1 | Page number |
limit | integer | 50 | Results per page (max 100) |
Example Request:
curl "https://api.querri.com/api/permissions/organization/users?search=alice" \ -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."Response: 200 OK
{ "users": [ { "user_id": "user_01ABCDEF", "email": "alice@example.com", "name": "Alice Johnson", "avatar_url": "https://avatars.example.com/alice.jpg", "role": "admin", "status": "active", "joined_at": "2024-06-15T10:00:00Z" }, { "user_id": "user_04STUVWX", "email": "alice.smith@example.com", "name": "Alice Smith", "avatar_url": null, "role": "member", "status": "active", "joined_at": "2024-11-20T14:00:00Z" } ], "groups": [ { "group_id": "group_01ABCDEF", "name": "Analytics Team", "member_count": 8, "created_at": "2024-07-01T10:00:00Z" } ], "pagination": { "page": 1, "limit": 50, "total": 2 }}FGA Warrant Format
Section titled “FGA Warrant Format”Querri uses OpenFGA warrants (tuples) to represent permissions. Understanding the warrant format helps debug permission issues.
Warrant Structure
Section titled “Warrant Structure”{ "object": "{resource_type}:{resource_id}", "relation": "{permission_level}", "subject": "{subject_type}:{subject_id}"}Examples
Section titled “Examples”User with Editor Permission on Project:
{ "object": "project:proj_01ABCDEF", "relation": "editor", "subject": "user:user_02GHIJKL"}Group with Viewer Permission on Dashboard:
{ "object": "dashboard:dash_01ABCDEF", "relation": "viewer", "subject": "group:group_01ABCDEF"}User Member of Group:
{ "object": "group:group_01ABCDEF", "relation": "member", "subject": "user:user_02GHIJKL"}Organization-wide Access:
{ "object": "project:proj_01ABCDEF", "relation": "viewer", "subject": "organization:org_01ABCDEF"}Permission Hierarchy
Section titled “Permission Hierarchy”Permissions have implicit hierarchies:
ownerincludeseditorandviewereditorincludesviewer
FGA automatically resolves these relationships.
Permission Scopes by Resource Type
Section titled “Permission Scopes by Resource Type”Project Permissions
Section titled “Project Permissions”| Permission | Capabilities |
|---|---|
owner | Delete project, manage permissions, edit, execute, view |
editor | Edit project, add/remove steps, execute, view |
viewer | View project, view results (read-only) |
Dashboard Permissions
Section titled “Dashboard Permissions”| Permission | Capabilities |
|---|---|
owner | Delete dashboard, manage permissions, edit, refresh, view |
editor | Edit widgets, update layout, refresh, view |
viewer | View dashboard only (read-only) |
Connector Permissions
Section titled “Connector Permissions”| Permission | Capabilities |
|---|---|
owner | Delete connector, manage permissions, edit config, use in projects |
editor | Edit config, test connection, use in projects |
viewer | View connector (credentials hidden), test connection |
File Permissions
Section titled “File Permissions”| Permission | Capabilities |
|---|---|
owner | Delete file, manage permissions, upload versions, download |
editor | Upload versions, update metadata, download |
viewer | Download only (read-only) |
Bulk Operations
Section titled “Bulk Operations”Grant Permissions to Multiple Users
Section titled “Grant Permissions to Multiple Users”curl -X POST "https://api.querri.com/api/permissions/resource/project/proj_01ABCDEF/bulk" \ -H "Authorization: Bearer ${JWT_TOKEN}" \ -H "Content-Type: application/json" \ -d '{ "grants": [ {"user_id": "user_02GHIJKL", "permission": "editor"}, {"user_id": "user_03MNOPQR", "permission": "viewer"}, {"group_id": "group_01ABCDEF", "permission": "viewer"} ], "notify": true }'Response:
{ "success_count": 3, "failure_count": 0, "grants": [ { "user_id": "user_02GHIJKL", "permission": "editor", "status": "created" }, { "user_id": "user_03MNOPQR", "permission": "viewer", "status": "created" }, { "group_id": "group_01ABCDEF", "permission": "viewer", "status": "created" } ]}Permission Expiration
Section titled “Permission Expiration”Set temporary access with expiration:
curl -X POST "https://api.querri.com/api/permissions/resource/project/proj_01ABCDEF" \ -H "Authorization: Bearer ${JWT_TOKEN}" \ -H "Content-Type: application/json" \ -d '{ "user_id": "user_02GHIJKL", "permission": "editor", "expires_at": "2025-02-15T00:00:00Z", "notify": true, "message": "Temporary access for Q4 review" }'Expired permissions are automatically revoked by a background job.
Permission Audit Log
Section titled “Permission Audit Log”View Permission Changes
Section titled “View Permission Changes”curl "https://api.querri.com/api/permissions/resource/project/proj_01ABCDEF/audit" \ -H "Authorization: Bearer ${JWT_TOKEN}"Response:
{ "resource_type": "project", "resource_id": "proj_01ABCDEF", "events": [ { "event_id": "evt_01ABCDEF", "event_type": "permission_granted", "user_id": "user_02GHIJKL", "permission": "editor", "performed_by": "user_01ABCDEF", "timestamp": "2025-01-12T14:30:00Z", "details": { "notify_sent": true, "message": "Sharing Q4 analysis project" } }, { "event_id": "evt_02GHIJKL", "event_type": "permission_revoked", "user_id": "user_03MNOPQR", "permission": "viewer", "performed_by": "user_01ABCDEF", "timestamp": "2025-01-14T09:15:00Z" } ], "pagination": { "page": 1, "limit": 50, "total": 8 }}Organization-Wide Policies
Section titled “Organization-Wide Policies”Organization admins can set default permission policies:
Set Default Visibility
Section titled “Set Default Visibility”curl -X POST "https://api.querri.com/api/permissions/organization/policy" \ -H "Authorization: Bearer ${JWT_TOKEN}" \ -H "Content-Type: application/json" \ -d '{ "resource_type": "project", "default_visibility": "organization", "default_permission": "viewer", "allow_external_sharing": false }'Policy Options:
| Field | Values | Description |
|---|---|---|
default_visibility | private, organization, public | Who can discover resources |
default_permission | viewer, editor, none | Default access level for org members |
allow_external_sharing | boolean | Allow sharing outside organization |
Common Sharing Patterns
Section titled “Common Sharing Patterns”Share with Team
Section titled “Share with Team”# Create group for teamcurl -X POST "https://api.querri.com/api/groups" \ -H "Authorization: Bearer ${JWT_TOKEN}" \ -d '{"name": "Analytics Team", "member_ids": ["user_02GHIJKL", "user_03MNOPQR"]}'
# Grant group access to projectcurl -X POST "https://api.querri.com/api/permissions/resource/project/proj_01ABCDEF" \ -H "Authorization: Bearer ${JWT_TOKEN}" \ -d '{"group_id": "group_01ABCDEF", "permission": "editor"}'Share Entire Organization
Section titled “Share Entire Organization”curl -X POST "https://api.querri.com/api/permissions/resource/dashboard/dash_01ABCDEF" \ -H "Authorization: Bearer ${JWT_TOKEN}" \ -d '{ "organization_id": "org_01ABCDEF", "permission": "viewer", "notify": false }'Temporary Contractor Access
Section titled “Temporary Contractor Access”curl -X POST "https://api.querri.com/api/permissions/resource/project/proj_01ABCDEF" \ -H "Authorization: Bearer ${JWT_TOKEN}" \ -d '{ "user_id": "user_contractor", "permission": "viewer", "expires_at": "2025-03-31T00:00:00Z", "message": "Temporary access for Q1 audit" }'Permission Notifications
Section titled “Permission Notifications”When notify: true, users receive email notifications:
Subject: Alice Johnson shared "Q4 Sales Analysis" with you
Body:
Alice Johnson has shared the project "Q4 Sales Analysis" with you.
Permission: EditorMessage: Sharing Q4 analysis project with you for review
View Project: https://app.querri.com/projects/proj_01ABCDEFNotifications are sent via configured email provider (SendGrid, Postmark, etc.).
Best Practices
Section titled “Best Practices”- Use Groups for Teams - Manage team permissions via groups instead of individual users
- Grant Minimum Permissions - Start with
viewer, escalate toeditoronly when needed - Set Expiration Dates - For temporary access, always set expiration
- Audit Regularly - Review permission audit logs monthly
- Use Organization Defaults - Set sensible org-wide defaults to reduce manual grants
- Document Sharing Reasons - Include messages when granting access
- Revoke Promptly - Remove access when users leave teams or projects complete
HTTP Status Codes
Section titled “HTTP Status Codes”| Code | Description |
|---|---|
| 200 | Success (GET, PATCH) |
| 201 | Created (POST grant) |
| 204 | No Content (DELETE revoke) |
| 400 | Bad Request (validation error) |
| 401 | Unauthorized |
| 403 | Forbidden (insufficient permissions) |
| 404 | Not Found (resource or user not found) |
| 409 | Conflict (permission already exists) |
| 429 | Too Many Requests |
| 500 | Internal Server Error |