> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tappify.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Scopes

> Every permission an owner can grant, what it unlocks, and which ones need a justification or a security review.

This is the reference for the scope keys a manifest may declare. A scope is a permission the
owner grants at install. Your manifest lists the ones you need; the install screen shows each
one with the sentence Tappify wrote for it and the sentence you wrote. Declare only what your
contributions use.

```bash theme={null}
tappify extension scopes add analytics:read
```

Leave the key off and the command asks which one, showing each scope by the name owners read.
The live table is also served at `https://api.tappify.ai/api/v1/extensions/registry`, which is
what the command reads.

## Every scope

| Key                    | Owners read                     | Grants                                                                                                        | Security review | Justification |
| ---------------------- | ------------------------------- | ------------------------------------------------------------------------------------------------------------- | --------------- | ------------- |
| `ui:render`            | Render UI                       | Render the declared pages, tabs, widgets, row actions and settings panel, and open chat with a visible prompt | no              | no            |
| `projects:read`        | Read the project                | Project name, platforms and store identity                                                                    | no              | no            |
| `analytics:read`       | Read analytics                  | Downloads, impressions, page views and conversion series                                                      | no              | no            |
| `store.metadata:read`  | Read store metadata             | Listing metadata, keyword set and keyword positions                                                           | no              | no            |
| `reviews:read`         | Read store reviews              | Review text, ratings and replies                                                                              | no              | no            |
| `crashes:read`         | Read crash data                 | Crash-free rate and issue counts                                                                              | no              | no            |
| `revenue:read`         | Read revenue                    | MRR, revenue series and refund counts                                                                         | no              | yes           |
| `metrics:write`        | Write connector metrics         | Write the metrics this extension declares into the metrics store                                              | no              | no            |
| `alerts:write`         | Raise alerts and banners        | Turn declared webhooks into alerts and banners, at most three a day per project                               | no              | yes           |
| `insights:write`       | Post insights                   | Post insights to Home and to chat                                                                             | no              | yes           |
| `ai:tools`             | Add assistant tools             | Offer tools and mentions to the assistant                                                                     | no              | no            |
| `ai:actions`           | Run assistant actions           | Run declared actions, each behind the approval card                                                           | yes             | yes           |
| `store.metadata:write` | Propose store metadata changes  | Propose listing changes through a Tappify approval                                                            | yes             | yes           |
| `autopilot:trigger`    | Trigger automated store actions | Start automated store actions on the owner's behalf                                                           | yes             | yes           |
| `work:create`          | Create work items               | Create issues, pages or messages in the vendor's system                                                       | no              | yes           |
| `work:sync`            | Sync work items                 | Two-way comments and status on created work items                                                             | no              | yes           |
| `storage:write`        | Use hosted storage              | Read and write this extension's own hosted storage collections                                                | no              | no            |
| `messaging:send`       | Send messages to end users      | Send messages or push notifications to the owner's end users through owner-supplied credentials               | yes             | yes           |
| `ai:skills`            | Shape the assistant             | Add skills, prompt templates and context providers that shape the assistant's behaviour                       | no              | yes           |

A manifest declares at least one scope and at most all nineteen.

A scope is declarable, consented to and carried on your install token from the day you publish.
What it unlocks is another matter. The read scopes, `ui:render`, `storage:write`,
`metrics:write`, `alerts:write`, `insights:write`, `messaging:send` and the three `ai:` scopes
serve live surfaces. `store.metadata:write`, `autopilot:trigger`, `work:create` and `work:sync`
describe surfaces that are still being built: declaring one now costs an owner a consent decision
and buys nothing yet, so ask for it in the release that uses it.

## What the owner sees

One row per scope on the install screen: the name from the "Owners read" column, then the
sentence from "Grants", then your justification as `<Your vendor name> says: …`. A scope that
needs a security review carries a "Security reviewed" chip beside its name.

## Justifications

A scope marked "justification" needs 20 to 500 characters saying why you need it.

```bash theme={null}
tappify extension scopes add revenue:read --justification "The payback widget divides ad spend by revenue for the same window."
```

Leave `--justification` off and the command asks for it, and refuses anything outside 20 to 500
characters. Write it for the owner deciding, not for a reviewer.

`tappify extension doctor --fix` writes a placeholder starting `Replace this:` for a scope it
adds on your behalf. That placeholder fails the `scopes.justification_placeholder` check, so a
release cannot carry it to an owner.

## Security review

Four scopes cannot go live until Tappify has set your vendor's security-review flag:
`ai:actions`, `store.metadata:write`, `autopilot:trigger` and `messaging:send`. That is a review
of your company, not of one release, so contact Tappify before you build against them. A publish
that needs the flag and does not have it fails the `scopes.security_review` check.

## What your contributions require

`doctor` derives these, and `--fix` adds them:

| You declare                                               | You need                                     |
| --------------------------------------------------------- | -------------------------------------------- |
| A page, tab, widget, row action, marker or settings panel | `ui:render`                                  |
| `contributes.markers`                                     | `alerts:write`                               |
| `contributes.storage`                                     | `storage:write`                              |
| `contributes.connector.metrics`                           | `metrics:write`                              |
| A webhook with `as: "alert"` or `as: "banner"`            | `alerts:write`                               |
| `contributes.ai.tools` or `contributes.ai.mentions`       | `ai:tools`                                   |
| `contributes.ai.actions`                                  | `ai:actions`, plus each action's own `scope` |
| `contributes.ai.skills`, `.prompts` or `.context`         | `ai:skills`                                  |
| `contributes.work`                                        | `work:create` and `work:sync`                |

Removing a scope that one of these rules still needs is refused, with the rule as the reason:

```bash theme={null}
tappify extension scopes remove storage:write
```

## What the assistant scopes unlock

`insights:write` is the chat half of "post insights to Home and to chat": with it,
`tap.ui.openInChat` puts a card from your own widget or page into the owner's transcript as a card
the host draws. Without it the same call falls back to a sentence Tappify writes about the card,
in the owner's composer for them to send.

`ai:tools` is what puts your tools in the assistant's tool set and your objects in the owner's
`@` picker, on every turn in a project where you are installed. `ai:skills` is what lists your
skills for the assistant to pick from, draws your prompt chips, and has your context providers
called at the start of a turn. `ai:actions` is what lets the assistant reach an action at all, on
top of the action's own scope, and because it needs a security review an owner can never stop
being asked before a run.

None of the three is checked once: every turn re-reads what the install actually holds, so a
scope the owner takes back stops being honoured from the next turn.

## What each data query needs

`tap.data.query` and `useTapQuery` take one of seven kinds, and each has one scope:

| Query kind | Scope                 |
| ---------- | --------------------- |
| `project`  | `projects:read`       |
| `series`   | `analytics:read`      |
| `keywords` | `store.metadata:read` |
| `listing`  | `store.metadata:read` |
| `reviews`  | `reviews:read`        |
| `crashes`  | `crashes:read`        |
| `revenue`  | `revenue:read`        |

A kind the data API does not serve comes back as `QUERY_KIND_UNSUPPORTED`.

## Asking at runtime

```tsx theme={null}
import { TapEmptyState, useTap } from "@tappify/extension-sdk";

export function Payback() {
  const tap = useTap();

  if (!tap.auth.can("revenue:read")) {
    return <TapEmptyState title="Revenue is not shared with this extension" />;
  }

  return null;
}
```

A data query, a storage call or a procedure outside your granted scopes throws
`TAP_SCOPE_MISSING` in your component. The backend's own code for that refusal is
`SCOPE_NOT_GRANTED`; the host turns it into `TAP_SCOPE_MISSING` before your component sees
it. When a new release asks for a scope the owner has not granted, the host
draws the missing-scope card and keeps serving the release they did grant.

<Card title="Errors" icon="triangle-exclamation" href="/extensions/reference/errors">
  Every code a refusal can carry, and what to do about it.
</Card>
