Scopes & permissions
Read/write scopes per domain, umbrella aliases, admin scopes, and how to pick what your integration needs.
Scopes are strings that declare exactly what your integration is allowed to do — think of them as a checklist of permissions. When you create an API key or register an OAuth client, you pick the scopes you need. Every API call Zoop receives is checked against those scopes, and calls that need a scope you didn't request are rejected.
How scopes work
Each scope is a string in the format verb:resource. There are two verbs:
read:*— fetch or list datawrite:*— create, update, or delete data
Write access does not imply read access. If your integration needs to update a customer and also fetch that customer back, request both write:customers and read:customers.
Admin scopes follow a different pattern (admin:*) and are covered below.
Scope list
The table below lists every public scope. The "consent screen label" column shows the exact text a Zoop user sees on the authorization screen when they approve your OAuth connection — keep these labels in mind when deciding which scopes to request, because users see them.
| Scope | Consent screen label |
|---|---|
read:customers | View customers, contacts, and locations |
write:customers | Create and edit customers, contacts, and locations |
read:catalog | View service catalog and pricing |
write:catalog | Manage service catalog and pricing |
read:catalog_items | View catalog items |
write:catalog_items | Create and edit catalog items |
read:catalog_categories | View catalog categories |
write:catalog_categories | Create and edit catalog categories |
read:quotes | View quotes |
write:quotes | Create and edit quotes |
read:estimates | View estimates |
write:estimates | Create and edit estimates |
read:communications | View messages and communication history |
write:communications | Send messages on your behalf |
read:companies | View your business profile |
write:companies | Edit your business settings |
read:settings | View settings and configuration |
write:settings | Edit settings and configuration |
read:invoices | View invoices and payment history |
write:invoices | Create and edit invoices |
read:jobs | View jobs and job history |
write:jobs | Create and edit jobs |
read:job_series | View recurring job series |
write:job_series | Create and edit recurring job series |
read:plans | View service plans |
write:plans | Create and edit service plans |
read:tax_rates | View tax rates |
write:tax_rates | Create and edit tax rates |
read:notes | View notes |
write:notes | Create and edit notes |
read:agent_sessions | View AI agent session history |
write:agent_sessions | Run AI agent sessions on your behalf |
Umbrella scopes
Two scopes are shortcuts that expand into multiple child scopes automatically. You can request either the umbrella scope or the individual child scopes — the effect on your token is identical.
Catalog umbrella
read:catalog expands to read:catalog_items + read:catalog_categories.
write:catalog expands to write:catalog_items + write:catalog_categories.
Request read:catalog if your integration needs to browse the full service catalog. Request read:catalog_items or read:catalog_categories alone if you only need one sub-resource.
# Requesting the umbrella — gives access to both sub-resources
scope=read:catalog
# Requesting only one sub-resource
scope=read:catalog_items
Estimates — legacy alias for quotes
Zoop renamed "estimates" to "quotes" in May 2026. The read:estimates and write:estimates scopes still work and expand to read:quotes and write:quotes respectively. They exist so tokens minted before the rename continue to work without reauthorization.
New integrations should request read:quotes / write:quotes directly. The estimates aliases are kept for backward compatibility but are not the preferred form.
| Legacy scope | Expands to |
|---|---|
read:estimates | read:quotes |
write:estimates | write:quotes |
Sub-resources under customers
Contacts, locations, and tags are sub-resources of a customer. They share the customer scopes rather than having their own:
- To list or create contacts for a customer, your token needs
read:customersorwrite:customers. - The same applies to service locations and customer tags.
Notes are different. Notes have their own scope pair (read:notes / write:notes) and are independent of read:customers. Either an API key or an OAuth token that holds these scopes can use the notes endpoints.
There is one important restriction on note writes: creating, updating, or archiving a note requires a user-actor token (that is, an OAuth token or a user-actor API key), because every note change is attributed to the specific user who made it. A standard tenant-actor API key can read notes but cannot create or change them.
Admin scopes
Admin scopes grant elevated access to sensitive account-level operations. They can only be requested by integrations authorized by the tenant owner — office and technician roles cannot create tokens that include admin scopes.
Request admin scopes only when strictly necessary. Most integrations do not need them.
| Scope | What it grants |
|---|---|
admin:tenant | Manage team members and tenant settings |
admin:billing | Access billing and subscription info |
admin:credentials | Manage API keys and OAuth clients |
admin:credentials is required to create or revoke API keys via the API. The external access endpoints (/api/external-access) enforce this scope.
User-actor vs tenant-actor
Every credential in Zoop acts as one of two "actor" types, and the actor type decides whether a per-member ceiling applies.
- User-actor — the credential represents a specific team member. Both OAuth access tokens and user API keys (
zoop_uk_…) are user-actor credentials. Their effective scopes are bounded by that member's ceiling — see the per-member ceiling below. - Tenant-actor — a tenant API key (
zoop_tk_…) that acts on behalf of the business as a whole, not tied to any individual. Tenant keys are not bound to any member ceiling; their scopes are exactly what you set when creating the key.
So OAuth always gives you a user-actor token, while API keys come in both kinds depending on the type you create.
See OAuth and API keys for setup details.
The per-member ceiling
Every team member has a ceiling: the maximum set of external (API and MCP) scopes any credential they connect can ever use. The ceiling lives on their membership. An empty ceiling means no external access at all.
- The member's role sets the default ceiling when they accept their invite. An owner can then tighten or widen it per member from the Team page, in the MCP & API access dialog. Demoting a member (for example owner → tech) automatically narrows their ceiling to the new role's defaults, intersected with their current scopes — so a demotion can never leave them with more API or MCP access than the new role allows, while any owner-set scopes still within the new role's bounds are kept.
- The effective scope of any user-actor credential is
granted ∩ ceiling— the scopes attached to the credential intersected with the member's current ceiling. This is re-evaluated on every request. So if an owner lowers a member's ceiling, every outstanding OAuth token and user API key for that member is narrowed immediately — not just on the next token refresh. - Only owners can grant the admin scopes (
admin:tenant,admin:billing,admin:credentials) to an external client.
Default ceilings by role:
| Role | Default ceiling |
|---|---|
| Owner | Every area, read + write, plus admin |
| Office | Every area read + write, except catalog and settings are read-only; no admin |
| Tech | Customers, jobs, quotes, and messages read + write; invoices and catalog read-only; no settings; no admin |
Tenant keys (zoop_tk_…) ignore all of this — they represent the account, not a person.
Permission areas
In the consent screen and the per-member access dialog, scopes are not shown one by one. They are grouped into seven plain-language permission areas, each set to None, View, or Edit, plus an owner-only Admin toggle. View maps to the read:* scopes for that area; Edit adds the write:* scopes.
| Permission area | View grants | Edit adds |
|---|---|---|
| Customers & contacts | read:customers | write:customers |
| Jobs & scheduling | read:jobs, read:job_series | write:jobs, write:job_series |
| Quotes & estimates | read:quotes, read:estimates | write:quotes, write:estimates |
| Invoices & payments | read:invoices, read:plans | write:invoices, write:plans |
| Catalog & pricing | read:catalog, read:catalog_items, read:catalog_categories, read:tax_rates | write:catalog, write:catalog_items, write:catalog_categories, write:tax_rates |
| Messages & notes | read:communications, read:notes | write:communications, write:notes |
| Business settings | read:settings, read:companies | write:settings, write:companies |
| Admin access (owner only) | admin:tenant, admin:billing, admin:credentials | — |
These same areas appear when an owner sets a member's ceiling on the Team page and on the Settings screens. When a member connects an OAuth client, the consent screen offers the areas inside their current ceiling: a fresh self-registered client is offered the member's whole ceiling to choose from (there is no fixed read-only default set anymore), and a client that pre-registered scopes is narrowed down to the ceiling.
Requesting scopes
Pass your chosen scopes as a space-separated list in the scope parameter. The two tabs below show where that parameter goes for each auth method.
GET /oauth/authorize
?client_id=YOUR_CLIENT_ID
&redirect_uri=https://yourapp.example.com/callback
&response_type=code
&scope=read:customers write:customers read:jobs
&state=RANDOM_STATE_VALUE
curl -X POST https://app.zoop.pro/{tenantId}/api/external-access \
-H "Authorization: Bearer ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "My integration",
"scopes": ["read:customers", "read:jobs", "write:invoices"]
}'
https://app.zoop.pro is a placeholder. Resolve your real host from GET /.well-known/oauth-protected-resource — never hardcode it.
Choosing the right scopes
Request only the scopes your integration actually uses. Fewer scopes means less damage if a token is ever leaked, and users are more likely to approve a short, focused consent screen than a long one.
A typical read-only reporting integration needs:
read:customers read:jobs read:invoices read:quotes
A full-service scheduling integration might need:
read:customers write:customers read:jobs write:jobs read:catalog read:quotes write:quotes