Modos Live y Test
Cómo funciona la separación de tráfico productivo y de desarrollo. Prefijos de keys, routing de webhooks, supresión, idempotency.
ReallyQuickEmails separa tráfico productivo y de desarrollo a través del prefijo de la API key, no de un parámetro del request. Esto evita errores típicos donde un dev olvida un flag y manda emails reales desde staging.
El modelo
| Live | Test | |
|---|---|---|
| Prefijo de key | sk_proj_* o sk_live_* | sk_test_* |
| Cuota mensual | cuenta normal | skip — no consume |
| Activity | is_test=false | is_test=true (filtrable) |
| Webhook outbound | webhook_url | webhook_url_dev |
| Webhook inbound (replies) | inbound_webhook_url | inbound_webhook_url_dev |
| Envío real vía SES | sí | sí, también (modelo Resend) |
| Rate limits SES | comparte cuota AWS | comparte cuota AWS |
| Suppression list | comparte | comparte |
Test mode SÍ envía emails reales
La elección fue deliberada: queremos que pruebes deliverability, render visual y comportamiento de los clientes de email igual que en producción. La diferencia está en contabilidad y routing, no en el envío en sí.
Cómo se decide el modo
El modo viaja en la key. No hay parámetro mode en el body, ni headers especiales, ni flag por endpoint:
curl -X POST https://api.reallyquickemails.com/v1/send-email \
-H "Authorization: Bearer sk_live_tu_secret_key" \
-H "Content-Type: application/json" \
-d '{
"recipient_email": "cliente@empresa.com",
"sender_email": "noreply@tudominio.com",
"subject": "Confirmación de pedido",
"html_body": "<p>Tu pedido fue confirmado.</p>"
}'→ req.isTestMode = false
→ activity con is_test=false
→ webhook a project.webhook_url
curl -X POST https://api.reallyquickemails.com/v1/send-email \
-H "Authorization: Bearer sk_test_tu_test_key" \
-H "Content-Type: application/json" \
-d '{
"recipient_email": "qa@empresa.com",
"sender_email": "noreply@tudominio.com",
"subject": "QA — confirmación",
"html_body": "<p>Test desde staging.</p>"
}'→ req.isTestMode = true
→ activity con is_test=true
→ webhook a project.webhook_url_dev
→ skip counter mensual
Webhook routing
Cada proyecto tiene cuatro URLs configurables (en pares outbound/inbound, una para cada modo):
Si la URL _dev está vacía
No hay fallback cross-mode
Si tu proyecto no tiene webhook_url_dev configurada y enviás con sk_test_*, los eventos quedan en DB (tabla email_events, activity con is_test=true) pero no se entregan al webhook_url de live. Es deliberado para evitar contaminar tu sistema productivo con tráfico de pruebas.
is_test en el payload
Todos los webhooks (outbound e inbound) incluyen is_test: boolean en el body. Eso te permite, si querés, recibir todo en una sola URL (webhook_url) y filtrar lado-cliente, dejando webhook_url_dev vacío:
{
"event": "email.delivered",
"is_test": true,
"data": { "...": "..." }
}Pero mantenerlas separadas es lo recomendado para evitar accidentes operativos.
Caso de uso: setup multi-environment
# .env.local
RQE_API_KEY=sk_test_tu_test_keyTu app local usa sk_test_. Los emails que mandes durante desarrollo:
- Llegan al inbox real (testeás render, deliverability)
- No cuentan contra tu cuota
- No mezclan métricas con prod
- Webhooks (si configurás
webhook_url_devapuntando a un ngrok o servicio de testing) funcionan idéntico
# Variables del ambiente staging
RQE_API_KEY=sk_test_tu_test_keyMismo sk_test_* que local. Tu staging environment manda emails reales pero contabilizados como test. Si tenés webhook listener en tu staging, configurá webhook_url_dev apuntando a esa URL.
# Variables del ambiente prod
RQE_API_KEY=sk_live_tu_live_keyProducción usa sk_live_* (o el legacy sk_proj_*). Cuota mensual cuenta. Webhooks a webhook_url (production listener).
Tu código no necesita saber el modo
La elección de modo es solo cuestión de qué env var lees. Los call sites a la API son idénticos. Esto deja el switch en infra (Render/Vercel envs) y no en código, donde es más fácil olvidar un flag.
Casos extremos
Suppression list
Es compartida entre live y test. Si un destinatario se dio de baja por un envío live, los envíos test al mismo recipient también se omiten (response 200 con skipped: true). Esto previene que tests de QA reactiven la entrega a usuarios que ya no quieren tus emails.
Rate limits SES
Live y test comparten la cuota AWS de tu cuenta SES (16/seg, 106k/día por default). Un spike de tests también afecta tu envío productivo.
Regenerar keys
Live y test se regeneran independientemente desde el dashboard. Regenerar la live no afecta la test, y viceversa. La key anterior queda invalidada al instante.
Próximos pasos
- API Keys — referencia completa con ejemplos de idempotency y dry-run.
- Webhooks — formato de payload y verificación HMAC.
- Quickstart — primer envío end-to-end con
sk_test_*.