AuthenticationScopes & permissions

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 data
  • write:* — 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.

ScopeConsent screen label
read:customersView customers, contacts, and locations
write:customersCreate and edit customers, contacts, and locations
read:catalogView service catalog and pricing
write:catalogManage service catalog and pricing
read:catalog_itemsView catalog items
write:catalog_itemsCreate and edit catalog items
read:catalog_categoriesView catalog categories
write:catalog_categoriesCreate and edit catalog categories
read:quotesView quotes
write:quotesCreate and edit quotes
read:estimatesView estimates
write:estimatesCreate and edit estimates
read:communicationsView messages and communication history
write:communicationsSend messages on your behalf
read:companiesView your business profile
write:companiesEdit your business settings
read:settingsView settings and configuration
write:settingsEdit settings and configuration
read:invoicesView invoices and payment history
write:invoicesCreate and edit invoices
read:jobsView jobs and job history
write:jobsCreate and edit jobs
read:job_seriesView recurring job series
write:job_seriesCreate and edit recurring job series
read:plansView service plans
write:plansCreate and edit service plans
read:tax_ratesView tax rates
write:tax_ratesCreate and edit tax rates
read:notesView notes
write:notesCreate and edit notes
read:agent_sessionsView AI agent session history
write:agent_sessionsRun 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 scopeExpands to
read:estimatesread:quotes
write:estimateswrite: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:customers or write: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.

ScopeWhat it grants
admin:tenantManage team members and tenant settings
admin:billingAccess billing and subscription info
admin:credentialsManage 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:

RoleDefault ceiling
OwnerEvery area, read + write, plus admin
OfficeEvery area read + write, except catalog and settings are read-only; no admin
TechCustomers, 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 areaView grantsEdit adds
Customers & contactsread:customerswrite:customers
Jobs & schedulingread:jobs, read:job_serieswrite:jobs, write:job_series
Quotes & estimatesread:quotes, read:estimateswrite:quotes, write:estimates
Invoices & paymentsread:invoices, read:planswrite:invoices, write:plans
Catalog & pricingread:catalog, read:catalog_items, read:catalog_categories, read:tax_rateswrite:catalog, write:catalog_items, write:catalog_categories, write:tax_rates
Messages & notesread:communications, read:noteswrite:communications, write:notes
Business settingsread:settings, read:companieswrite: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

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