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

# Manifest schema

> Every field of tappify.extension.json, generated from the published JSON Schema.

Every field `tappify.extension.json` accepts. This page is generated from the JSON Schema
the SDK, the CLI and Tappify all validate against, so it cannot drift from what a publish
enforces. Do not edit it by hand; the next SDK release overwrites it.

Point your editor at it and get completion and validation while you edit:

```json theme={null}
{ "$schema": "https://schema.tappify.ai/extension/v2.json" }
```

`tappify extension init` writes that line, and
[`tappify extension doctor`](/extensions/test/doctor-checks) adds it when it is missing.

## `$schema`

string · optional

The URL of this JSON Schema. Editors read it for completion and inline validation.

## `id`

string · required · pattern `^[a-z][a-z0-9-]{2,39}$`

The identifier for this extension, unique across the Tap Store and fixed after the first publish.

## `name`

string · required · 2–40 characters

The name owners see in the Tap Store and on every surface this extension renders.

## `description`

string · required · 10–200 characters

The one line owners read on the Tap Store card and on the install screen.

## `icon`

string · required · 1–200 characters

The icon file carried in the bundle, as a path relative to the manifest. Square png or svg, at most 512 KB.

## `category`

`"market_data"` | `"subscriptions"` | `"product_analytics"` | `"crashes"` | `"attribution"` | `"experiments"` | `"messaging"` | `"session_replay"` | `"localization"` | `"design"` | `"ci_cd"` | `"trackers"` | `"support"` | `"chat"` | `"ad_monetization"` | `"warehouse"` | `"compliance"` | `"additional_stores"` · required

The Tap Store section owners browse and filter by.

## `sdk`

string · required · 1–60 characters

The SDK range this build targets, such as "^2.0.0". The host serves one major at a time and rejects a range that excludes it.

## `visibility`

`"public"` | `"unlisted"` | `"private"` · required

Who can find and install this extension. "public" lists it in the Tap Store and needs all five listing fields; "unlisted" installs by link; "private" installs only in your own workspace.

## `installScope`

`"project"` | `"organization"` · required

Whether one install belongs to a single project or to a whole organization. An organization install contributes no pages, tabs, widgets or series, because those render on a project.

## `scopes`

object\[] · required · 1–19 entries

The permissions an owner grants at install. Declare only what your contributions use.

| Field           | Type                                                                                                                                                                                                                                                                                                                                                                                                  | Required | Means                                                                                                                                |
| --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `key`           | `"ui:render"` \| `"projects:read"` \| `"analytics:read"` \| `"store.metadata:read"` \| `"reviews:read"` \| `"crashes:read"` \| `"revenue:read"` \| `"metrics:write"` \| `"alerts:write"` \| `"insights:write"` \| `"ai:tools"` \| `"ai:actions"` \| `"store.metadata:write"` \| `"autopilot:trigger"` \| `"work:create"` \| `"work:sync"` \| `"storage:write"` \| `"messaging:send"` \| `"ai:skills"` | yes      | The scope key, from the scopes reference.                                                                                            |
| `justification` | string, 20–500 characters                                                                                                                                                                                                                                                                                                                                                                             | no       | Why this extension asks for the scope, in your own words. Shown on the consent screen, and required for the scopes that ask for one. |

## `contributes`

object · required

What this extension adds to the host. At most 20 UI contributions in total, and a page, tab, widget, row action, marker or settings panel needs the `ui:render` scope.

| Field        | Type                                             | Required | Means                                                                                                                                                                                                                                      |
| ------------ | ------------------------------------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `pages`      | object\[], at most 20 entries                    | no       | Pages of this extension's own, each on its own route under the project. `nav: true` also puts one in the project navigation.                                                                                                               |
| `tabs`       | object\[], at most 20 entries                    | no       | Tabs beside the host's own on a named host page.                                                                                                                                                                                           |
| `widgets`    | object\[], at most 20 entries                    | no       | Cards drawn into a named slot on a host page.                                                                                                                                                                                              |
| `series`     | object\[], at most 20 entries                    | no       | Lines drawn onto a host chart from a metric the connector writes. The host draws the line on the chart this names and puts the vendor in the chart legend; a stale metric is drawn dashed with the time of its last sync.                  |
| `banners`    | object\[], at most 20 entries                    | no       | Banners raised on a host page by a webhook declared with as: "banner". The host draws one at the top of that page, carrying the `why` sentence from the delivery, until the owner dismisses it.                                            |
| `rowActions` | object\[], at most 20 entries                    | no       | Actions offered on a row of a host table.                                                                                                                                                                                                  |
| `markers`    | object\[], at most 20 entries                    | no       | Markers drawn on a host chart by a webhook declared with as: "event". Needs the `ui:render` and `alerts:write` scopes. The host draws the glyph on the chart time axis at the moment the delivery arrived, with the vendor named on hover. |
| `settings`   | object                                           | no       | The settings panel owners open for this install.                                                                                                                                                                                           |
| `storage`    | record of object, keys match `^[a-z][a-z0-9_]*$` | no       | The hosted storage collections this extension reads and writes. At most 10, and the name "settings" is reserved. Needs the `storage:write` scope.                                                                                          |
| `connector`  | object                                           | no       | How this extension authenticates against an outside service, how often it syncs, and the metrics it writes.                                                                                                                                |
| `webhooks`   | object\[], at most 20 entries                    | no       | The events this extension's server posts to Tappify, and what each one becomes. An `alert` or a `banner` needs the `alerts:write` scope.                                                                                                   |
| `ai`         | object                                           | no       | What this extension offers the assistant: tools, mentions, actions, knowledge, skills, prompts and context providers.                                                                                                                      |
| `work`       | object                                           | no       | The work destination this extension creates issues, pages or messages in. Needs the `work:create` and `work:sync` scopes.                                                                                                                  |

### `contributes.pages`

object\[] · optional · at most 20 entries

Pages of this extension's own, each on its own route under the project. `nav: true` also puts one in the project navigation.

| Field    | Type                                     | Required | Means                                                                                                                                        |
| -------- | ---------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`     | string, pattern `^[a-z][a-z0-9-]{1,39}$` | yes      | The id for this contribution, and the handle the host and your generated types refer to it by.                                               |
| `title`  | string, 1–60 characters                  | yes      | The label owners read on this contribution.                                                                                                  |
| `entry`  | string, 1–200 characters                 | yes      | The source file that renders this contribution, as a path relative to the manifest. It has to be present as an expose in the built bundle.   |
| `nav`    | boolean                                  | no       | Whether this page also gets an entry in the project navigation. The route `/projects/:projectId/ext/:extensionId/:pageId` exists either way. |
| `mobile` | boolean                                  | no       | Whether this surface is drawn on phone layouts. Defaults to true; set false to hide it there.                                                |

### `contributes.tabs`

object\[] · optional · at most 20 entries

Tabs beside the host's own on a named host page.

| Field    | Type                                                            | Required | Means                                                                                                                                      |
| -------- | --------------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `id`     | string, pattern `^[a-z][a-z0-9-]{1,39}$`                        | yes      | The id for this contribution, and the handle the host and your generated types refer to it by.                                             |
| `title`  | string, 1–60 characters                                         | yes      | The label owners read on this contribution.                                                                                                |
| `entry`  | string, 1–200 characters                                        | yes      | The source file that renders this contribution, as a path relative to the manifest. It has to be present as an expose in the built bundle. |
| `page`   | `"overview"` \| `"analytics"` \| `"deployments"` \| `"ai_chat"` | yes      | The host page this tab sits on.                                                                                                            |
| `mobile` | boolean                                                         | no       | Whether this surface is drawn on phone layouts. Defaults to true; set false to hide it there.                                              |

### `contributes.widgets`

object\[] · optional · at most 20 entries

Cards drawn into a named slot on a host page.

| Field        | Type                                                                            | Required | Means                                                                                                                                      |
| ------------ | ------------------------------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `id`         | string, pattern `^[a-z][a-z0-9-]{1,39}$`                                        | yes      | The id for this contribution, and the handle the host and your generated types refer to it by.                                             |
| `title`      | string, 1–60 characters                                                         | yes      | The label owners read on this contribution.                                                                                                |
| `entry`      | string, 1–200 characters                                                        | yes      | The source file that renders this contribution, as a path relative to the manifest. It has to be present as an expose in the built bundle. |
| `page`       | `"overview"` \| `"analytics"` \| `"deployments"` \| `"ai_chat"`                 | yes      | The host page this widget renders on.                                                                                                      |
| `slot`       | `"kpi-row"` \| `"insights"` \| `"sidebar"` \| `"below-chart"` \| `"below-list"` | yes      | The named anchor on that page. A slot that is not on the page fails validation.                                                            |
| `size`       | `"1x1"` \| `"2x1"` \| `"2x2"`                                                   | yes      | The widget's footprint in the slot grid, as columns by rows. Every slot takes all three.                                                   |
| `expandable` | boolean                                                                         | no       | Whether owners can open the same component in the expand panel.                                                                            |

### `contributes.series`

object\[] · optional · at most 20 entries

Lines drawn onto a host chart from a metric the connector writes. The host draws the line on the chart this names and puts the vendor in the chart legend; a stale metric is drawn dashed with the time of its last sync.

| Field    | Type                                                           | Required | Means                                                                                          |
| -------- | -------------------------------------------------------------- | -------- | ---------------------------------------------------------------------------------------------- |
| `id`     | string, pattern `^[a-z][a-z0-9-]{1,39}$`                       | yes      | The id for this contribution, and the handle the host and your generated types refer to it by. |
| `label`  | string, 1–60 characters                                        | yes      | The label owners read on this contribution.                                                    |
| `chart`  | `"installs-activity"` \| `"conversion"` \| `"release-cadence"` | yes      | The host chart this line is drawn on.                                                          |
| `metric` | string, 1–60 characters                                        | yes      | The connector metric this line plots. It has to be one the connector declares.                 |

### `contributes.banners`

object\[] · optional · at most 20 entries

Banners raised on a host page by a webhook declared with as: "banner". The host draws one at the top of that page, carrying the `why` sentence from the delivery, until the owner dismisses it.

| Field   | Type                                                            | Required | Means                                                                                             |
| ------- | --------------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------- |
| `id`    | string, pattern `^[a-z][a-z0-9-]{1,39}$`                        | yes      | The id for this contribution, and the handle the host and your generated types refer to it by.    |
| `page`  | `"overview"` \| `"analytics"` \| `"deployments"` \| `"ai_chat"` | yes      | The host page this banner appears on.                                                             |
| `event` | string, pattern `^[a-z][a-z0-9_]*\.[a-z][a-z0-9_]*$`            | yes      | The event that raises it. Declare the same name under `contributes.webhooks` with `as: "banner"`. |

### `contributes.rowActions`

object\[] · optional · at most 20 entries

Actions offered on a row of a host table.

| Field   | Type                                                                                                  | Required | Means                                                                                                                                      |
| ------- | ----------------------------------------------------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `id`    | string, pattern `^[a-z][a-z0-9-]{1,39}$`                                                              | yes      | The id for this contribution, and the handle the host and your generated types refer to it by.                                             |
| `title` | string, 1–60 characters                                                                               | yes      | The label owners read on this contribution.                                                                                                |
| `table` | `"analytics.keywords"` \| `"analytics.reviews"` \| `"deployments.releases"` \| `"deployments.builds"` | yes      | The host table whose row menu gains this entry.                                                                                            |
| `entry` | string, 1–200 characters                                                                              | yes      | The source file that renders this contribution, as a path relative to the manifest. It has to be present as an expose in the built bundle. |

### `contributes.markers`

object\[] · optional · at most 20 entries

Markers drawn on a host chart by a webhook declared with as: "event". Needs the `ui:render` and `alerts:write` scopes. The host draws the glyph on the chart time axis at the moment the delivery arrived, with the vendor named on hover.

| Field   | Type                                                                    | Required | Means                                                                                                             |
| ------- | ----------------------------------------------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------- |
| `id`    | string, pattern `^[a-z][a-z0-9-]{1,39}$`                                | yes      | The id for this contribution, and the handle the host and your generated types refer to it by.                    |
| `label` | string, 1–60 characters                                                 | yes      | The label owners read on this contribution.                                                                       |
| `chart` | `"installs-activity"` \| `"conversion"` \| `"release-cadence"` \| `"*"` | yes      | The host chart this marker is drawn on, or `*` for every chart.                                                   |
| `event` | string, pattern `^[a-z][a-z0-9_]*\.[a-z][a-z0-9_]*$`                    | yes      | The event that places it on the time axis. Declare the same name under `contributes.webhooks` with `as: "event"`. |
| `glyph` | string, 1–2 characters                                                  | yes      | The one or two characters drawn at the marker.                                                                    |

### `contributes.settings`

object · optional

The settings panel owners open for this install.

| Field    | Type                     | Required | Means                                                                                                                                      |
| -------- | ------------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `entry`  | string, 1–200 characters | yes      | The source file that renders this contribution, as a path relative to the manifest. It has to be present as an expose in the built bundle. |
| `schema` | record of unknown        | yes      | The JSON Schema for the values owners fill in. It becomes `TapSettingsProps["values"]` and the reserved `settings` singleton.              |

### `contributes.storage`

record of object · optional · keys match `^[a-z][a-z0-9_]*$`

The hosted storage collections this extension reads and writes. At most 10, and the name "settings" is reserved. Needs the `storage:write` scope.

| Field       | Type                                        | Required | Means                                                                                |
| ----------- | ------------------------------------------- | -------- | ------------------------------------------------------------------------------------ |
| `scope`     | `"user"` \| `"install"` \| `"organization"` | yes      | Who the documents belong to: one `user`, the whole `install`, or the `organization`. |
| `singleton` | boolean                                     | no       | Whether the collection holds exactly one document rather than many.                  |
| `schema`    | record of unknown                           | yes      | The JSON Schema for one document in this collection.                                 |

### `contributes.connector`

object · optional

How this extension authenticates against an outside service, how often it syncs, and the metrics it writes.

| Field     | Type                                             | Required | Means                                                                                      |
| --------- | ------------------------------------------------ | -------- | ------------------------------------------------------------------------------------------ |
| `auth`    | object                                           | no       | How owners authenticate this connector.                                                    |
| `sync`    | `"5m"` \| `"15m"` \| `"1h"` \| `"6h"` \| `"24h"` | no       | How often Tappify asks your server to sync.                                                |
| `metrics` | object\[], at most 50 entries                    | no       | The metrics this connector writes into the metrics store. Needs the `metrics:write` scope. |

#### `contributes.connector.auth`

object · optional

How owners authenticate this connector.

| Field    | Type                                 | Required | Means                                                                                |
| -------- | ------------------------------------ | -------- | ------------------------------------------------------------------------------------ |
| `method` | `"none"` \| `"api_key"` \| `"oauth"` | yes      | The credential flow owners go through at install.                                    |
| `fields` | object\[], at most 20 entries        | no       | The credentials owners enter. They reach your server as `credentials` on every call. |
| `oauth`  | object                               | no       | The endpoints, for `method: "oauth"`.                                                |

#### `contributes.connector.auth.fields`

object\[] · optional · at most 20 entries

The credentials owners enter. They reach your server as `credentials` on every call.

| Field      | Type                                                               | Required | Means                                                       |
| ---------- | ------------------------------------------------------------------ | -------- | ----------------------------------------------------------- |
| `name`     | string, pattern `^[a-z][a-z0-9_]*$`                                | yes      | The key your server reads this value back as.               |
| `label`    | string, 1–60 characters                                            | yes      | The label owners read above the input.                      |
| `type`     | `"text"` \| `"password"` \| `"textarea"` \| `"file"` \| `"select"` | yes      | How the input is drawn to the owner.                        |
| `required` | boolean                                                            | yes      | Whether owners have to fill it in before the install works. |
| `options`  | string\[]                                                          | no       | The choices offered, for a `select` field.                  |
| `help`     | string, at most 200 characters                                     | no       | The hint drawn under the input.                             |

#### `contributes.connector.auth.oauth`

object · optional

The endpoints, for `method: "oauth"`.

| Field              | Type                                          | Required | Means                                                                                    |
| ------------------ | --------------------------------------------- | -------- | ---------------------------------------------------------------------------------------- |
| `authorizationUrl` | string, pattern `^https:\/\/.*`, format `uri` | yes      | Where owners are sent to approve access.                                                 |
| `tokenUrl`         | string, pattern `^https:\/\/.*`, format `uri` | yes      | Where the authorization code is exchanged for a token.                                   |
| `scopes`           | string\[]                                     | yes      | The scopes asked of the outside service. These are that service's scopes, not Tappify's. |

#### `contributes.connector.metrics`

object\[] · optional · at most 50 entries

The metrics this connector writes into the metrics store. Needs the `metrics:write` scope.

| Field          | Type                                                               | Required | Means                                                                                 |
| -------------- | ------------------------------------------------------------------ | -------- | ------------------------------------------------------------------------------------- |
| `key`          | string, pattern `^[a-z][a-z0-9_]*$`                                | yes      | The metric key. A series names it to draw its line.                                   |
| `label`        | string, 1–60 characters                                            | yes      | The label owners read on the metric.                                                  |
| `unit`         | `"count"` \| `"ratio"` \| `"currency"` \| `"seconds"` \| `"bytes"` | yes      | How the value is formatted.                                                           |
| `kind`         | `"gauge"` \| `"counter"`                                           | yes      | Whether the value is a level at a point in time (`gauge`) or accumulates (`counter`). |
| `dimensions`   | object\[], at most 10 entries                                      | no       | The breakdowns this metric can be split by.                                           |
| `backfillDays` | integer, minimum 1, maximum 90                                     | no       | How many days of history to fetch on the first sync.                                  |

#### `contributes.connector.metrics.dimensions`

object\[] · optional · at most 10 entries

The breakdowns this metric can be split by.

| Field   | Type                                | Required | Means                                   |
| ------- | ----------------------------------- | -------- | --------------------------------------- |
| `key`   | string, pattern `^[a-z][a-z0-9_]*$` | yes      | The dimension key.                      |
| `label` | string, 1–60 characters             | yes      | The label owners read on the dimension. |

### `contributes.webhooks`

object\[] · optional · at most 20 entries

The events this extension's server posts to Tappify, and what each one becomes. An `alert` or a `banner` needs the `alerts:write` scope.

| Field     | Type                                                 | Required | Means                                                                                                                          |
| --------- | ---------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `event`   | string, pattern `^[a-z][a-z0-9_]*\.[a-z][a-z0-9_]*$` | yes      | The event name your server posts, as `domain.thing`.                                                                           |
| `as`      | `"alert"` \| `"banner"` \| `"event"`                 | yes      | What Tappify turns it into: an inbox `alert`, a page `banner`, or an `event` a chart marker and `tap.data.subscribe` can read. |
| `payload` | record of unknown                                    | yes      | The JSON Schema for the body your server posts. An alert or a banner has to carry `why`.                                       |

### `contributes.ai`

object · optional

What this extension offers the assistant: tools, mentions, actions, knowledge, skills, prompts and context providers.

| Field       | Type                          | Required | Means                                                                                                                                                                                                                                                                     |
| ----------- | ----------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `tools`     | object\[], at most 15 entries | no       | The tools the assistant may call on your server. Needs the `ai:tools` scope.                                                                                                                                                                                              |
| `mentions`  | object\[], at most 10 entries | no       | The things owners can name with @ in chat, and where to list them. Needs the `ai:tools` scope.                                                                                                                                                                            |
| `actions`   | object\[], at most 10 entries | no       | The actions the assistant may run, each behind an owner approval. Needs the `ai:actions` scope, and each action's own scope. `tap.actions.run` creates the run and resolves with it; the host draws the approval card and calls your server only once the owner approves. |
| `knowledge` | object\[], at most 20 entries | no       | Markdown the assistant indexes and cites when it answers about this extension.                                                                                                                                                                                            |
| `skills`    | object\[], at most 5 entries  | no       | Markdown procedures that shape how the assistant works. Needs the `ai:skills` scope.                                                                                                                                                                                      |
| `prompts`   | object\[], at most 20 entries | no       | Prompt templates the host offers owners as chips. Needs the `ai:skills` scope.                                                                                                                                                                                            |
| `context`   | object\[], at most 3 entries  | no       | Providers the host calls to put your data in front of the assistant. Needs the `ai:skills` scope.                                                                                                                                                                         |

#### `contributes.ai.tools`

object\[] · optional · at most 15 entries

The tools the assistant may call on your server. Needs the `ai:tools` scope.

| Field         | Type                                                               | Required | Means                                                                                                                                                                                                 |
| ------------- | ------------------------------------------------------------------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`          | string, pattern `^[a-z][a-z0-9-]{1,39}$`                           | yes      | The id for this contribution, and the handle the host and your generated types refer to it by.                                                                                                        |
| `description` | string, 10–500 characters                                          | yes      | What this tool does, in the words the assistant reads when it decides whether to call it.                                                                                                             |
| `input`       | record of unknown                                                  | yes      | The JSON Schema for the arguments the assistant passes. The assistant reads the `title` and `description` strings inside it, so they are scanned at publish and the whole schema is bounded at 16 KB. |
| `returns`     | `"comparison"` \| `"table"` \| `"series"` \| `"list"` \| `"value"` | yes      | The card the host draws from the result.                                                                                                                                                              |
| `cost`        | `"low"` \| `"medium"` \| `"high"`                                  | yes      | Roughly what one call costs to serve, so the assistant can prefer a cheaper tool.                                                                                                                     |
| `cache`       | `"1m"` \| `"5m"` \| `"1h"`                                         | no       | How long an identical call may be served from cache instead of reaching your server.                                                                                                                  |

#### `contributes.ai.mentions`

object\[] · optional · at most 10 entries

The things owners can name with @ in chat, and where to list them. Needs the `ai:tools` scope.

| Field   | Type                                     | Required | Means                                                                                                                                                                                                                                                       |
| ------- | ---------------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`    | string, pattern `^[a-z][a-z0-9-]{1,39}$` | yes      | The id for this contribution, and the handle the host and your generated types refer to it by.                                                                                                                                                              |
| `label` | string, 1–60 characters                  | yes      | The label owners read on this contribution.                                                                                                                                                                                                                 |
| `list`  | string, 1–200 characters                 | yes      | The path that answers the picker with `{ items: [{ id, label }] }`. The host always calls `/tappify/mentions/<id>` under `server.baseUrl` — the route `createTappifyHandler` serves — so write that path here; it records the route, it does not choose it. |

#### `contributes.ai.actions`

object\[] · optional · at most 10 entries

The actions the assistant may run, each behind an owner approval. Needs the `ai:actions` scope, and each action's own scope. `tap.actions.run` creates the run and resolves with it; the host draws the approval card and calls your server only once the owner approves.

| Field           | Type                                                                                                                                                                                                                                                                                                                                                                                                  | Required | Means                                                                                                                                                                                                 |
| --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`            | string, pattern `^[a-z][a-z0-9-]{1,39}$`                                                                                                                                                                                                                                                                                                                                                              | yes      | The id for this contribution, and the handle the host and your generated types refer to it by.                                                                                                        |
| `description`   | string, 10–500 characters                                                                                                                                                                                                                                                                                                                                                                             | yes      | What this action does, in the words the owner will read on the approval card.                                                                                                                         |
| `input`         | record of unknown                                                                                                                                                                                                                                                                                                                                                                                     | yes      | The JSON Schema for the arguments the assistant passes. The assistant reads the `title` and `description` strings inside it, so they are scanned at publish and the whole schema is bounded at 16 KB. |
| `approval`      | `true`                                                                                                                                                                                                                                                                                                                                                                                                | yes      | Always true. An action goes through an owner approval; there is no way to opt out.                                                                                                                    |
| `reversible`    | boolean                                                                                                                                                                                                                                                                                                                                                                                               | yes      | Whether the owner can undo it after it runs.                                                                                                                                                          |
| `scope`         | `"ui:render"` \| `"projects:read"` \| `"analytics:read"` \| `"store.metadata:read"` \| `"reviews:read"` \| `"crashes:read"` \| `"revenue:read"` \| `"metrics:write"` \| `"alerts:write"` \| `"insights:write"` \| `"ai:tools"` \| `"ai:actions"` \| `"store.metadata:write"` \| `"autopilot:trigger"` \| `"work:create"` \| `"work:sync"` \| `"storage:write"` \| `"messaging:send"` \| `"ai:skills"` | yes      | The scope this action needs. Declare it under `scopes` as well.                                                                                                                                       |
| `estimatesCost` | boolean                                                                                                                                                                                                                                                                                                                                                                                               | no       | Whether the approval card will show what the action costs.                                                                                                                                            |

#### `contributes.ai.knowledge`

object\[] · optional · at most 20 entries

Markdown the assistant indexes and cites when it answers about this extension.

| Field      | Type                                               | Required | Means                                                                                          |
| ---------- | -------------------------------------------------- | -------- | ---------------------------------------------------------------------------------------------- |
| `id`       | string, pattern `^[a-z][a-z0-9-]{1,39}$`           | yes      | The id for this contribution, and the handle the host and your generated types refer to it by. |
| `category` | `"metric_definitions"` \| `"gotchas"` \| `"setup"` | yes      | What kind of knowledge the file carries.                                                       |
| `file`     | string, 1–200 characters                           | yes      | The markdown file in the bundle, as a path relative to the manifest.                           |

#### `contributes.ai.skills`

object\[] · optional · at most 5 entries

Markdown procedures that shape how the assistant works. Needs the `ai:skills` scope.

| Field         | Type                                     | Required | Means                                                                                          |
| ------------- | ---------------------------------------- | -------- | ---------------------------------------------------------------------------------------------- |
| `id`          | string, pattern `^[a-z][a-z0-9-]{1,39}$` | yes      | The id for this contribution, and the handle the host and your generated types refer to it by. |
| `name`        | string, 1–60 characters                  | yes      | The skill name the assistant matches on.                                                       |
| `description` | string, 10–300 characters                | yes      | When the assistant should reach for this skill.                                                |
| `file`        | string, 1–200 characters                 | yes      | The markdown file in the bundle, as a path relative to the manifest.                           |
| `tools`       | string\[], at most 15 entries            | no       | The tool ids this skill is allowed to use.                                                     |

#### `contributes.ai.prompts`

object\[] · optional · at most 20 entries

Prompt templates the host offers owners as chips. Needs the `ai:skills` scope.

| Field      | Type                                                                                    | Required | Means                                                                                                                                |
| ---------- | --------------------------------------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `id`       | string, pattern `^[a-z][a-z0-9-]{1,39}$`                                                | yes      | The id for this contribution, and the handle the host and your generated types refer to it by.                                       |
| `title`    | string, 1–60 characters                                                                 | yes      | The label owners read on this contribution.                                                                                          |
| `template` | string, 1–2000 characters                                                               | yes      | The prompt text, with `{{project.*}}`, `{{context.*}}` and `{{input.*}}` placeholders. The owner sees it resolved before it is sent. |
| `surfaces` | (`"chat_suggestion"` \| `"widget"` \| `"page"` \| `"result_card"`)\[], at least 1 entry | yes      | Where the host draws this prompt as a chip.                                                                                          |
| `after`    | string\[], at most 15 entries                                                           | no       | The tool ids whose results this prompt follows up on. They have to be this extension's own.                                          |
| `input`    | record of unknown                                                                       | no       | The JSON Schema for a short form the owner fills in before the prompt is sent.                                                       |

#### `contributes.ai.context`

object\[] · optional · at most 3 entries

Providers the host calls to put your data in front of the assistant. Needs the `ai:skills` scope.

| Field         | Type                                                 | Required | Means                                                                                          |
| ------------- | ---------------------------------------------------- | -------- | ---------------------------------------------------------------------------------------------- |
| `id`          | string, pattern `^[a-z][a-z0-9-]{1,39}$`             | yes      | The id for this contribution, and the handle the host and your generated types refer to it by. |
| `description` | string, 10–300 characters                            | yes      | What context this provider adds to the turn.                                                   |
| `when`        | (`"turn_start"` \| `"mention"`)\[], at least 1 entry | yes      | When the host calls your server for it.                                                        |
| `cache`       | `"1m"` \| `"5m"` \| `"1h"`                           | no       | How long the context may be reused before refetching.                                          |

### `contributes.work`

object · optional

The work destination this extension creates issues, pages or messages in. Needs the `work:create` and `work:sync` scopes.

| Field         | Type                                                        | Required | Means                                                                               |
| ------------- | ----------------------------------------------------------- | -------- | ----------------------------------------------------------------------------------- |
| `kind`        | `"tracker"` \| `"docs"` \| `"chat"`                         | yes      | What kind of destination this is.                                                   |
| `container`   | object                                                      | yes      | Where items land, and how owners pick one.                                          |
| `creates`     | (`"issue"` \| `"page"` \| `"message"`)\[], at least 1 entry | yes      | What this destination can create.                                                   |
| `fields`      | object                                                      | yes      | What a created item carries.                                                        |
| `attachments` | (`"image"` \| `"link"`)\[]                                  | yes      | What can be attached to a created item.                                             |
| `comments`    | `"two-way"` \| `"none"`                                     | yes      | Whether comments sync back and forth or not at all.                                 |
| `status`      | object                                                      | no       | How status travels back from your system.                                           |
| `identity`    | `"user"` \| `"install"`                                     | yes      | Whether items are created as the owner who asked (`user`) or as the install itself. |

#### `contributes.work.container`

object · required

Where items land, and how owners pick one.

| Field   | Type                         | Required | Means                                                                                                  |
| ------- | ---------------------------- | -------- | ------------------------------------------------------------------------------------------------------ |
| `label` | string, 1–60 characters      | yes      | What you call a container, such as "Project", "Space" or "Channel". Owners read it when they pick one. |
| `list`  | string, at least 1 character | yes      | The path under `server.baseUrl` that lists the containers an owner can choose from.                    |

#### `contributes.work.fields`

object · required

What a created item carries.

| Field      | Type      | Required | Means                                                     |
| ---------- | --------- | -------- | --------------------------------------------------------- |
| `priority` | string\[] | no       | Your own priority names, highest first.                   |
| `labels`   | boolean   | no       | Whether items carry labels.                               |
| `assignee` | boolean   | no       | Whether items carry an assignee.                          |
| `required` | string\[] | yes      | The fields an owner has to fill in before you can create. |

#### `contributes.work.status`

object · optional

How status travels back from your system.

| Field     | Type                                                 | Required | Means                                                           |
| --------- | ---------------------------------------------------- | -------- | --------------------------------------------------------------- |
| `webhook` | string, pattern `^[a-z][a-z0-9_]*\.[a-z][a-z0-9_]*$` | yes      | The event your server posts when an item changes status.        |
| `map`     | object                                               | yes      | Your status names, grouped into the three stages Tappify draws. |

#### `contributes.work.status.map`

object · required

Your status names, grouped into the three stages Tappify draws.

| Field         | Type      | Required | Means                                        |
| ------------- | --------- | -------- | -------------------------------------------- |
| `open`        | string\[] | yes      | Your own status names that mean not started. |
| `in_progress` | string\[] | yes      | Your own status names that mean under way.   |
| `done`        | string\[] | yes      | Your own status names that mean finished.    |

## `server`

object · optional

Your own server: where the host calls it, and what it answers. Required once you declare a connector, a webhook, an assistant contribution, a work destination or a procedure.

| Field            | Type                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 | Required | Means                                                                                                                                                          |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `baseUrl`        | string, pattern `^https:\/\/.*`, format `uri`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        | yes      | Where the host calls your server. `<baseUrl>/tappify/health` answers 200 within three seconds at publish.                                                      |
| `sandboxBaseUrl` | string, pattern `^https:\/\/.*`, format `uri`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        | no       | Where dev installs and sandbox projects call instead. Defaults to `baseUrl`.                                                                                   |
| `procedures`     | record of object, keys match `^[a-zA-Z][a-zA-Z0-9_-]{1,59}$`                                                                                                                                                                                                                                                                                                                                                                                                                                                                         | no       | The procedures the host may call on your server, by name, each with its input and output schema. At most 30.                                                   |
| `events`         | (`"release.shipped"` \| `"metadata.changed"` \| `"keyword.set_changed"` \| `"screenshots.updated"` \| `"price.changed"` \| `"featuring.started"` \| `"score.changed"` \| `"incident.opened"` \| `"incident.resolved"` \| `"playbook.step_approved"` \| `"review.thread_opened"` \| `"review.thread_resolved"` \| `"approval.requested"` \| `"digest.sent"` \| `"install.created"` \| `"install.paused"` \| `"install.resumed"` \| `"install.revoked"` \| `"install.token_rotated"` \| `"scopes.changed"` \| `"settings.changed"`)\[] | no       | The Tappify events your server subscribes to. Each is delivered to `POST /tappify/events` as it happens, with the same body every host-to-vendor call carries. |

### `server.procedures`

record of object · optional · keys match `^[a-zA-Z][a-zA-Z0-9_-]{1,59}$`

The procedures the host may call on your server, by name, each with its input and output schema. At most 30.

| Field    | Type                       | Required | Means                                                                                                 |
| -------- | -------------------------- | -------- | ----------------------------------------------------------------------------------------------------- |
| `input`  | record of unknown          | yes      | The JSON Schema for the argument your handler receives. The host validates against it before calling. |
| `output` | record of unknown          | yes      | The JSON Schema for what your handler returns.                                                        |
| `cache`  | `"1m"` \| `"5m"` \| `"1h"` | no       | How long an identical call may be served from cache instead of reaching your server.                  |
| `kind`   | `"read"` \| `"write"`      | no       | Whether this procedure only reads or also changes something.                                          |

## `telemetry`

string\[] · optional · at most 20 entries · each pattern `^[a-z][a-z0-9_]*$` · each at most 60 characters

The telemetry names this extension may report. `tap.telemetry.event` rejects a name that is not declared here.

## `listing`

object · optional

What owners read on your Tap Store listing page. A public extension needs all five fields.

| Field             | Type                                              | Required | Means                                                                                           |
| ----------------- | ------------------------------------------------- | -------- | ----------------------------------------------------------------------------------------------- |
| `longDescription` | string, 1–5000 characters                         | no       | The body of your listing page, in markdown.                                                     |
| `screenshots`     | string\[], 1–6 entries, each at least 1 character | no       | Screenshot files carried in the bundle, as paths relative to the manifest. png at 1600 by 1000. |
| `website`         | string, pattern `^https:\/\/.*`, format `uri`     | no       | Where owners go to learn more about this extension.                                             |
| `supportUrl`      | string, pattern `^https:\/\/.*`, format `uri`     | no       | Where owners go when something breaks.                                                          |
| `privacyUrl`      | string, pattern `^https:\/\/.*`, format `uri`     | no       | Your privacy policy, linked from the consent screen before anyone installs.                     |

## `pricing`

never · optional

Reserved. Tappify sets pricing outside the manifest, so a manifest that carries this field fails validation.

## `score`

never · optional

Reserved. Tappify computes the quality score, so a manifest that carries this field fails validation.
