> For the complete documentation index, see [llms.txt](https://docs.thewalletcrew.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.thewalletcrew.io/developers-guides/integration-guides/scan-api.md).

# Scan API

## Scan API

The Scan API records a barcode or QR scan, then correlates it to a pass. This creates a reliable “in-store / at-venue” signal that downstream systems can use for redemption flows, attendance tracking, or CRM automation.

<details>

<summary><strong>Real-world examples</strong></summary>

* **Retail loyalty**: record a scan at checkout to trigger post-visit automation.
* **Event entry**: record gate scans to track attendance and prevent reuse.
* **Voucher redemption**: record scans to mark an offer as consumed.

</details>

### When Scan API is needed

Barcode/QR hardware can read the value displayed on a pass. The Scan API is the piece that makes that scan actionable in The Wallet Crew.

When the Scan API is invoked, The Wallet Crew can:

* identify which pass was scanned
* record the scan event with timestamp and symbology
* emit downstream events for connected systems

{% hint style="info" %}
The Scan API does not replace operational validation logic. It provides a scan record and correlation. Redemption rules, anti-fraud, and privilege consumption depend on the chosen workflow.
{% endhint %}

### Endpoint

The Scan API is available through this tenant-scoped operation:

## Record a pass barcode scan event

> Records scan events when pass barcodes/QR codes are scanned. Scans are correlated to passes\
> and generate scan completion events for downstream systems (redemption, attendance tracking, etc.).\
> \
> \## Authorization\
> Requires PassScan.Scan scope

```json
{"openapi":"3.1.1","info":{"title":"Neostore internal API","version":"v1"},"tags":[{"name":"Scan"}],"servers":[{"url":"https://app.neostore.cloud","description":"Production Server"},{"url":"https://app-qa.neostore.cloud","description":"Staging Server"}],"security":[{"admin-bearer":["ScopedAuthorizeRequirement"]},{"apiKey":["ScopedAuthorizeRequirement"]}],"components":{"securitySchemes":{"admin-bearer":{"type":"oauth2","flows":{"implicit":{"authorizationUrl":"https://auth.neostore.cloud/authorize?audience=https://app.neostore.cloud/api/","scopes":{}}}},"apiKey":{"type":"apiKey","name":"X-API-KEY","in":"header"}},"schemas":{"AddScanRequest":{"required":["data","type"],"type":"object","properties":{"data":{"maxLength":2048,"minLength":3,"type":"string","description":"Raw Data of the scan value"},"type":{"description":"Type of data","$ref":"#/components/schemas/TypeEnum"}},"additionalProperties":false},"TypeEnum":{"enum":["manual","text","qrcode","pdf417","aztec","code128","code39","codabar","ean8","ean13","itf14","upca","datamatrix","nfc"],"type":"string"},"AddScanResponse":{"required":["passId"],"type":"object","properties":{"passId":{"type":"string","description":"Identifier of the pass"}},"additionalProperties":false},"ProblemDetails":{"type":"object","properties":{"type":{"type":["null","string"]},"title":{"type":["null","string"]},"status":{"type":["null","integer"],"format":"int32"},"detail":{"type":["null","string"]},"instance":{"type":["null","string"]}},"additionalProperties":{}},"HttpValidationProblemDetails":{"type":"object","allOf":[{"$ref":"#/components/schemas/ProblemDetails"}],"properties":{"errors":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}},"additionalProperties":{}}}},"paths":{"/api/{tenantId}/scans":{"post":{"tags":["Scan"],"summary":"Record a pass barcode scan event","description":"Records scan events when pass barcodes/QR codes are scanned. Scans are correlated to passes\nand generate scan completion events for downstream systems (redemption, attendance tracking, etc.).\n\n## Authorization\nRequires PassScan.Scan scope","parameters":[{"name":"tenantId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"description":"Scan payload containing raw barcode data and format type","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddScanRequest"}},"text/json":{"schema":{"$ref":"#/components/schemas/AddScanRequest"}},"application/*+json":{"schema":{"$ref":"#/components/schemas/AddScanRequest"}}}},"responses":{"201":{"description":"Scan successfully recorded; pass identifier returned","content":{"text/plain":{"schema":{"$ref":"#/components/schemas/AddScanResponse"}},"application/json":{"schema":{"$ref":"#/components/schemas/AddScanResponse"}},"text/json":{"schema":{"$ref":"#/components/schemas/AddScanResponse"}}}},"400":{"description":"Invalid scan request (data too short/long, missing type)","content":{"text/plain":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"text/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}},"401":{"description":"Not authorized or invalid credentials","content":{"text/plain":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"text/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}},"404":{"description":"No pass found with matching scan data","content":{"text/plain":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"text/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}},"500":{"description":"Processing error during scan event generation"}}}}}}
```

### Authentication and authorization

The Scan API requires an identity allowed to record scans.

Common patterns are:

* **API key** authentication (`X-API-KEY`) for server-to-server calls from a POS backend.
* **OAuth 2.0 Bearer token** for admin/user identities.

The effective authorization model depends on tenant configuration. In the OpenAPI definition, this endpoint requires the `PassScan.Scan` scope.

### Request fields

The operation accepts two fields:

* `data`: the raw decoded value read by the scanner.
* `type`: the scan input type (symbology).

#### `type` values

`type` is an enum. Common values are:

* `qrcode`
* `pdf417`
* `code128`
* `ean13`
* `datamatrix`
* `nfc`
* `manual` (when staff types a code)

{% hint style="warning" %}
`data` is treated as an opaque identifier. Keeping it stable matters more than keeping it human-readable. Formatting changes (trim, leading zeros, separators) are common causes of correlation failures.
{% endhint %}

### Response

The operation returns the correlated pass identifier. It returns `404` when correlation fails.

### Implementation notes

A scanning system can call the Scan API either directly (when network access is available) or via a backend relay.

Calling from a backend relay is common. It keeps secrets out of scanning devices and allows enrichment, logging, and retry policies.

### Validate the integration

A minimal validation sequence is:

1. Use a real pass issued by The Wallet Crew.
2. Scan its barcode/QR and capture the decoded value.
3. Use the Scan API operation with `data` and `type`.
4. Confirm a `201` response and a non-null `passId`.
5. Confirm the downstream signal is received (for example, `wallet_scanned` in Bloomreach).

### Troubleshooting

#### `404 Not Found`

The scanned value did not match any pass.

Common causes are barcode formatting differences, missing prefixes/suffixes, or a mismatch between template barcode configuration and scanner decoding.

#### `400 Bad Request`

The payload failed validation.

Common causes are missing fields, `data` length outside limits, or an invalid `type`.

#### `401 Unauthorized`

The credentials are missing, invalid, or do not have the required scope.

### FAQ

<details>

<summary><strong>Does every barcode scan trigger a CRM event?</strong></summary>

Only scans recorded through the Scan API are known to The Wallet Crew as scan events.

When recorded, the scan can be forwarded to connected systems such as Bloomreach as a `wallet_scanned` event.

</details>

<details>

<summary><strong>Should the Scan API be called from the scanning device or from a backend?</strong></summary>

A backend relay is the most common setup because it keeps API keys server-side and allows retries.

Direct calls from devices can work, but it increases secret handling and device management constraints.

</details>

<details>

<summary><strong>What should be stored in <code>data</code>?</strong></summary>

The raw decoded value that is configured in the pass barcode/QR.

Keeping that value stable across systems is the key requirement.

</details>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.thewalletcrew.io/developers-guides/integration-guides/scan-api.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
