Skip to main content
tappify extension doctor runs these checks on your machine, and Tappify runs them again on the bundle you upload, so nothing here should be a surprise.

What the manifest has to pass

manifest.schema is one id carrying the whole schema, so these all arrive under it:
  • A widget’s slot that is not on its page, and a series naming a metric your connector does not declare.
  • Contributions that imply a scope you did not declare — rendered UI needs ui:render, hosted storage needs storage:write, markers need ui:render and alerts:write, an action needs ai:actions and its own declared scope.
  • A scope whose definition asks for a justification, without one of 20 to 500 characters.
  • Limits: 20 UI contributions, 15 assistant tools, 10 actions, 10 storage collections, 5 skills, 20 prompts, 3 context providers, 30 procedures, 20 telemetry names.
  • A banner or marker naming an event your webhooks do not declare.
  • A storage collection called settings, which the settings contribution owns.
  • A prompt whose after names a tool id that is not yours.
  • A telemetry name that is not snake_case.
  • server.baseUrl missing when a connector, webhook, assistant contribution, work destination or procedure exists, and any declared host that is not https.
  • An sdk range naming no major Tappify serves.
  • A public extension without all five listing fields.
  • An organization-scoped extension declaring pages, tabs, widgets or series.
  • A pricing or score block, which the schema reserves and does not accept yet.

What the bundle has to pass

What your server has to answer

There is no check id for this one. After the bundle passes, Tappify asks <base url>/tappify/health for a 200 within three seconds, on server.baseUrl and on server.sandboxBaseUrl if you declared one. A redirect is a failure, and the publish stops there. See Deploy your server.

What does not stop a publish

Three rules are warnings. They print under doctor and a release goes out with them: storage.pii, a storage schema field named email, name, phone, address or ip, which a reviewer reads before it goes live; procedures.write_kind, a write-shaped procedure name without kind: "write"; and bundle.style_injection, built code that writes styles into the document. A publish reports only what stopped it, so doctor is where you read them. Your typecheck, lint, tests and accessibility pass are not run at publish — publish runs doctor with your own scripts left out, because it has just run your build. doctor runs them, and they are the reason to run it before you publish rather than after. doctor also checks the files on your disk before they ever reach a zip — a missing entry, an icon that is not square, a screenshot at the wrong size, a $ref that points nowhere, a stale src/tappify.d.ts. Those ids are on Doctor checks.

What a reviewer reads

Automated checks decide whether a release can be reviewed at all. A person then reads:
  • Every scope you asked for against the justification you wrote, and whether the contributions need them.
  • The diff against your live release: which scopes are new, which contribution kinds are new, and whether the release widens who can install.
  • Every word your extension puts in front of the assistant — tool descriptions, skills, prompt templates, knowledge files.
  • Your listing, screenshots and privacy URL, for a public extension.
  • Your release notes.
A decision reaches your vendor admins in their inbox, and the Releases page in the developer portal shows it. A rejection carries a reason. Fix what the reason names and publish again; the next publish re-enters review. Unlisted extensions get the automated checks only; no person reads them. Private extensions are never reviewed.