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

# Tap

> The bridge a mount talks to the host through, which useTap returns.

The bridge a mount talks to the host through, which `useTap` returns.

## Remarks

The host replaces `auth`, `project`, `filters`, `theme`, `ui`, `params` and
`context` with new objects rather than mutating them, so the `useTap*` hooks
can compare snapshots by reference. Read a value through its hook when the
component has to re-render on a change, and through `tap` when it does not.

## Example

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

function Installs({ count }: { count: number }) {
  const tap = useTap();
  return <TapStat label="Installs" value={tap.format.number(count)} />;
}
```

## Extends

* [`TapInternals`](/extensions/reference/frontend-api/Interface.TapInternals)

## Properties

### \_\_portal

```ts theme={null}
__portal: HTMLElement | null;
```

The portal node inside the mount's shadow root, or `null` until the shadow
root exists.

#### Inherited from

[`TapInternals`](/extensions/reference/frontend-api/Interface.TapInternals).[`__portal`](/extensions/reference/frontend-api/Interface.TapInternals#__portal)

***

### actions

```ts theme={null}
actions: {
  run: Promise<TapActionRun>;
};
```

#### run()

```ts theme={null}
run<A>(actionId, input): Promise<TapActionRun>;
```

Creates the run and resolves with it as soon as it exists. `status` is
`pending` while the owner has still to approve — the host draws the
approval card — and `succeeded` or `failed` when the owner has turned
asking off for this install, which no action-declaring extension can do
today. Rejects with a `TapServerError` when the host refuses the run.

##### Type Parameters

###### A

`A` *extends* `string`

##### Parameters

###### actionId

`A`

###### input

[`ActionInput`](/extensions/reference/frontend-api/TypeAlias.ActionInput)\<`A`>

##### Returns

`Promise`\<[`TapActionRun`](/extensions/reference/frontend-api/Interface.TapActionRun)>

***

### auth

```ts theme={null}
auth: {
  organizationId: string;
  scopes: (
     | "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")[];
  token: string;
  user: {
     id: string;
     role: "admin" | "member";
  };
  can: boolean;
};
```

#### organizationId

```ts theme={null}
organizationId: string;
```

#### scopes

```ts theme={null}
scopes: (
  | "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")[];
```

#### token

```ts theme={null}
token: string;
```

The install token to send to your own server as a bearer token.

#### user

```ts theme={null}
user: {
  id: string;
  role: "admin" | "member";
};
```

##### user.id

```ts theme={null}
id: string;
```

##### user.role

```ts theme={null}
role: "admin" | "member";
```

#### can()

```ts theme={null}
can(scope): boolean;
```

Whether the install granted the scope.

##### Parameters

###### 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"`

##### Returns

`boolean`

***

### context

```ts theme={null}
context: Record<string, unknown>;
```

What Expand carried in, or the row behind a row action.

***

### data

```ts theme={null}
data: {
  query: Promise<TapResult<Q>>;
  subscribe: () => void;
};
```

#### query()

```ts theme={null}
query<Q>(query): Promise<TapResult<Q>>;
```

Rejects with `TAP_SCOPE_MISSING` when the query's scope is not granted.

##### Type Parameters

###### Q

`Q` *extends* [`TapQuery`](/extensions/reference/frontend-api/TypeAlias.TapQuery)

##### Parameters

###### query

`Q`

##### Returns

`Promise`\<[`TapResult`](/extensions/reference/frontend-api/TypeAlias.TapResult)\<`Q`>>

#### subscribe()

```ts theme={null}
subscribe<E>(event, handler): () => void;
```

Returns an unsubscribe function; call it when the component unmounts.

##### Type Parameters

###### E

`E` *extends*
\| `"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"`

##### Parameters

###### event

`E`

###### handler

(`payload`) => `void`

##### Returns

() => `void`

***

### env

```ts theme={null}
env: "dev" | "sandbox" | "production";
```

`dev` while the dev server serves the mount, else the install's channel.

***

### extension

```ts theme={null}
extension: {
  id: string;
  installId: string;
  name: string;
  projectId: string | null;
};
```

#### id

```ts theme={null}
id: string;
```

#### installId

```ts theme={null}
installId: string;
```

#### name

```ts theme={null}
name: string;
```

#### projectId

```ts theme={null}
projectId: string | null;
```

`null` when the extension is installed on the workspace, not a project.

***

### filters

```ts theme={null}
filters: TapFilters;
```

***

### format

```ts theme={null}
format: TapFormatters;
```

***

### host

```ts theme={null}
host: {
  sdkMajors: number[];
  version: string;
};
```

#### sdkMajors

```ts theme={null}
sdkMajors: number[];
```

#### version

```ts theme={null}
version: string;
```

***

### locale

```ts theme={null}
locale: string;
```

***

### nav

```ts theme={null}
nav: {
  openChat: void;
  openExternal: void;
  openSettings: void;
  push: void;
  setSearch: void;
};
```

#### openChat()

```ts theme={null}
openChat(prompt, context?): void;
```

Opens the owner's chat and fills the composer with the prompt. The owner
reads it and sends it — nothing here sends by itself. The context goes
under it as JSON on a visible `From <extension name>:` line the owner can
edit or delete, cut to 2 KB, the same ceiling every vendor block the
assistant reads is held to.

##### Parameters

###### prompt

`string`

###### context?

`Record`\<`string`, `unknown`>

##### Returns

`void`

#### openExternal()

```ts theme={null}
openExternal(url): void;
```

Asks the owner to confirm, then opens the url in a new tab.

##### Parameters

###### url

`string`

##### Returns

`void`

#### openSettings()

```ts theme={null}
openSettings(): void;
```

Opens the owner's settings page for this install.

##### Returns

`void`

#### push()

```ts theme={null}
push(path): void;
```

Refused with a toast when the path leaves the project or the extension.

##### Parameters

###### path

`` `/${string}` ``

##### Returns

`void`

#### setSearch()

```ts theme={null}
setSearch(params): void;
```

Sets the query string on the host's URL; a `null` drops that parameter.

##### Parameters

###### params

`Record`\<`string`, `string` | `null`>

##### Returns

`void`

***

### params

```ts theme={null}
params: Record<string, string>;
```

A page's route: `pageId`, `path`, and one key per sub-path segment.

***

### project

```ts theme={null}
project: {
  apps: TapApp[];
  id: string;
  keywords: TapKeyword[];
  name: string;
  platforms: TapPlatform[];
  releases: TapRelease[];
};
```

#### apps

```ts theme={null}
apps: TapApp[];
```

#### id

```ts theme={null}
id: string;
```

#### keywords

```ts theme={null}
keywords: TapKeyword[];
```

#### name

```ts theme={null}
name: string;
```

#### platforms

```ts theme={null}
platforms: TapPlatform[];
```

#### releases

```ts theme={null}
releases: TapRelease[];
```

***

### server

```ts theme={null}
server: TapProcedures;
```

***

### state

```ts theme={null}
state: {
  get: T | undefined;
  set: void;
  subscribe: () => void;
};
```

#### get()

```ts theme={null}
get<T>(key): T | undefined;
```

Returns the same reference for the same key until `set` replaces it, so
`useTapState` can use the value as a `useSyncExternalStore` snapshot.

##### Type Parameters

###### T

`T`

##### Parameters

###### key

`string`

##### Returns

`T` | `undefined`

#### set()

```ts theme={null}
set<T>(key, value): void;
```

Notifies every mount of the install that reads the same key.

##### Type Parameters

###### T

`T`

##### Parameters

###### key

`string`

###### value

`T`

##### Returns

`void`

#### subscribe()

```ts theme={null}
subscribe(key, handler): () => void;
```

Returns an unsubscribe function.

##### Parameters

###### key

`string`

###### handler

() => `void`

##### Returns

() => `void`

***

### storage

```ts theme={null}
storage: TapStorage;
```

***

### telemetry

```ts theme={null}
telemetry: {
  event: void;
};
```

#### event()

```ts theme={null}
event(name, props?): void;
```

Queued and sent by the host; the call itself does no network work.

##### Parameters

###### name

`string`

###### props?

`Record`\<`string`, `string` | `number` | `boolean`>

##### Returns

`void`

***

### theme

```ts theme={null}
theme: {
  mode: "light" | "dark";
};
```

#### mode

```ts theme={null}
mode: "light" | "dark";
```

***

### timezone

```ts theme={null}
timezone: string;
```

***

### ui

```ts theme={null}
ui: {
  size: TapSize;
  confirm: Promise<boolean>;
  copy: Promise<void>;
  download: void;
  expand: void;
  openInChat: void;
  toast: void;
};
```

#### size

```ts theme={null}
size: TapSize;
```

#### confirm()

```ts theme={null}
confirm(options): Promise<boolean>;
```

Resolves to what the owner chose in the host's confirmation dialog.

##### Parameters

###### options

[`TapConfirmOptions`](/extensions/reference/frontend-api/Interface.TapConfirmOptions)

##### Returns

`Promise`\<`boolean`>

#### copy()

```ts theme={null}
copy(text): Promise<void>;
```

Copies the text to the clipboard and toasts that it was copied.

##### Parameters

###### text

`string`

##### Returns

`Promise`\<`void`>

#### download()

```ts theme={null}
download(blob, filename): void;
```

Saves the blob to the owner's downloads under that filename.

##### Parameters

###### blob

`Blob`

###### filename

`string`

##### Returns

`void`

#### expand()

```ts theme={null}
expand(): void;
```

Opens the widget's expand panel.

##### Returns

`void`

#### openInChat()

```ts theme={null}
openInChat(card): void;
```

Puts the card in the owner's chat, drawn by the host from the values you
send. Needs `insights:write`; without it the host fills the composer with
a sentence describing the card instead, and the owner sends that.

##### Parameters

###### card

[`TapChatCard`](/extensions/reference/frontend-api/TypeAlias.TapChatCard)

##### Returns

`void`

#### toast()

```ts theme={null}
toast(message, tone?): void;
```

Raises a host toast; the tone defaults to `neutral`.

##### Parameters

###### message

`string`

###### tone?

[`TapToastTone`](/extensions/reference/frontend-api/TypeAlias.TapToastTone)

##### Returns

`void`

## Methods

### \_\_subscribe()

```ts theme={null}
__subscribe(key, listener): () => void;
```

The host replaces the member a key names with a new object and only then
calls the key's listeners; it never mutates a member in place, because the
`useTap*` hooks compare snapshots by reference. Returns an unsubscribe
function.

#### Parameters

##### key

[`TapChangeKey`](/extensions/reference/frontend-api/TypeAlias.TapChangeKey)

##### listener

() => `void`

#### Returns

() => `void`

#### Inherited from

[`TapInternals`](/extensions/reference/frontend-api/Interface.TapInternals).[`__subscribe`](/extensions/reference/frontend-api/Interface.TapInternals#__subscribe)

***

### invalidate()

```ts theme={null}
invalidate(name?): void;
```

Drops the cached answers for one procedure, or for everything of yours when
the name is left out, across every mount of the install.

#### Parameters

##### name?

`string`

#### Returns

`void`
