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

# Escaneos

> Registra validaciones de pases y consulta el historial de escaneos.

# Escaneos

Un escaneo representa la validación de un pase: en la entrada de un evento, en el punto de venta o en un lector NFC. Además de quedar en el historial, los escaneos alimentan las [reglas de activación](/api/reglas-de-activacion).

***

## Registrar un escaneo

```http theme={null}
POST /v1/scans
```

Registra una lectura y devuelve el veredicto de Alara.

### Cuerpo

<ParamField body="pass_id" type="string" required>
  El identificador del pase que se escaneó.
</ParamField>

<ParamField body="reader_id" type="string" required>
  Identificador del lector o punto de escaneo. Lo recibirás de vuelta como `scanner_id`.
</ParamField>

<ParamField body="meta" type="object">
  Metadatos libres del escaneo: sucursal, ticket, monto, lo que necesites. Se devuelve como `metadata`.
</ParamField>

<ParamField body="status" type="string">
  Veredicto que tú impones, si tu sistema ya decidió. Acepta `"true"` o `"false"` (también `"1"` / `"0"`). Si lo omites, Alara evalúa el escaneo.
</ParamField>

<ParamField body="reason" type="string">
  Motivo del rechazo. Solo se registra cuando `status` indica fallo.
</ParamField>

### Parámetros de consulta

<ParamField query="pass_info" type="boolean" default="false">
  Si es `true`, la respuesta incluye el pase escaneado. Evita una segunda petición cuando necesitas mostrar datos del portador en el momento.
</ParamField>

### Respuesta

Devuelve `200 OK`.

<ResponseField name="id" type="string">Identificador del escaneo.</ResponseField>
<ResponseField name="scanner_id" type="string">El `reader_id` que enviaste.</ResponseField>
<ResponseField name="scanner_name" type="string">Nombre del lector, si está registrado en tu cuenta.</ResponseField>
<ResponseField name="pass_id" type="string">Identificador del pase.</ResponseField>
<ResponseField name="created_at" type="string">Momento del escaneo, RFC 3339.</ResponseField>
<ResponseField name="metadata" type="object">Los metadatos que enviaste en `meta`.</ResponseField>

<ResponseField name="status" type="string">
  **El veredicto**: `succeeded` o `failed`.
</ResponseField>

<ResponseField name="reason" type="string">
  Código del motivo cuando `status` es `failed`. Cadena vacía cuando fue exitoso.
</ResponseField>

<ResponseField name="pass" type="object">
  Presente solo con `?pass_info=true`.

  <Expandable title="propiedades">
    <ResponseField name="id" type="string">Identificador del pase.</ResponseField>
    <ResponseField name="visible" type="object">Campos visibles del pase.</ResponseField>
    <ResponseField name="template_id" type="string">Plantilla del pase.</ResponseField>
    <ResponseField name="status" type="string">Estado del pase.</ResponseField>
    <ResponseField name="loyalty_type" type="string">`none`, `balance` o `stamps`.</ResponseField>
    <ResponseField name="balance" type="integer | null">Saldo actual.</ResponseField>
  </Expandable>
</ResponseField>

<Warning>
  **Un escaneo rechazado también responde `200 OK`.** El veredicto vive en el campo `status` del cuerpo, no en el código HTTP. Revisa siempre `status` antes de dar acceso o entregar un beneficio.
</Warning>

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://api.alaramx.com/v1/scans?pass_info=true" \
    -H "Authorization: Bearer $ALARA_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "pass_id": "cliente-001",
      "reader_id": "sucursal-centro-01",
      "meta": {
        "ticket": "A-4471",
        "monto": 189.50
      }
    }'
  ```

  ```json Aceptado theme={null}
  {
    "id": "7c1f9a20-4b3d-4e5f-8a90-1b2c3d4e5f60",
    "scanner_id": "sucursal-centro-01",
    "scanner_name": "Sucursal Centro",
    "pass_id": "cliente-001",
    "created_at": "2026-07-24T14:41:58Z",
    "metadata": { "ticket": "A-4471", "monto": 189.50 },
    "status": "succeeded",
    "reason": "",
    "pass": {
      "id": "cliente-001",
      "visible": { "nombre": "Ada Lovelace", "puntos": "42" },
      "template_id": "lealtad_oro",
      "status": "active",
      "loyalty_type": "balance",
      "balance": 42
    }
  }
  ```

  ```json Rechazado theme={null}
  {
    "id": "9e8d7c6b-5a49-4382-9170-6f5e4d3c2b1a",
    "scanner_id": "sucursal-centro-01",
    "pass_id": "cliente-001",
    "created_at": "2026-07-24T14:52:03Z",
    "metadata": {},
    "status": "failed",
    "reason": "DOUBLE_SCAN"
  }
  ```
</CodeGroup>

### Motivos de rechazo

| `reason`         | Qué significa                                                                         |
| ---------------- | ------------------------------------------------------------------------------------- |
| `DOUBLE_SCAN`    | El pase se escaneó de nuevo dentro del periodo de espera configurado para ese lector. |
| `PASS_SUSPENDED` | El pase está desactivado o eliminado.                                                 |

***

## Listar escaneos

```http theme={null}
GET /v1/scans
```

### Parámetros

<ParamField query="limit" type="integer" default="25">Máximo `100`.</ParamField>

<ParamField query="offset" type="integer" default="0" />

<ParamField query="q" type="string">
  Busca en el identificador del escaneo, el del pase, el del lector y el nombre del lector.
</ParamField>

<ParamField query="status" type="string">
  Repetible. Valores: `processing`, `succeeded`, `failed`.
</ParamField>

<ParamField query="scanner_id" type="string">
  Repetible. Filtra por uno o varios lectores.
</ParamField>

<ParamField query="sort_by" type="string" default="created_at">
  `id`, `name` o `created_at`.
</ParamField>

<ParamField query="sort_dir" type="string" default="desc">`asc` o `desc`.</ParamField>
<ParamField query="created_from" type="string">RFC 3339, inclusivo.</ParamField>
<ParamField query="created_to" type="string">RFC 3339, exclusivo.</ParamField>

Devuelve `200 OK` con un **arreglo** de escaneos, más `X-Total-Count` y `X-Has-More`.

<Warning>
  En este listado, `created_at` viene con el formato `2026-07-24 14:41:58.842 +0000 UTC`, no en RFC 3339. Consulta [Convenciones](/api/convenciones#formatos-de-fecha-en-las-respuestas).
</Warning>

<CodeGroup>
  ```bash cURL theme={null}
  curl "https://api.alaramx.com/v1/scans?status=succeeded&scanner_id=sucursal-centro-01&created_from=2026-07-01T00:00:00Z" \
    -H "Authorization: Bearer $ALARA_API_KEY"
  ```

  ```json Respuesta theme={null}
  [
    {
      "id": "7c1f9a20-4b3d-4e5f-8a90-1b2c3d4e5f60",
      "scanner_id": "sucursal-centro-01",
      "scanner_name": "Sucursal Centro",
      "pass_id": "cliente-001",
      "created_at": "2026-07-24 14:41:58.842 +0000 UTC",
      "metadata": { "ticket": "A-4471" },
      "status": "succeeded",
      "reason": ""
    }
  ]
  ```
</CodeGroup>

***

## Consultar un escaneo

```http theme={null}
GET /v1/scans/{id}
```

<ParamField path="id" type="string" required>
  Identificador del escaneo.
</ParamField>

Devuelve `200 OK` con el escaneo, o `404 RESOURCE_NOT_FOUND`.

```bash theme={null}
curl "https://api.alaramx.com/v1/scans/7c1f9a20-4b3d-4e5f-8a90-1b2c3d4e5f60" \
  -H "Authorization: Bearer $ALARA_API_KEY"
```

***

## En tiempo real

Si prefieres reaccionar a los escaneos en lugar de consultarlos, suscríbete al evento [`scan.created`](/api/webhooks/eventos#scan-created) y Alara notificará a tu servidor cada vez que se registre uno.
