El servidor de respaldo es una segunda dirección en la que funciona exactamente la misma API. Está pensado para el caso en que una solicitud a `portal.bulkgate.com` no llega en absoluto a su destino: el host de respaldo acepta la solicitud y la reenvía por otra ruta.

## Cómo está construido el servicio

BulkGate funciona en varios servidores situados en **tres ubicaciones geográficas**. Si una de ellas cae, las restantes asumen el tráfico automáticamente: para su integración no cambia nada y `portal.bulkgate.com` sigue respondiendo. Este caso no requiere ninguna gestión por su parte. Toda la configuración se describe en [Infraestructura](https://www.bulkgate.com/es/centro-de-ayuda/informacion-general/infraestructura/).

Lo que esto no cubre es **la ruta hasta nosotros**. Una interrupción en el camino entre su servidor y el nuestro (en un operador, en un proveedor de conectividad o en cualquier punto intermedio) queda fuera de nuestro control, y la solicitud entonces no llega a nosotros en absoluto. Para eso sirve el host de respaldo.

El estado actual del servicio está disponible en [bulkgate.status.online](https://bulkgate.status.online/en/).

## Dirección

```url
https://backup1.bulkgate.com
```

El host es un sustituto exacto: las rutas, los parámetros, las credenciales y las respuestas son idénticos a los de `portal.bulkgate.com`. Lo único que cambia es el dominio:

```url
https://portal.bulkgate.com/api/2.0/advanced/transactional
https://backup1.bulkgate.com/api/2.0/advanced/transactional
```

Funciona con la misma cuenta, el mismo [token](https://www.bulkgate.com/es/documentacion/administracion-de-la-api/tokens-de-api/) y el mismo crédito. No hay nada que configurar ni que activar.

> [!CAUTION]
> **No utilice el host de respaldo como dirección principal.** Puede desconectarse por mantenimiento en cualquier momento y sin previo aviso; está pensado para llevar el tráfico solo mientras el host principal no está disponible. Mantenga `portal.bulkgate.com` en primer lugar.

## Cuándo cambiar a él

> [!IMPORTANT]
> El host de respaldo resuelve un **fallo de conexión, no una solicitud rechazada**. A una solicitud que la API ha rechazado responde exactamente igual.

Cambie cuando la solicitud **no obtenga respuesta**:

| situación | ¿cambiar? |
|:--------------------------------------------------------------|:-----------|
| Tiempo de espera agotado al conectar o al leer                | **Sí**     |
| No se pudo resolver el DNS                                    | **Sí**     |
| Conexión rechazada, red inaccesible                           | **Sí**     |
| HTTP 5xx sin cuerpo                                           | **Sí**     |
| Una respuesta con un [tipo de error](https://www.bulkgate.com/es/documentacion/administracion-de-la-api/tipos-de-error/) (4xx) | No  |
| `429 Too Many Requests` ([límites](https://www.bulkgate.com/es/documentacion/administracion-de-la-api/limites-de-peticiones-de-la-api/)) | No       |

En los dos últimos casos la API sí respondió: la solicitud llegó y se evaluó. Repetirla en el host de respaldo devuelve el mismo error y solo consume otro intento de su límite.

## Cómo incorporar el failover a su integración

Guarde las direcciones en una lista ordenada y pruébelas una tras otra. Si la conexión falla, pase a la siguiente; si la API respondió, devuelva el resultado tal cual.

```php
$hosts = ['portal.bulkgate.com', 'backup1.bulkgate.com'];

foreach ($hosts as $host)
{
    $curl = curl_init();

    curl_setopt_array($curl, [
        CURLOPT_URL => "https://$host/api/2.0/advanced/transactional",
        CURLOPT_POST => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT => 30,
        CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
        CURLOPT_POSTFIELDS => json_encode([
            'application_id' => 451,
            'application_token' => 'NKN02TWE6NfBkfl-your-secret-token-Wf05GaqLIWLW4aBj',
            'number' => ['420777777777'],
            'text' => 'Your order 4517 has been shipped.',
            // Protección contra el doble envío cuando el primer intento agotó el tiempo
            // de espera pero el mensaje se envió de todos modos — consulte Qué hay que tener en cuenta.
            'duplicates_check' => 'on',
        ], JSON_THROW_ON_ERROR),
    ]);

    $body = curl_exec($curl);
    $error = curl_error($curl);

    curl_close($curl);

    // La conexión ha fallado — probar la siguiente dirección.
    if ($body === false)
    {
        continue;
    }

    // La API ha respondido, ya sea con éxito o con un error. No se cambia más.
    print_r(json_decode($body, true, flags: JSON_THROW_ON_ERROR));

    break;
}
```

```python
# pip install requests
import requests

hosts = ['portal.bulkgate.com', 'backup1.bulkgate.com']

for host in hosts:
    try:
        response = requests.post(f'https://{host}/api/2.0/advanced/transactional', json={
            'application_id': 451,
            'application_token': 'NKN02TWE6NfBkfl-your-secret-token-Wf05GaqLIWLW4aBj',
            'number': ['420777777777'],
            'text': 'Your order 4517 has been shipped.',
            'duplicates_check': 'on',
        }, timeout=30)
    except requests.RequestException:
        continue   # la conexión ha fallado, probar la siguiente dirección

    print(response.json())
    break
```

```js
const hosts = ['portal.bulkgate.com', 'backup1.bulkgate.com'];

for (const host of hosts) {
  let response;

  try {
    response = await fetch(`https://${host}/api/2.0/advanced/transactional`, {
      method: 'POST',
      headers: {'Content-Type': 'application/json'},
      body: JSON.stringify({
        application_id: 451,
        application_token: 'NKN02TWE6NfBkfl-your-secret-token-Wf05GaqLIWLW4aBj',
        number: ['420777777777'],
        text: 'Your order 4517 has been shipped.',
        duplicates_check: 'on',
      }),
    });
  } catch {
    continue;   // la conexión ha fallado, probar la siguiente dirección
  }

  console.log(await response.json());
  break;
}
```

## Qué hay que tener en cuenta

> [!WARNING]
> Un tiempo de espera agotado no significa que el mensaje no se haya enviado. Puede que la solicitud llegara y que la respuesta se perdiera en el camino de vuelta; un reintento en el host de respaldo enviaría entonces el mensaje por segunda vez.

Por eso, en los endpoints **transaccionales** envíe `duplicates_check` activado. Un mensaje con el mismo texto al mismo número se descarta dentro de una ventana de **5 minutos**, de modo que el intento repetido no duplica nada.

> [!IMPORTANT]
> En los endpoints **promocionales**, `duplicates_check` no protege un reintento: solo elimina los duplicados dentro de un mismo lote, no entre solicitudes. Para él, una solicitud repetida es un lote nuevo y se envía completo. En los envíos masivos, compruebe primero si el primer intento se realizó antes de repetirlo.

Establezca un tiempo de espera razonable. Sin él, el cliente espera una respuesta que nunca llega y ni siquiera llega a pasar al host de respaldo.

> [!NOTE]
> La **idempotencia de la API** está en desarrollo: será posible marcar una solicitud repetida para que la API la reconozca y devuelva el resultado de la original en lugar de enviar de nuevo. Esto eliminará la diferencia entre los endpoints transaccionales y promocionales descrita arriba. Documentaremos el procedimiento aquí en cuanto esté disponible.
