Recompensas y entrega
Cómo llega a tu sistema lo que el usuario ganó, la rama de espera, y el reclamo explícito
La recompensa se configura en el Control Center. Tu sistema no decide qué se entrega ni cuándo — solo lo recibe y lo muestra.
Cuándo se paga
Una recompensa se entrega al completarse la campaña, nunca al completarse una misión suelta. Vale para las cuatro mecánicas por igual: una campaña de tipo hito con tres misiones paga una vez, cuando las tres están cumplidas — no tres veces.
| Experiencia | Qué tiene que completarse |
|---|---|
| Hito | Todas las misiones de la campaña |
| Reto | La cadena completa |
| Racha | La meta de ventanas seguidas |
| Rutina | El último ciclo |
Los tres caminos por los que llega
Según cómo integres, la recompensa aparece en un lugar distinto:
| Camino | Cuándo | Qué obtenés |
|---|---|---|
| En la respuesta del evento | Emitís por el camino sincrónico y la entrega se resolvió en el momento | El detalle en el mismo request que la disparó |
| Por webhook | Emitís por el camino asincrónico | Un aviso a tu endpoint. Ver Recibir notificaciones |
| Consultando | Siempre disponible | GET /v1/gamification/missions/my-rewards/{accountId}. Ver Mis recompensas |
Los tres pueden convivir. El patrón robusto es usar el primero o el segundo para reaccionar rápido, y el tercero para reconciliar — porque es el único que no depende de que una entrega puntual haya llegado.
La rama de espera
La misión puede completarse sin que la recompensa esté disponible todavía. Si el stock se agotó justo en ese momento, la misión queda completada y la recompensa se entrega después.
El feed lo resuelve por vos: el ítem trae rewardStatus: PENDING_STOCK. Mostrale al usuario "misión cumplida, tu recompensa llega en breve". Cuando la entrega se concreta, el mismo ítem pasa a DELIVERED. Nunca digas "no ganaste".
En la respuesta del evento lo ves como una misión COMPLETED con rewards vacío. En my-rewards aparece solo si la entrega llegó a registrarse: si el stock ya estaba en cero al completarse, no hay entrada todavía y el rewardStatus del feed es la única fuente de ese estado.
Es una rama real y hay que diseñarla: si la pantalla solo contempla "ganaste" y "no ganaste", este caso se muestra como una pérdida que en realidad no ocurrió.
Mis recompensas
GET /v1/gamification/missions/my-rewards/{accountId} es el historial de lo que el usuario ganó. Devuelve una entrada por entrega: la misma recompensa pagada por dos campañas son dos entradas, cada una con sus códigos, y una recompensa que se entrega en varias partes también son varias entradas.
Además de la recompensa, cada entrada trae:
| Campo | Qué es |
|---|---|
redemptionId | La entrega. Sirve para pedir el código a demanda. |
campaignId | La campaña en la que la ganó. Viene en null en entregas antiguas. |
status | DELIVERED, o PENDING_STOCK mientras la entrega no se concretó. |
deliveredAt | Cuándo se entregó (UTC). Viene en null mientras está en PENDING_STOCK. |
rewardType | PRODUCTS, FRAGMENTS, POINTS o CHEST. En el feed el mismo dato se llama reward.type. |
amount | Cuánto se entregó: unidades del producto, o puntos. |
product | El producto entregado, en PRODUCTS y FRAGMENTS. |
codes | Los códigos de esta entrega, como { code, item }. Solo en PRODUCTS. |
statuses por entrega, no por campaña.PENDING_STOCKcubre tanto la entrega que espera stock como la que no se pudo cumplir: pasado tu SLA de reposición no se resuelve sola, es una alerta operativa. ElrewardStatusdel feed, en cambio, es el estado agregado de la campaña, así que durante una entrega parcial el feed puede decirPENDING_STOCKmientrasmy-rewardsya lista una entradaDELIVERED.- Para filtrar, usá el parámetro
filtersestándar sobrecampaignId,rewardTypeostatus. Ver Paginación y filtros. Sin paginación: siempre devuelve un array. Para un rango de fechas, filtrá pordeliveredAten tu app. - Los códigos vienen incluidos por defecto, y solo en recompensas
PRODUCTS. Si tu pantalla los muestra recién cuando el usuario toca la recompensa, pedí la lista conincludeCodes=falsey traé los códigos de esa entrega conGET /redemptions/{redemptionId}/code, que devuelve{ "codes": ["ABCD-1234", "EFGH-5678"] }— strings sueltos, sin elitemque sí traen loscodesdemy-rewards.
Reclamo explícito de la recompensa
Solo si tu UX incluye un botón de reclamo. Si no lo incluye, la recompensa se entrega sola y este caso no existe en tu app. Habilitá el botón solo cuando el ítem del feed trae rewardStatus: CLAIMABLE.
POST /v1/campaigns/{campaignId}/completion-bonus/claim/account/{accountId}El campaignId viene en cada ítem del feed. El claim es idempotente: varias llamadas resuelven una única entrega.
| Respuesta | Qué significa | Qué mostrar |
|---|---|---|
200 | Recompensa entregada. | El código, o el saldo de puntos acreditado. |
200 en un reclamo repetido | Ya lo había reclamado. | La misma recompensa, nunca un error de "ya reclamado". |
422 | Todavía no completó la mecánica. | Refrescá el feed y mostrá cuánto le falta. Revisá por qué el botón estaba habilitado. |
409 | La recompensa se agotó. | "Te avisamos cuando esté disponible". Alerta operativa para reponer stock. |
400 · 404 | La campaña no tiene recompensa configurada, o el id no existe. | Nada al usuario: ocultá el botón. Es un problema de configuración. |
Lo que ya se entregó no se toca
Ni cuando la campaña se pausa, ni cuando termina, ni cuando el usuario pierde una racha. El historial de recompensas del usuario se mantiene intacto.
Qué sigue
Recibir notificaciones — si vas por el camino asincrónico.
