Skip to content

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.

https://api.querri.com/api

All Permissions API endpoints require JWT authentication. See Authentication for details.

Querri uses OpenFGA for relationship-based access control with these key concepts:

TypeDescription
projectData analysis projects
dashboardVisualization dashboards
connectorDatabase/API connectors
fileUploaded or generated files
organizationOrganization-wide permissions
LevelProjectsDashboardsConnectorsFiles
ownerFull control, delete, shareFull control, delete, shareFull control, delete, shareDelete, share
editorEdit, execute, shareEdit widgets, refreshEdit config, useUpload versions
viewerView only, read resultsView onlyView only (creds hidden)Download only
  • Organization members inherit base permissions
  • Explicit grants override inherited permissions
  • Owner permissions cannot be revoked by non-owners
  • Resources inherit organization visibility settings

Get all users/groups with access to a resource.

GET /api/permissions/resource/{type}/{id}

Path Parameters:

ParameterTypeDescription
typestringResource type: project, dashboard, connector, file
idstringResource UUID

Example Request:

Terminal window
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 access to a user or group for a resource.

POST /api/permissions/resource/{type}/{id}

Headers:

Authorization: Bearer {JWT_TOKEN}
Content-Type: application/json

Request 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:

FieldTypeRequiredDescription
user_idstringConditionalUser to grant access (mutually exclusive with group_id)
group_idstringConditionalGroup to grant access
organization_idstringConditionalMake accessible to entire org
permissionstringYesPermission level: owner, editor, viewer
notifybooleanNoSend email notification (default: true)
messagestringNoCustom message in notification
expires_atstringNoExpiration 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"
}

Remove a user’s or group’s access to a resource.

DELETE /api/permissions/resource/{type}/{id}

Query Parameters:

ParameterTypeDescription
user_idstringUser to revoke access from
group_idstringGroup to revoke access from

Example Request:

Terminal window
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"
}

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 if a user has a specific permission on a resource.

GET /api/permissions/check

Query Parameters:

ParameterTypeRequiredDescription
resource_typestringYesResource type
resource_idstringYesResource UUID
permissionstringYesPermission to check
user_idstringNoUser to check (defaults to current user)

Example Request:

Terminal window
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"
}

Get users in the organization for sharing UI.

GET /api/permissions/organization/users

Query Parameters:

ParameterTypeDefaultDescription
searchstring-Search by name or email
pageinteger1Page number
limitinteger50Results per page (max 100)

Example Request:

Terminal window
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
}
}

Querri uses OpenFGA warrants (tuples) to represent permissions. Understanding the warrant format helps debug permission issues.

{
"object": "{resource_type}:{resource_id}",
"relation": "{permission_level}",
"subject": "{subject_type}:{subject_id}"
}

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"
}

Permissions have implicit hierarchies:

  • owner includes editor and viewer
  • editor includes viewer

FGA automatically resolves these relationships.


PermissionCapabilities
ownerDelete project, manage permissions, edit, execute, view
editorEdit project, add/remove steps, execute, view
viewerView project, view results (read-only)
PermissionCapabilities
ownerDelete dashboard, manage permissions, edit, refresh, view
editorEdit widgets, update layout, refresh, view
viewerView dashboard only (read-only)
PermissionCapabilities
ownerDelete connector, manage permissions, edit config, use in projects
editorEdit config, test connection, use in projects
viewerView connector (credentials hidden), test connection
PermissionCapabilities
ownerDelete file, manage permissions, upload versions, download
editorUpload versions, update metadata, download
viewerDownload only (read-only)

Terminal window
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"
}
]
}

Set temporary access with expiration:

Terminal window
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.


Terminal window
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 admins can set default permission policies:

Terminal window
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:

FieldValuesDescription
default_visibilityprivate, organization, publicWho can discover resources
default_permissionviewer, editor, noneDefault access level for org members
allow_external_sharingbooleanAllow sharing outside organization

Terminal window
# Create group for team
curl -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 project
curl -X POST "https://api.querri.com/api/permissions/resource/project/proj_01ABCDEF" \
-H "Authorization: Bearer ${JWT_TOKEN}" \
-d '{"group_id": "group_01ABCDEF", "permission": "editor"}'
Terminal window
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
}'
Terminal window
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"
}'

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: Editor
Message: Sharing Q4 analysis project with you for review
View Project: https://app.querri.com/projects/proj_01ABCDEF

Notifications are sent via configured email provider (SendGrid, Postmark, etc.).


  1. Use Groups for Teams - Manage team permissions via groups instead of individual users
  2. Grant Minimum Permissions - Start with viewer, escalate to editor only when needed
  3. Set Expiration Dates - For temporary access, always set expiration
  4. Audit Regularly - Review permission audit logs monthly
  5. Use Organization Defaults - Set sensible org-wide defaults to reduce manual grants
  6. Document Sharing Reasons - Include messages when granting access
  7. Revoke Promptly - Remove access when users leave teams or projects complete

CodeDescription
200Success (GET, PATCH)
201Created (POST grant)
204No Content (DELETE revoke)
400Bad Request (validation error)
401Unauthorized
403Forbidden (insufficient permissions)
404Not Found (resource or user not found)
409Conflict (permission already exists)
429Too Many Requests
500Internal Server Error