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

# manualTapSource

> A tap source over a value your own interface collected - a pasted link, a scanned code, a test flow. The provider supplies the value; this source validates it and shapes it like every other tap.

```ts theme={null}
manualTapSource(read: ManualTapProvider): TapSource

type ManualTapProvider = (
  options?: TapCaptureOptions,
) =>
  | string
  | TapInput
  | null
  | undefined
  | Promise<string | TapInput | null | undefined>

const MANUAL_TAP_SOURCE_PRIORITY = -10
```

The manual source runs after the platform sources: at priority `-10` it sits below the redirect (`20`) and the Web NFC scanner (`0`), so an explicit value is a last resort, taken only when nothing else could capture. It is not part of [`defaultTapSources()`](/sdks/web/reference/tap-sources/defaultTapSources); register it yourself with `client.registerTapSource(...)` or include it in the `tapSources` config option.

The provider returns whatever your interface has: a tap URL, an already identified `TapInput`, or a nullish value (or empty string) when there is nothing to submit. A string is validated through the same core parser as every other source, and an already identified tap is normalized through it too, so this source cannot emit values another source would have rejected or cased differently. An invalid URL throws the core `ChipUrlError`, so form validation can branch on it.

## Example

```ts theme={null}
import { EndstatePublicClient, ChipUrlError } from "@endstate-sdk/core";
import { manualTapSource } from "@endstate-sdk/web";

const client = new EndstatePublicClient({ publishableKey: "end_pk_live_..." });
client.registerTapSource(manualTapSource(() => pasteInput.value));

submitButton.onclick = async () => {
  try {
    const tap = await client.captureTap();
    if (tap) await client.verify(tap);
  } catch (err) {
    if (err instanceof ChipUrlError) showFieldError("That is not a tap link.");
  }
};
```

Return an already identified tap when your interface parsed one itself:

```ts theme={null}
manualTapSource(() => ({ chip_id: chipId, e, source: "manual" }));
```

## Parameters

<ParamField body="read" type="ManualTapProvider" required>
  Supplies the tap value. Called with the `TapCaptureOptions` from
  `captureTap()` (its `signal` is honored: an aborted capture throws the
  signal's reason). Return a tap URL string, a `TapInput`, or a nullish value or
  empty string for "nothing to submit". May be sync or async.
</ParamField>

## Returns

<ResponseField name="TapSource" type="object" required>
  A source named `manual` with priority `MANUAL_TAP_SOURCE_PRIORITY` (`-10`).
  Its `isAvailable()` is always `true`; `capture()` runs the provider and
  returns the validated `TapResult`, or `null` when the provider yields nothing.
</ResponseField>

<ResponseField name="MANUAL_TAP_SOURCE_PRIORITY" type="number" required>
  The source's priority, `-10`. Exported for interfaces that order or compare
  sources themselves.
</ResponseField>
