Crear codigo
Crear un codigo promocional asociado a un usuario final.
/api/v1/codesCrea un codigo promocional unico asociado a un usuario final, usando la plantilla configurada en la campana.
Este endpoint emite un unico codigo a la vez para un externalUserId especifico. Los parametros del codigo (descuento, expiracion, prefijo, largo) se toman de la configuracion defaultCodeConfig de la campana, asi el caller externo no controla los descuentos.
Reutilizacion automatica: si el mismo usuario ya tiene un codigo activo (no canjeado, no expirado, no desactivado) para esa campana, el endpoint devuelve ese codigo existente en lugar de generar uno nuevo. Evita acumular codigos sin usar y no cuenta contra generationLimits. La respuesta trae idempotent: true cuando esto ocurre. Una vez que el usuario canjea el codigo (o expira), la proxima llamada genera uno nuevo.
Request body
| Parametro | Tipo | Descripcion |
|---|---|---|
| campaignId* | string | ID de la campana. Debe tener rewardType = promo_code y defaultCodeConfig configurado (las campanas points o none no emiten codigos). |
| externalUserId* | string | ID externo del usuario que recibe el codigo. |
Headers opcionales
| Parametro | Tipo | Descripcion |
|---|---|---|
| Idempotency-Key | string | UUID para deduplicar reintentos. Si se envia el mismo key dentro de las siguientes 24h, devuelve el mismo codigo (HTTP 200 en vez de 201). |
Ejemplo
curl -X POST https://southgames.ai/api/v1/codes \
-H "Authorization: Bearer sg_live_xxx" \
-H "X-Org-Id: mi-empresa" \
-H "Idempotency-Key: 11111111-1111-1111-1111-111111111111" \
-H "Content-Type: application/json" \
-d '{"campaignId": "camp_123", "externalUserId": "user_123"}'
Respuesta
{
"code": "SGAB12CD34",
"discountType": "percentage",
"discountValue": 20,
"maxDiscountAmount": 25000,
"maxUses": 1,
"expiresAt": "2026-06-01T00:00:00.000Z",
"remainingGenerations": 2,
"limitPeriod": "month",
"idempotent": false
}
Status codes:
201 Created— codigo nuevo generado.200 OK— respuesta idempotente: ya existia un codigo con el mismoIdempotency-Keypara ese usuario en las ultimas 24h.
Campos:
code— codigo generado. Puede usarse inmediatamente en/api/v1/codes/redeem.maxDiscountAmount— tope absoluto del descuento. Solo relevante cuandodiscountType === "percentage".nullcuando no hay tope. La integracion debe aplicarmin(price * discountValue/100, maxDiscountAmount)al cobrar.remainingGenerations— cuantos codigos mas puede recibir este usuario en el periodo actual. Se omite si la campana no tienegenerationLimits.limitPeriod— periodo del limite (day|week|month|total). Se omite si no hay limite.idempotent—truesi la respuesta es replay de una generacion previa.
Errores
| Codigo | HTTP | Descripcion |
|---|---|---|
VALIDATION_ERROR | 400 | Falta campaignId o externalUserId |
CAMPAIGN_NOT_FOUND | 404 | Campana no existe en el org |
CAMPAIGN_INACTIVE | 400 | Campana no esta activa (status != "active") |
CAMPAIGN_NOT_CODE_REWARD | 400 | La campana no entrega codigos: premia en puntos (points) o no premia por partida (none). Requiere rewardType === "promo_code" |
CAMPAIGN_MISSING_CODE_CONFIG | 400 | La campana no tiene defaultCodeConfig configurado en el dashboard |
GENERATION_LIMIT_REACHED | 429 | El usuario supero generationLimits.maxPerUser del periodo |
CODE_COLLISION_RETRY_EXCEEDED | 500 | No se pudo generar un codigo unico (retry). Intenta de nuevo. |
Idempotencia
Para prevenir duplicados en reintentos de red, envia el header Idempotency-Key con un UUID v4 unico por operacion logica:
- La primera request con un key nuevo crea el codigo y retorna 201.
- Reintentos con el mismo key y el mismo
externalUserIddentro de 24h devuelven el mismo codigo con 200 yidempotent: true. - Un key diferente siempre genera un codigo nuevo (y cuenta contra
generationLimits). - Si el codigo de ese key ya no sirve (canjeado, desactivado o vencido) o pasaron mas de 24h, la request emite uno nuevo: la idempotencia nunca te devuelve un codigo muerto.
- El key se interpreta dentro de tu flujo: la misma cadena usada por otro usuario, o por otro origen de emision (por ejemplo el cumplimiento de premios de un mini-juego), no comparte codigo.
Limites por usuario
Los limites se configuran en la campana (campo generationLimits en el dashboard). Si estan activos:
- Se cuenta cuantos codigos ha recibido el
externalUserIdpara esa campana en el periodo (dia/semana/mes/total). - Si supera
maxPerUser, el endpoint retorna 429. - El contador solo cuenta generaciones exitosas (no idempotentes replays).
Webhook
Se dispara el evento code.created (solo cuando hay creacion real, no en replays idempotentes) con el payload:
{
"event": "code.created",
"timestamp": "2026-04-23T18:30:00.000Z",
"data": {
"code": "SGAB12CD34",
"campaignId": "camp_123",
"externalUserId": "user_123",
"discountType": "percentage",
"discountValue": 20,
"maxDiscountAmount": 25000,
"maxUses": 1,
"expiresAt": "2026-06-01T00:00:00.000Z"
}
}
Ver Webhooks para configurar endpoints y verificar firmas.