# Doble opt-in (/es/recipes/double-opt-in)

Con el doble opt-in, una inscripción no se convierte en destinataria de marketing hasta que ha hecho clic en un enlace de confirmación. Ese clic adicional es tu defensa contra direcciones mal escritas, bandejas de entrada compartidas por rol, y envíos de personas que nunca quisieron suscribirse.

Todo depende de `{{subscribeUrl}}` — un enlace que Bitelio genera por contacto e inyecta en cualquier correo que envíes. Cuando el destinatario hace clic en él, su indicador `subscribed` pasa a `true` y Bitelio emite un evento `contact.subscribed`.

## Configuración

import {Step, Steps} from 'fumadocs-ui/components/steps';

<Steps>
  <Step>
    ### Crea dos plantillas

    * Una plantilla **Transaccional** con el mensaje de confirmación, con `{{subscribeUrl}}` dentro:

      ```html
      <p>Hi {{firstName}}, please confirm your email to start receiving updates:</p>
      <p><a href="{{subscribeUrl}}">Confirm my email</a></p>
      ```

    * Una plantilla **Marketing** para el mensaje de bienvenida que solo se envía *una vez* que han confirmado.

    <Callout title="La confirmación debe ser transaccional" type="warn">
      Si una plantilla de marketing se dirige a un contacto que no está suscrito, el envío se [omite en silencio](/es/concepts/contacts#emails-by-subscription-state). Por eso exactamente el correo de confirmación tiene que ser transaccional: los envíos transaccionales ignoran el estado de suscripción.
    </Callout>
  </Step>

  <Step>
    ### Dispara la inscripción desde tu backend

    Desde tu servidor, registra al contacto con una clave secreta (`sk_*`, `contacts:edit`) y registra después el evento con la clave pública (`pk_*`) del mismo proyecto. El registro de eventos solo acepta la clave pública. Nunca expongas la clave secreta en el navegador.

    ```bash
    curl https://api.bitelio.com/v1/contacts \
      -H "Authorization: Bearer sk_your_secret_key" \
      -H "Content-Type: application/json" \
      -d '{ "email": "ada@example.com", "subscribed": false, "data": { "firstName": "Ada" } }'

    curl https://api.bitelio.com/v1/track \
      -H "Authorization: Bearer pk_your_public_key" \
      -H "Content-Type: application/json" \
      -d '{ "event": "signup.pending", "email": "ada@example.com", "subscribed": false }'
    ```

    Ten en cuenta que `subscribed: false` aparece en ambas solicitudes. Llamar a `/v1/track` por sí solo igual crearía el contacto faltante — pero como uno suscrito, lo que anularía todo el esquema de doble opt-in. De ahí la creación explícita del contacto primero.
  </Step>

  <Step>
    ### Flujo de trabajo A: envía la confirmación

    En **Flujos de trabajo → Nuevo flujo de trabajo**:

    * **Disparador**: `EVENT` en `signup.pending`
    * Paso `SEND_EMAIL` → plantilla de confirmación transaccional

    Actívalo.
  </Step>

  <Step>
    ### Flujo de trabajo B: dales la bienvenida tras la confirmación

    En **Flujos de trabajo → Nuevo flujo de trabajo**:

    * **Disparador**: `EVENT` en `contact.subscribed`
    * Paso `SEND_EMAIL` → plantilla de bienvenida de marketing

    Actívalo. Como `contact.subscribed` se emite en cada opt-in — un clic en `{{subscribeUrl}}`, la página de preferencias, o una actualización por API — este único flujo de trabajo cubre tanto las confirmaciones nuevas como los contactos que se resuscriben más adelante.
  </Step>
</Steps>

## Recordatorio si no confirman

Añade un paso `WAIT_FOR_EVENT` al Flujo de trabajo A, justo después del envío:

* **Evento**: `contact.subscribed`
* **Tiempo de espera**: `86400` (24 horas)

Si se agota el tiempo de espera, envía un único recordatorio — de nuevo desde una plantilla transaccional. Resiste la tentación de añadir más: para los proveedores de bandeja de entrada (y para el destinatario), un flujo de correos de "por favor confirma" se lee como spam.

## Qué sigue

<Cards>
  <Card title="Páginas de baja y preferencias" href="/es/guides/unsubscribe-pages">
    Cómo funciona `{{subscribeUrl}}` y qué hacen las páginas alojadas.
  </Card>

  <Card title="Plantillas" href="/es/concepts/templates">
    Plantillas Marketing vs. Transaccional vs. Headless, y cuándo aplica cada una.
  </Card>
</Cards>
