Snap-in scopes
Snap-ins support scopes—a permissions framework that controls exactly what a snap-in's service account can access during execution. Instead of granting broad system-level permissions, scopes let you declare the minimum set of permissions your snap-in needs, improving security and governance across your DevRev environment.
Every snap-in must declare the specific permissions it requires in its manifest file. These permissions are granted to the service account at installation time, and the snap-in can only access what it has been explicitly allowed.
When installing a snap-in, users are prompted to review and grant the permissions the snap-in requests. Installation proceeds only after all required permissions have been accepted.
Scope format
Each scope follows an object:permission syntax. You specify the object type you need access to, followed by a colon, followed by the permission level.
Permission levels
Most objects support three standard permission levels:
| Scope | What it grants |
|---|---|
object:read | Read-only access (get, list, count) |
object:write | Read, create, and update access |
object:all | Full access — read, create, update, and delete |
Some objects use more granular permissions like object:update (grants create and update but not broader write access). When in doubt, refer to the API-to-scope reference.
Examples
| Scope | Meaning |
|---|---|
ticket:read | Read-only access to tickets |
ticket:write | Read, create, and update tickets |
rev_user:write | Read, create, and update customer details |
article:update | Read and update articles |
conversation:read | Read access to conversations |
command:write | Create and update commands |
tag:write | Create or modify tags |
Add scopes to a snap-in
1. Identify the APIs your snap-in uses
Review your snap-in's code and identify all DevRev API calls it makes. For example, if your snap-in creates timeline entries and updates customers, note the endpoints: timeline-entries.create, rev-users.get, rev-users.update.
2. Determine the required scopes
Map each API endpoint to its corresponding scope using these general rules:
- GET/list operations →
object:read - Create and update operations →
object:write - Delete operations →
object:all
For timeline-entries.create and timeline-entries.update, the required scope is the parent object's read permission — if the timeline entry belongs to an issue, you need issue:read; if it belongs to a ticket, you need ticket:read.
Referenced DON IDs require read scope. Any DevRev object reference (DON ID) passed in a request payload requires the corresponding :read scope on that object type, in addition to the scope required for the operation itself.
For example, calling works.create to create an issue with a tags field that references existing tag DON IDs requires both:
issue:write— to create the issuetag:read— to resolve and validate each tag DON ID passed in the payload
This applies to every endpoint and every field that accepts a DON ID — owners, parts, accounts, custom object references, and so on. Audit each request payload for DON ID fields and add a :read scope for every referenced object type.
Refer to the API-to-scope reference for the complete mapping.
3. Add scopes to the manifest
Declare the scopes in the service_account section of your snap-in's manifest file:
service_account:
display_name: DevRev Bot
scopes:
self:
- scope: conversation:read
optional: false
reason: "Allow read access to conversations."
- scope: rev_user:write
optional: false
reason: "Allow read and update customer details."
- scope: comment:write
optional: false
reason: "Allow creation and updating of timeline entries."Each scope entry includes:
| Field | Description |
|---|---|
scope | The permission being requested, in object:permission format |
optional | Whether the scope is required (false) or optional (true) for installation |
reason | A human-readable explanation shown to users during installation |
4. Publish your snap-in
The snap-in creation and draft process remains the same. On the UI, users see a prompt to explicitly grant the requested permissions while installing the snap-in. Installation proceeds only after all required permissions have been accepted.
5. Upgrade an existing snap-in
For existing snap-ins, add the scopes to the manifest and republish. The upgrade flow remains the same. For DevRev snap-ins, ensure the marketplace.yaml file is present alongside manifest.yaml.
Scope types: self vs. impersonate
Scopes are organized into two categories under the service_account section.
Self scopes
Self scopes define the permissions the service account has when operating on its own behalf. This is the most common type.
service_account:
scopes:
self:
- scope: ticket:read
optional: false
reason: "Read ticket details."Impersonate scopes
Impersonate scopes define the permissions the service account has when acting on behalf of another entity (a customer or user). The effective permissions are the intersection of the service account's impersonate scopes and the impersonated user's own permissions—so the snap-in can never exceed what the user themselves could do.
service_account:
scopes:
impersonate:
- act_as: all_devusers
scopes:
- scope: ticket:read
optional: false
reason: "Read tickets on behalf of the user."Scopes for manifest-defined objects
If your snap-in's manifest creates objects like commands, tags, imports, or dashboards, include the corresponding scopes as well, since these objects are created via the service account.
For example, if your manifest defines tags and commands:
service_account:
display_name: Example Snap-in
scopes:
self:
- scope: command:write
reason: "For command creation."
- scope: tag:write
reason: "For tag creation."
tags:
- name: exampleTag
description: Example description
commands:
- name: exampleCommand
namespace: devrev
description: Example description
surfaces:
- surface: discussions
object_types:
- snap_in
usage_hint: ""
function: init_category_importCustom object scopes
For snap-ins that work with custom objects, scopes follow this format:
custom_object:<leaf_type>:<permission>Example: custom_object:asset:read grants read access to the custom object type asset.
Custom object scopes are created automatically when a custom object is defined via the consumer.
For certain cases, you might need to allowlist a snap-in to bypass the custom object scopes requirement. Check the Manage allowlist exceptions section for more details.
Empty scopes
If your snap-in does not call any DevRev APIs and needs no permissions, declare empty scopes to indicate this explicitly:
service_account:
display_name: DevRev Bot
scopes:
self:This tells the platform that the service account intentionally requires no permissions.
Best practices
Know the default self-permissions. The snap-in service account has default permission to read and update itself. Any API calls targeting the snap-in itself (including event-sources and related APIs where the parent is the snap-in) work without explicitly setting scopes.
Do not add required scopes after the initial release. Adding new required scopes on an update can break the snap-in for users who haven't granted the new permission. If you need additional scopes later, add them as optional: true. Optional scopes are granted automatically, but this behavior may change—plan your scopes from the start.
Remove scopes on updates without restriction. Removing scopes on subsequent updates is supported.
Request only what you need. Follow the principle of least privilege. Requesting fewer permissions makes it easier for admins to approve your snap-in and reduces security risk.
Include clear reasons. The reason field is shown to users during installation. Write descriptions that explain why each permission is needed in plain language.
Allowlist exceptions
In some cases, the scopes framework cannot fully support a snap-in's requirements. For these, the snap-in can be added to the allowlist to bypass scopes and use the older systems group permissions.
Custom objects created during activation
If a snap-in creates a custom object during its activation hooks and also needs custom_object:<leaf_type>:<permission> scopes for that same object, a circular dependency exists. Because the object doesn't exist yet when scopes are evaluated, these snap-ins need to be added to the allowlist.
Impersonation with agent authorization
If a snap-in uses impersonation scopes with act_as and expects permissions to be the intersection of the user's and service account's permissions, it may need to be added to the allowlist until agent impersonation is fully rolled out.
If your snap-in falls into either of these categories, raise a ticket to request allowlisting by snap-in version or package slug.
API-to-scope reference
The table below maps commonly used APIs to their required scopes. For the complete auto-generated reference covering every public endpoint, see the API-to-scope reference.
Work items (tickets, issues, opportunities)
The works.* APIs require scopes for each work item type your snap-in interacts with.
| API | Required scopes |
|---|---|
works.get, works.list, works.count, works.group, works.export | ticket:read, issue:read, opportunity:read |
works.create, works.update | ticket:write, issue:write, opportunity:write |
works.delete | ticket:all, issue:all, opportunity:all |
Accounts and workspaces
| API | Required scope |
|---|---|
accounts.get, accounts.list | account:read |
accounts.create, accounts.update | account:write |
rev-orgs.get, rev-orgs.list | rev_org:read |
rev-orgs.create, rev-orgs.update | rev_org:write |
Users
| API | Required scope |
|---|---|
rev-users.get, rev-users.list | rev_user:read |
rev-users.create, rev-users.update, rev-users.merge | rev_user:write |
rev-users.delete | rev_user:all |
dev-users.get, dev-users.list | dev_user:read |
dev-users.create, dev-users.update | dev_user:write |
Conversations
| API | Required scope |
|---|---|
conversations.get, conversations.list | conversation:read |
conversations.create, conversations.update | conversation:write |
Timeline entries
Timeline entries require the parent object's read scope. If the entry belongs to a ticket, you need ticket:read; if it belongs to an issue, you need issue:read.
| API | Required scope |
|---|---|
timeline-entries.create, .update, .get, .list, .delete | <parent>:read |
Articles
| API | Required scope |
|---|---|
articles.get, articles.list | article:read |
articles.create, articles.update | article:update |
articles.delete | article:all |
Tags and commands
| API | Required scope |
|---|---|
tags.get, tags.list | tag:read |
tags.create | tag:write |
commands.get | command:read |
Links
| API | Required scope |
|---|---|
links.list | link:read |
links.create | link:write |
links.delete | link:all |
Engagements and meetings
| API | Required scope |
|---|---|
engagements.list | engagement:read |
engagements.create, engagements.update | engagement:write |
engagements.delete | engagement:all |
meetings.get, meetings.list | meeting:read |
meetings.create, meetings.update | meeting:write |
Incidents
| API | Required scope |
|---|---|
incidents.get, incidents.list | incident:read |
incidents.create, incidents.update | incident:write |
Surveys
| API | Required scope |
|---|---|
surveys.get, surveys.list | survey:read |
surveys.create, surveys.update, surveys.send, surveys.submit | survey:write |
Groups and roles
| API | Required scope |
|---|---|
groups.get, groups.list | group:read |
groups.update | group:update |
groups.members.list | group_membership:read, group:read |
groups.members.add, groups.members.remove | group_membership:update, group:read |
roles.list | role:read |
roles.create, roles.apply, roles.update | role:update |
Artifacts and dashboards
| API | Required scope |
|---|---|
artifacts.get, artifacts.download, artifacts.locate | artifact:read |
artifacts.prepare | artifact:create |
dashboards.get, dashboards.list | dashboard:read |
dashboards.create, dashboards.update | dashboard:write |
Workflows
| API | Required scope |
|---|---|
workflows.get | workflow:read |
workflows.create, workflows.templates.instantiate | workflow:write |
workflows.delete | workflow:all |
Custom objects
| API | Required scope |
|---|---|
custom-objects.get, custom-objects.list, custom-objects.count | custom_object:<leaf_type>:read |
custom-objects.create, custom-objects.update | custom_object:<leaf_type>:write |
custom-objects.delete | custom_object:<leaf_type>:all |
Snap widgets
| API | Required scope |
|---|---|
snap-widgets.get, snap-widgets.count | parent:read, snap_widget:read |
snap-widgets.create, snap-widgets.update | parent:read, snap_widget:write |
APIs that require no scope
The following APIs do not require explicit scopes: schemas.aggregated.get, schemas.custom.list, schemas.stock.list, schemas.subtypes.list, stage-diagrams.list, stages.custom.get, stages.custom.list, states.custom.get, states.custom.list, snap-ins.get, snap-ins.update, event-sources.event.schedule, event-sources.schedule, event-sources.unschedule, and recommendations.* endpoints.
FAQ
Does my existing snap-in stop working? No. Previously published snap-ins are not affected. Scopes are only required when publishing a new snap-in or republishing an updated version.
What happens if I don't define scopes? Publishing is blocked. The platform validates the presence of scopes as part of the publishing process.
Can I change scopes after publishing?
You can remove scopes. You can add new scopes as optional: true. Adding new required scopes after the initial release is not supported—it can break the snap-in for existing users.
What if my snap-in doesn't need any permissions?
Use empty scopes (self: with no entries) to explicitly declare that no permissions are required.
How do custom object scopes work?
Use the format custom_object:<leaf_type>:<permission>. For example, custom_object:asset:read. These scopes are created automatically when a custom object is defined.
Where can I find the full API-to-scope mapping? See the API-to-scope reference—it is auto-generated from the public OpenAPI spec and stays up to date with every spec change.
Last updated on