> ## Documentation Index
> Fetch the complete documentation index at: https://docs.platform.embeddables.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Protocol instance & hooks

> ProtocolInstance methods, the useProtocol hook, its result types, and errors

Install, init, and walkthrough examples live in [Overview & setup](/sdks/protocols/overview). **This page** is the reference for the `ProtocolInstance` methods, the `useProtocol` React hook, and the values they return.

<Note>
  Protocols drives protocol navigation — which question is current, whether the visitor can
  continue, and eligibility. The question's inputs and the visitor's answers are owned by
  [Forms](/sdks/forms/overview): render and store each answer on the form field whose
  `protocolFieldId` the protocol reads, and the protocol reads it back. Connect a ready-made
  protocol with [`em protocol connect`](/get-started/em-cli#protocols).
</Note>

<h2 id="instance">
  ProtocolInstance
</h2>

`initProtocols({ core }).initProtocol({ protocol, forms })` returns a `ProtocolInstance`. Every method reads the forms' current values at call time — there is no form-data argument and no `subscribe()`.

<ResponseField name="getFirstQuestionId()" type="string">
  Id of the protocol's first medical-intake question.
</ResponseField>

<ResponseField name="getQuestionById(id)" type="ProtocolQuestion | undefined">
  The question definition for an id, or `undefined` when the id is unknown.
</ResponseField>

<ResponseField name="shouldShowQuestion(id)" type="boolean">
  Whether the question should be shown, given the forms' current values.
</ResponseField>

<ResponseField name="getContinueStatus(id)" type="ContinueStatus">
  Whether the visitor can continue past this question — see [ContinueStatus](#continue-status).
</ResponseField>

<ResponseField name="getNextQuestionId(id)" type="string | null">
  The next visible question id, or `null` at the end of the medical intake.
</ResponseField>

<ResponseField name="getPrevQuestionId(id)" type="string | null">
  The previous visible question id, or `null` at the start.
</ResponseField>

<ResponseField name="canStartMedicalIntake()" type="boolean">
  Whether the visitor has answered enough to begin the medical intake.
</ResponseField>

<ResponseField name="isEligible()" type="EligibilityResult">
  The eligibility outcome — see [EligibilityResult](#eligibility).
</ResponseField>

<Warning>
  `shouldShowQuestion`, `getContinueStatus`, and the next/prev navigation methods throw
  `UnknownQuestionError` when passed a question id that is not in the protocol — branch on its
  `.questionId` if you accept ids from outside the protocol's questions. `getQuestionById` returns
  `undefined` instead of throwing.
</Warning>

<h2 id="react">
  React
</h2>

Register Protocols on `EmbeddablesProvider` with `protocols()`, then drive navigation with `useProtocol`. Put `protocols()` **after** `forms()` in `modules` — Protocols reads the Forms client at init.

<h3 id="protocols-module">
  protocols()
</h3>

`protocols()` is the module factory for `EmbeddablesProvider`'s `modules` prop — not a hook.

```typescript theme={null}
import { EmbeddablesProvider } from '@embeddables/core/react'
import { forms } from '@embeddables/forms/react'
import { protocols } from '@embeddables/protocols/react'

<EmbeddablesProvider
  config={config}
  modules={[forms(), protocols({ protocols: [myProtocol] })]}
>
  {children}
</EmbeddablesProvider>
```

<ParamField body="protocols" type="readonly Protocol[]">
  Protocol definitions to register (from `embeddables/_dist` after `em protocol connect`).
</ParamField>

<ParamField body="formIds" type="readonly string[]">
  Optional. Form ids whose fields protocols may read via `protocolFieldId`. When omitted, every form
  registered through `forms()` is composed in.
</ParamField>

<h3 id="react-hook">
  useProtocol()
</h3>

`useProtocol({ protocolId })` from `@embeddables/protocols/react` mirrors `useForm`'s no-type-argument ergonomics. It owns the current question as hook-local state and recomputes derived values on every render after any composed form changes.

<ParamField path="protocolId" type="string" required>
  Id of a protocol registered through [`protocols()`](#protocols-module). Throws if `protocols()`
  was not registered or the id is unknown.
</ParamField>

It returns:

| Field                    | Type                            | Description                                                                    |
| ------------------------ | ------------------------------- | ------------------------------------------------------------------------------ |
| `instance`               | `ProtocolInstance`              | Direct access to every [instance method](#instance) for arbitrary question ids |
| `currentQuestionId`      | `string \| null`                | The hook's current question id                                                 |
| `question`               | `ProtocolQuestion \| undefined` | The current question definition                                                |
| `shouldShowQuestion`     | `boolean`                       | Whether the current question should be shown                                   |
| `continueStatus`         | `ContinueStatus \| null`        | Continue status for the current question (`null` until Core is ready)          |
| `canStartMedicalIntake`  | `boolean`                       | Whether the visitor can begin the intake                                       |
| `eligibility`            | `EligibilityResult \| null`     | The current eligibility outcome (`null` until Core is ready)                   |
| `goToNextQuestion()`     | `() => void`                    | Advance to the next visible question                                           |
| `goToPreviousQuestion()` | `() => void`                    | Go back to the previous visible question                                       |
| `goToQuestion(id)`       | `(id: string) => void`          | Jump to a specific question                                                    |

<Note>
  For visitor or project id in React, use Core's `useAppUserId` and `useEmbeddablesProjectId`. For
  form values and inputs, use Forms' `useForm` / `useFormField` — Protocols reads those same forms.
</Note>

<h2 id="result-types">
  Result types
</h2>

The two values you branch on while driving protocol navigation.

<h3 id="continue-status">
  ContinueStatus
</h3>

Returned by [`getContinueStatus(id)`](#instance) and the hook's `continueStatus`:

```typescript theme={null}
{ status: 'can_continue' } | { status: 'no_reply' } | { status: 'disqualified' }
```

<h3 id="eligibility">
  EligibilityResult
</h3>

Returned by [`isEligible()`](#instance) and the hook's `eligibility`:

```typescript theme={null}
{
  status: 'qualified',
  modality: 'async',
  statusReasons: [],
  eligibleProducts: ['product_a'],
}
```

<ParamField path="status" type="EligibilityStatus" required>
  `qualified`, `qualified_for_eligible_products`, `disqualified`, or `pending`.
</ParamField>

<ParamField path="modality" type="EligibilityModality">
  `sync` or `async` — present on qualified outcomes, omitted for `disqualified` / `pending`.
</ParamField>

<ParamField path="statusReasons" type="string[]" required>
  Human-readable reasons behind the status.
</ParamField>

<ParamField path="eligibleProducts" type="string[]">
  Optional product ids the visitor qualifies for.
</ParamField>

<h2 id="errors">
  Errors
</h2>

All SDK errors extend `ProtocolsError`.

| When                                                                                            | Error                                                    |
| ----------------------------------------------------------------------------------------------- | -------------------------------------------------------- |
| Invalid Core instance at init                                                                   | `ProtocolsError` (thrown)                                |
| Invalid protocol at init                                                                        | `ProtocolSchemaError` (thrown)                           |
| React init with a protocol configured but no `forms()` before `protocols()`                     | `ProtocolsError` (thrown)                                |
| Unknown protocol id                                                                             | `UnknownProtocolError` (thrown)                          |
| Unknown question id passed to `shouldShowQuestion`, `getContinueStatus`, or a navigation method | `UnknownQuestionError` (thrown; branch on `.questionId`) |
