Crear codigo

Crear un codigo promocional asociado a un usuario final.

POST/api/v1/codes

Crea 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

ParametroTipoDescripcion

Headers opcionales

ParametroTipoDescripcion

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 mismo Idempotency-Key para 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 cuando discountType === "percentage". null cuando no hay tope. La integracion debe aplicar min(price * discountValue/100, maxDiscountAmount) al cobrar.
  • remainingGenerations — cuantos codigos mas puede recibir este usuario en el periodo actual. Se omite si la campana no tiene generationLimits.
  • limitPeriod — periodo del limite (day | week | month | total). Se omite si no hay limite.
  • idempotenttrue si la respuesta es replay de una generacion previa.

Errores

CodigoHTTPDescripcion
VALIDATION_ERROR400Falta campaignId o externalUserId
CAMPAIGN_NOT_FOUND404Campana no existe en el org
CAMPAIGN_INACTIVE400Campana no esta activa (status != "active")
CAMPAIGN_NOT_CODE_REWARD400La campana entrega puntos, no codigos (rewardType !== "promo_code")
CAMPAIGN_MISSING_CODE_CONFIG400La campana no tiene defaultCodeConfig configurado en el dashboard
GENERATION_LIMIT_REACHED429El usuario supero generationLimits.maxPerUser del periodo
CODE_COLLISION_RETRY_EXCEEDED500No 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:

  1. La primera request con un key nuevo crea el codigo y retorna 201.
  2. Reintentos con el mismo key y el mismo externalUserId dentro de 24h devuelven el mismo codigo con 200 y idempotent: true.
  3. Un key diferente siempre genera un codigo nuevo (y cuenta contra generationLimits).

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 externalUserId para 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.