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.--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
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.