Skip to main content
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.
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

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.
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: Removing a scope that one of these rules still needs is refused, with the rule as the reason:

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: A kind the data API does not serve comes back as QUERY_KIND_UNSUPPORTED.

Asking at runtime

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.

Errors

Every code a refusal can carry, and what to do about it.