> For the complete documentation index, see [llms.txt](https://docs.webtonative.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.webtonative.com/javascript-apis/barcode-scan.md).

# Barcode Scanner API

Scan barcodes and QR codes using the WebToNative JavaScript API. Enable fast, accurate code scanning in Android and iOS apps.

Functions to scan barcodes and QR codes from your website using the device's camera — or a photo from the gallery. The WebToNative Barcode Scan plugin renders a native scanner UI (with optional multi-scan sessions, gallery picking, and full styling control) and returns the result to your website through a JavaScript callback.

> You'll need to import the javascript file in your website before starting from this [link](https://docs.webtonative.com/javascript-apis/getting-started).

> **Platform support:** Android and iOS, kept in close feature parity. A few small differences are called out inline below and summarized in [Platform Differences](#platform-differences).

***

## Setting Up Barcode Scan

1. Go to your **WebToNative dashboard** → **Add-ons** → **Barcode Scan** and enable it.
2. Optionally configure default scanner styling (title, colors, animation, border style, etc.) on the same add-on page — every one of these can also be overridden per call from JavaScript. See [Style Options](#style-options) for the full list and the override priority order.
3. If you want **multi-scan** sessions available at all, configure the Multi-Scan section in the dashboard (confirm modal, confirm button text, count badge text). This doesn't turn multi-scan *on* by itself — see [Multi-Scan Mode](#multi-scan-mode).

{% hint style="info" %}
**Dashboard settings are defaults, not requirements.** Anything you set in the dashboard is just the fallback used when a given call doesn't override it — see the priority order in [Style Options](#style-options).
{% endhint %}

***

## JavaScript API Reference

### BarcodeScan

Opens the native barcode/QR scanner.

{% tabs %}
{% tab title="Plain Javascript" %}

```javascript
const { Format, BarcodeScan } = WTN.Barcode;

BarcodeScan({
  format: Format.QR_CODE, // optional — omit to scan all supported formats
  onBarcodeSearch: (value) => {
    console.log(value);
  },
});
```

{% endtab %}

{% tab title="npm" %}

```javascript
import { Format, BarcodeScan } from "webtonative/barcode";

BarcodeScan({
  format: Format.QR_CODE,
  onBarcodeSearch: (value) => {
    console.log(value);
  },
});
```

{% endtab %}
{% endtabs %}

**Parameters:**

| Key               | Type       | Required | Description                                                                                                                                                                                  |
| ----------------- | ---------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `format`          | `Format`   | No       | A single format to restrict scanning to. Omit (or pass no `format`) to scan all supported formats. See [Format Types](#format-types).                                                        |
| `onBarcodeSearch` | `Function` | No       | Called with just the scanned **string** value. Only fires for a single successful scan — it does not fire for multi-scan results, `SCAN_LIMIT_REACHED`, or errors. Use `callback` for those. |
| `callback`        | `Function` | No       | Called with the **full response object** for every outcome — single scan, multi-scan, scan-limit-reached, and errors. This is the one to use once you turn on multi-scan.                    |
| `multiScan`       | `Boolean`  | No       | Opt in to a multi-scan session for this call. Only takes effect if multi-scan is also configured in the dashboard — see [Multi-Scan Mode](#multi-scan-mode). Defaults to `false`.            |
| `maxCount`        | `Number`   | No       | Overrides the dashboard's max scan count for this call (single-scan sessions ignore this).                                                                                                   |
| `allowDuplicates` | `Boolean`  | No       | Overrides the dashboard's duplicate-scan setting for this call.                                                                                                                              |
| `style`           | `Object`   | No       | Per-call overrides for scanner appearance and behavior — see [Style Options](#style-options).                                                                                                |

{% hint style="info" %}
**Use `callback`, not `onBarcodeSearch`, once you turn on multi-scan.** `onBarcodeSearch(value)` only ever receives a single scanned string and is a no-op for multi-scan results, which arrive as a `scans` array instead — see [Callback Response Format](#callback-response-format).
{% endhint %}

***

## Format Types

Pass one of these to `format` to restrict scanning to a single symbology. Omit `format` entirely to scan for all of them at once.

| Constant             | Numeric code |
| -------------------- | ------------ |
| `Format.ALL_FORMATS` | `0`          |
| `Format.CODE_128`    | `1`          |
| `Format.CODE_39`     | `2`          |
| `Format.CODE_93`     | `4`          |
| `Format.CODABAR`     | `8`          |
| `Format.DATA_MATRIX` | `16`         |
| `Format.EAN_13`      | `32`         |
| `Format.EAN_8`       | `64`         |
| `Format.ITF`         | `128`        |
| `Format.QR_CODE`     | `256`        |
| `Format.UPC_A`       | `512`        |
| `Format.UPC_E`       | `1024`       |
| `Format.PDF417`      | `2048`       |
| `Format.AZTEC`       | `4096`       |

The scanned `format` in a **result** can also be one of these additional names, which aren't independently selectable via `format` but can come back when scanning all formats:

| Result-only format                                           | Notes                                                                            |
| ------------------------------------------------------------ | -------------------------------------------------------------------------------- |
| `MICRO_QR`                                                   |                                                                                  |
| `GS1_DATABAR`, `GS1_DATABAR_EXPANDED`, `GS1_DATABAR_LIMITED` |                                                                                  |
| `MICRO_PDF417`                                               | **iOS 17.4+ only** — Android has no equivalent symbology and never returns this. |
| `UNKNOWN`                                                    |                                                                                  |

***

## Multi-Scan Mode

By default, `BarcodeScan` closes the scanner immediately after one successful scan. Multi-scan keeps the scanner open, collecting codes into a session, until the user taps "Done" or the configured max count is reached.

**Turning it on requires both of these — either alone is not enough:**

1. Multi-Scan is configured in the dashboard (Add-ons → Barcode Scan → Multi-Scan section).
2. The call itself passes `multiScan: true`.

```javascript
BarcodeScan({
  multiScan: true,
  maxCount: 10,
  allowDuplicates: false,
  callback: (response) => {
    if (response.scans) {
      console.log(`Collected ${response.scans.length} codes`, response.scans);
    }
  },
});
```

* The session ends when the user taps "Done", **or** automatically once `maxCount` is reached (whichever happens first) — see the two multi-scan response shapes in [Callback Response Format](#callback-response-format).
* `maxCount` itself is always capped at 10,000 scans per session regardless of what you configure.
* `allowDuplicates: false` (the default) means the same code scanned twice in one session is only counted once.

***

## Style Options

Every field below can be set in three places, in this priority order (highest wins):

1. **This call** — pass it under `style` in `BarcodeScan({ style: { ... } })`.
2. **Dashboard default** — set once in Add-ons → Barcode Scan.
3. **Hard-coded default** — used if neither of the above is set.

```javascript
BarcodeScan({
  style: {
    title: "Scan a product",
    instructionText: "Align the code within the frame",
    scanWindowSize: "medium",
    scanAnimation: "sweep",
    borderStyle: "cornered",
    overlayDarkness: 45,
    flashlightButton: true,
    galleryButton: true,
    successVisualFeedback: true,
    visualFeedbackType: "checkmark",
    beepOnScan: true,
    vibrateOnScan: true,
    multiScanMode: { confirmModal: false, confirmButtonText: "Done", countBadgeText: "{{count}} scanned" },
  },
  callback: (response) => console.log(response),
});
```

| Key                               | Type      | Default                                                                                                                             | Description                                                                                                                           |
| --------------------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `title` / `subtitle`              | `String`  | `""`                                                                                                                                | Header text above the scan window.                                                                                                    |
| `instructionText`                 | `String`  | `""`                                                                                                                                | Guidance text near the scan window.                                                                                                   |
| `instructionPosition`             | `String`  | `"above"`                                                                                                                           | `"above"` \| `"inside"` \| `"below"`                                                                                                  |
| `footerText`                      | `String`  | `""`                                                                                                                                | Text shown at the bottom of the screen.                                                                                               |
| `discardDialog`                   | `Object`  | `{title:"Discard items?", subtitle:"All scanned results will be lost", primaryButtonText:"Go Back", secondaryButtonText:"Discard"}` | Confirmation dialog shown when the user backs out with unsaved scans.                                                                 |
| `scanWindowSize`                  | `String`  | `"medium"`                                                                                                                          | `"small"` \| `"medium"` \| `"large"`                                                                                                  |
| `scanAnimation`                   | `String`  | `"sweep"`                                                                                                                           | `"sweep"` \| `"pulse"` — identical on both platforms.                                                                                 |
| `borderStyle`                     | `String`  | `"cornered"`                                                                                                                        | `"cornered"` \| `"full"`                                                                                                              |
| `cornerRadius`                    | `Number`  | `0`                                                                                                                                 | Corner radius of the scan window border. **Android clamps this to ≥ 0; iOS does not** — a negative value on iOS passes through as-is. |
| `borderThickness`                 | `String`  | `"default"`                                                                                                                         | `"default"` \| `"thin"` \| `"bold"` (also accepted as `"thick"`)                                                                      |
| `defaultZoom`                     | `String`  | `"1"`                                                                                                                               | Initial camera zoom. **Parsing differs by platform** — see the hint below.                                                            |
| `overlayDarkness`                 | `Number`  | `45`                                                                                                                                | `0`–`100`, darkness of the area outside the scan window.                                                                              |
| `flashlightButton`                | `Boolean` | `true`                                                                                                                              | Show a flashlight toggle.                                                                                                             |
| `galleryButton`                   | `Boolean` | `true`                                                                                                                              | Show a "scan from gallery photo" button.                                                                                              |
| `successVisualFeedback`           | `Boolean` | `true`                                                                                                                              | Show a visual confirmation on successful scan.                                                                                        |
| `visualFeedbackType`              | `String`  | `"checkmark"`                                                                                                                       | `"checkmark"` \| `"flash"` `"`                                                                                                        |
| `beepOnScan`                      | `Boolean` | `true`                                                                                                                              | Play a beep on successful scan.                                                                                                       |
| `vibrateOnScan`                   | `Boolean` | `true`                                                                                                                              | Vibrate on successful scan.                                                                                                           |
| `multiScanMode.confirmModal`      | `Boolean` | `false`                                                                                                                             | Ask for confirmation before finalizing a multi-scan session.                                                                          |
| `multiScanMode.confirmButtonText` | `String`  | `"Done"`                                                                                                                            |                                                                                                                                       |
| `multiScanMode.countBadgeText`    | `String`  | `""`                                                                                                                                | Supports a `{{count}}` template token, e.g. `"{{count}} scanned"`.                                                                    |

***

## Callback Response Format

All responses are delivered to `callback` (and, for single scans only, the scanned value alone is also delivered to `onBarcodeSearch`) tagged with `"type": "BARCODE_SCAN"`.

### Single scan

```json
{ "type": "BARCODE_SCAN", "success": true, "value": "1234567890128", "format": "EAN_13" }
```

### Multi-scan — user tapped "Done"

```json
{
  "type": "BARCODE_SCAN",
  "success": true,
  "scans": [
    { "value": "1234567890128", "format": "EAN_13" },
    { "value": "9788809000000", "format": "EAN_13" }
  ]
}
```

### Multi-scan — hit the max count automatically

Same shape as above, plus a `status` field. This is **not an error** — `success` is still `true`.

```json
{
  "type": "BARCODE_SCAN",
  "success": true,
  "status": "SCAN_LIMIT_REACHED",
  "scans": [ { "value": "...", "format": "..." } ]
}
```

### Error

```json
{ "type": "BARCODE_SCAN", "success": false, "error": "CAMERA_PERMISSION_DENIED" }
```

**All possible `error` values:**

| Code                           | Platform     | Fires when                                                                                                                                                       |
| ------------------------------ | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `CAMERA_PERMISSION_DENIED`     | Both         | Camera permission denied.                                                                                                                                        |
| `CAMERA_INITIALIZATION_FAILED` | Both         | The camera couldn't be started (no back camera, or the capture session failed to initialize).                                                                    |
| `NO_BARCODE_FOUND`             | Both         | A gallery photo was picked but no recognizable barcode was found in it.                                                                                          |
| `INVALID_IMAGE`                | Both         | The picked gallery image couldn't be read/decoded. On iOS, this is also used if the photo picker itself times out or returns nothing.                            |
| `SCANNER_ERROR`                | **iOS only** | The underlying image-recognition request throws while scanning a gallery photo. Android folds this same failure into `INVALID_IMAGE` instead of a separate code. |
| `SCAN_DISCARDED`               | Both         | The user backed out of the scanner (back press, outside tap, or confirming "Discard" in the dialog) with zero scans collected.                                   |

***

## Platform Differences

* **`defaultZoom` parsing** — see the hint under [Style Options](#style-options).
* **`cornerRadius` clamping** — Android clamps to `≥ 0`; iOS allows negative values through unmodified.
* **`SCANNER_ERROR` vs `INVALID_IMAGE`** — iOS reports gallery-scan image-processing failures as `SCANNER_ERROR`; Android reports the same situation as `INVALID_IMAGE`.
* **`MICRO_PDF417` result format** — only ever returned on iOS 17.4+; Android's scanning engine has no equivalent symbology.

***

## Frequently Asked Questions

<details>

<summary>Why doesn't `onBarcodeSearch` fire for my multi-scan results?</summary>

`onBarcodeSearch` only ever receives a single scanned string, for single-scan sessions. Use `callback` instead — it receives the full response object for every outcome, including the `scans` array for multi-scan.

</details>

<details>

<summary>I set `multiScan: true` but the scanner still closes after one scan — why?</summary>

Multi-scan also requires the Multi-Scan section to be configured in your WebToNative dashboard (Add-ons → Barcode Scan). Passing `multiScan: true` from JavaScript alone is not enough — see [Multi-Scan Mode](#multi-scan-mode).

</details>

<details>

<summary>Is `SCAN_LIMIT_REACHED` an error I need to handle differently?</summary>

No — it's delivered with `success: true` and a full `scans` array, exactly like a normal "Done"-triggered multi-scan completion. Treat it the same way; the only difference is *why* the session ended.

</details>

<details>

<summary>What format types can I select with `format`, versus what can come back in a result?</summary>

`format` only accepts the fourteen selectable constants in [Format Types](#format-types). A handful of additional names — `MICRO_QR`, the `GS1_DATABAR` variants, `MICRO_PDF417` (iOS only), and `UNKNOWN` — can appear in a scan **result** but aren't individually selectable; they only show up when scanning for all formats.

</details>
