# Desde un servidor

> Crea envíos desde un backend, un script o una integración con una clave de API secreta.

Los backends, los cron jobs y las integraciones pueden enviar al mismo endpoint que los navegadores. Añade una clave de API secreta para que Ikiform sepa que el envío es tuyo.

## Crea una clave secreta

1. En el panel, abre **API keys** y crea una clave **secret**.
2. Dale el permiso `submissions:write`. Añade `submissions:read` y `forms:read` solo si la misma clave también lee datos de la [API REST](https://ikiform.com/es/docs/api.md).
3. Copia la clave. Empieza con `ik_sk_` y se muestra una sola vez; Ikiform solo guarda su hash.

## Crea un envío

**Node** (`submit.ts`)

```ts
const response = await fetch("https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: `Bearer ${process.env.IKIFORM_SECRET_KEY}`,
    "Idempotency-Key": order.id,
  },
  body: JSON.stringify({ email: order.email, plan: order.plan }),
});

const result = await response.json();
if (!result.ok) throw new Error(`${result.error.code}: ${result.error.message} (${result.request_id})`);
```

**curl** (`Terminal`)

```bash
curl https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx \
  -H "Authorization: Bearer $IKIFORM_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order_1234" \
  -d '{"email":"ada@example.com","plan":"team"}'
```

**Python** (`submit.py`)

```python
import os

import requests

response = requests.post(
    "https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx",
    json={"email": "ada@example.com", "plan": "team"},
    headers={
        "Authorization": f"Bearer {os.environ['IKIFORM_SECRET_KEY']}",
        "Idempotency-Key": "order_1234",
    },
    timeout=10,
)
result = response.json()
if not result["ok"]:
    raise RuntimeError(f"{result['error']['code']}: {result['error']['message']}")
```

## Qué cambia una clave secreta

| Comprobación | Sin clave | Con una clave secreta |
| --- | --- | --- |
| Dominios permitidos | Se comparan con `Origin` o `Referer` | Se omite |
| Verificación humana de Ikiform | Obligatoria cuando está activa | Se omite |
| Tus propias claves de Turnstile | Token obligatorio | El token sigue siendo obligatorio |
| Límites de frecuencia | Por IP de visitante y formulario | Se siguen aplicando, por la IP de tu servidor |
| Validación de campos y puntuación de spam | Se ejecutan | Se ejecutan |

Una clave de otro espacio de trabajo, una clave revocada o una clave secreta sin `submissions:write` recibe `403 forbidden`. Una cabecera `Authorization` mal formada también se rechaza en lugar de ignorarse, así que una errata nunca te convierte en silencio en un envío anónimo.

> Todos los envíos que salen de tu servidor vienen de la dirección IP de tu servidor, así que los límites de frecuencia por visitante de Ikiform cuentan a todos los visitantes juntos. El preset Standard permite 5 envíos por minuto por IP y formulario, algo que un sitio con mucho tráfico puede alcanzar. Envía desde el navegador cuando puedas, o elige el [límite de frecuencia](https://ikiform.com/es/docs/rate-limits.md) Relaxed o Custom para los formularios que pasan por un servidor.

## Claves publicables

Las claves publicables (`ik_pub_`) son para navegadores. Solo pueden enviar, y solo desde los dominios que indiques en la clave. Un envío con una clave publicable desde cualquier otro sitio recibe `403 origin_not_allowed`. No se saltan la verificación humana ni los dominios permitidos del propio formulario.

> Nunca pongas una clave secreta en código del navegador, en una app móvil ni en un repositorio público. Revócala en el panel si se filtra.

## Reintentos

Los servidores reintentan. Envía una `Idempotency-Key` que sea estable para lo que estás enviando, como un id de pedido, y un reintento repetirá la primera respuesta en lugar de crear un duplicado. Consulta [Idempotencia](https://ikiform.com/es/docs/frameworks.md#idempotency).
