Saltar al contenido principal

API de gestión

Además de consultar socios y registrar movimientos (API pública v1), /api/v1 permite configurar el programa sin entrar en el panel: programas y niveles, campañas, catálogo de recompensas, lotes de cupones y resultados de sorteos.

En los ejemplos, $BASE es la dirección de tu plataforma y $KEY tu clave API (o un token OAuth2).

Autenticación y ámbitos​

Cada llamada lleva Authorization: Bearer mk_.... La clave pertenece a una cuenta de cliente y todo se acota a esa cuenta: un recurso de otra cuenta responde 404, nunca 403.

Estos ámbitos son explícitos: una clave sin ámbitos (las antiguas, con acceso completo) no los recibe, y hay que concederlos a la clave expresamente.

El selector de ámbitos del panel no los ofrece

El selector Ámbitos de Administración › Claves API muestra otra lista (documents:read, credits:write, webhooks:write…) y no permite escribir valores libres. Para conceder los ámbitos de esta página, usa la API de administración con una sesión de administrador que tenga el permiso Claves API: POST /api/services/app/ApiKey/UpdateApiKey (o CreateApiKey al crear una nueva) con el campo scopes como texto separado por comas, por ejemplo "read,programs:read,programs:write". UpdateApiKey recibe la clave completa (id, name, isActive, límites…), no solo scopes.

ÁmbitoPermite
programs:readLeer programas y niveles
programs:writeCrear, modificar y borrar programas y niveles
campaigns:readLeer campañas, sus campos personalizados y sus reglas de participación
campaigns:writeCrear, modificar y borrar campañas; activate, pause; campos y reglas
rewards:readLeer el catálogo de recompensas
rewards:writeCrear, reemplazar y borrar recompensas
coupons:readListar cupones y sus códigos
coupons:manageEmitir lotes e invalidar cupones
raffles:readLeer sorteos realizados y sus ganadores
readLeer el esquema de atributos de socio (además de lo de la API pública)

Los ámbitos se comparan tal cual: campaigns:write no incluye campaigns:read, y coupons:write (canje en caja) no permite emitir cupones. Pide a la clave solo lo que necesita.

Cada bloque exige además que la función esté incluida en el plan de la cuenta (programa de puntos, niveles, campañas, recompensas, cupones; y Generador de cupones en bloque para emitir lotes). Si no lo está, la respuesta es 403 con code: feature_disabled.

Convenciones comunes​

Paginación. Los listados devuelven { "items": [...], "nextCursor": "..." }. Envía nextCursor como cursor para pedir la página siguiente; es null en la última. limit va de 1 a 200 (50 por defecto; sorteos: 1 a 100, 20 por defecto). Un cursor manipulado da 400 con code: invalid_cursor.

Errores. El cuerpo trae siempre un campo code estable; programa tu lógica contra code, no contra el texto. Los códigos no se renombran, pero pueden añadirse nuevos. Los límites por hora y el 429 son los de la clave.

Idempotencia. POST, PATCH y DELETE aceptan Idempotency-Key (ver API pública). Es obligatoria al emitir lotes de cupones (400 con code: idempotency_key_required si falta).

Enumerados. Programas, niveles y campañas usan texto en minúsculas con guion bajo (points, instant_win). Recompensas y cupones usan el nombre tal cual (Physical, PercentageDiscount). Trata cualquier valor desconocido con tolerancia: pueden añadirse.

Cambios parciales (PATCH). Solo cambian las propiedades que envías. Un null explícito borra los campos que admiten vacío; en el resto da 400 validation_failed.

Programas​

Una cuenta tiene como máximo un programa activo. Función del plan: programa de puntos.

Método y rutaÁmbitoQué hace
GET /api/v1/programs?status=&cursor=&limit=programs:readLista (por id)
GET /api/v1/programs/{id}programs:readUn programa
POST /api/v1/programsprograms:writeCrea (201)
PATCH /api/v1/programs/{id}programs:writeModificación parcial
DELETE /api/v1/programs/{id}programs:writeBorra (204)

Valores de type: points, cashback, stamps, product_cashback. Valores de status: draft, active, paused, archived. Campos principales: name, description, pointsCurrencyName, pointsToMoneyRate (valor en dinero de un punto, de 0 a 1), pointsExpirationDays, pendingPeriodDays, qualifyingPointsWindowMonths, tierGracePeriodDays, cashbackPercentage y modules (points, cashback, stamps, evidence, games, giftCards, courses, challenges, cada uno true/false).

curl -s -X POST "$BASE/api/v1/programs" \
-H "Authorization: Bearer $KEY" -H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"name":"Programa de puntos","type":"points","pointsCurrencyName":"puntos","pointsToMoneyRate":0.01}'

Las validaciones son las mismas que en el panel (por ejemplo, el periodo pendiente debe ser menor que la caducidad de puntos). Errores propios:

CódigocodeCuándo
409program_already_existsLa cuenta ya tiene un programa
409program_in_useAl borrar: aún tiene niveles o socios
404program_not_foundNo existe en tu cuenta
422quota_exceededSupera el límite de programas de tu plan

Niveles​

Función del plan: niveles.

Método y rutaÁmbito
GET /api/v1/tiers?programId=&cursor=&limit=programs:read
GET /api/v1/tiers/{id}programs:read
POST /api/v1/tiersprograms:write
PATCH /api/v1/tiers/{id}programs:write
DELETE /api/v1/tiers/{id}programs:write

Para crear, programId y name son obligatorios. Campos: level, criteriaType (points_accumulated, total_spent, transaction_count), minPoints, maintenancePoints, pointsMultiplier, color, iconUrl, isDefault. Un nivel no puede cambiar de programa.

curl -s -X POST "$BASE/api/v1/tiers" \
-H "Authorization: Bearer $KEY" -H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"programId":7,"name":"Oro","level":3,"minPoints":1000,"maintenancePoints":800,"pointsMultiplier":1.5}'

Borrar un nivel que aún tienen socios da 409 con code: tier_in_use.

Campañas​

Gestionas la carcasa de la campaña. La configuración propia de cada tipo (juegos, premios, momentos ganadores, constructor de landing) sigue en el panel. Función del plan: campañas.

Método y rutaÁmbitoNotas
GET /api/v1/campaigns?status=&type=campaigns:readLas más recientes primero
GET /api/v1/campaigns/{id}campaigns:read
POST /api/v1/campaignscampaigns:write201; siempre nace en draft
PATCH /api/v1/campaigns/{id}campaigns:writetype y loyaltyProgramId no cambian
DELETE /api/v1/campaigns/{id}campaigns:write204; solo borradores
POST /api/v1/campaigns/{id}/activatecampaigns:writePasa a active (o scheduled si aún no ha empezado)
POST /api/v1/campaigns/{id}/pausecampaigns:writeDesde active o scheduled
GET / POST /api/v1/campaigns/{id}/custom-fieldsread / writeDatos que pide la participación
PUT / DELETE /api/v1/campaigns/{id}/custom-fields/{fieldId}campaigns:write
GET / PUT /api/v1/campaigns/{id}/participation-rulesread / writePolítica de participación (policyJson, con versión)

type: game, points_multiplier, bonus_points, challenge, cashback, instant_win, pin_code, ugc_contest, sports_prediction, raffle. status: draft, scheduled, active, paused, completed, cancelled, expired_deserted.

Para crear: name (hasta 256 caracteres), type, startDate y endDate (la fin, posterior al inicio). Opcionales: slug, loyaltyProgramId, brandId, imageUrl, textos de landing, pointsMultiplier (0,01 a 100), bonusPoints, cashbackPercentage (0 a 100), maxTotalEntries, maxPrizesPerParticipant, minAgeYears y budget. En PATCH, un texto vacío borra el texto y 0 borra un límite o importe.

curl -s -X POST "$BASE/api/v1/campaigns" \
-H "Authorization: Bearer $KEY" -H "Idempotency-Key: crear-verano-2027" \
-H "Content-Type: application/json" \
-d '{"name":"Bonus de verano","type":"bonus_points","startDate":"2027-06-01","endDate":"2027-08-31","bonusPoints":100}'

curl -s -X POST "$BASE/api/v1/campaigns/17/activate" -H "Authorization: Bearer $KEY"
CódigocodeCuándo
400invalid_requestDatos fuera de rango o faltan obligatorios
403feature_disabledCampañas, o cashback si el tipo/porcentaje lo exige, no incluidos en el plan
404campaign_not_foundNo existe en tu cuenta
409campaign_not_draftBorrar una campaña que no es borrador (pausa en su lugar)
409invalid_status_transitionActivar o pausar desde un estado que no lo permite
409budget_pausedPausada por presupuesto: súbelo y reactívala desde el panel
409end_date_frozenCambiar la fecha de fin de un sorteo ya realizado, o de una campaña cerrada
409slug_takenEl slug ya está en uso
422cannot_go_liveactivate rechazado (ventana terminada, reglas inválidas, instant-win sin momentos); el mensaje dice por qué
422loyalty_program_not_found, brand_not_foundLa referencia no es de tu cuenta
422quota_exceededSupera el límite de campañas de tu plan

Recompensas​

Catálogo canjeable por puntos. Función del plan: recompensas.

Método y rutaÁmbito
GET /api/v1/rewards?programId=&campaignId=&status=&category=rewards:read
GET /api/v1/rewards/{id}rewards:read
POST /api/v1/rewardsrewards:write
PUT /api/v1/rewards/{id}rewards:write
DELETE /api/v1/rewards/{id}rewards:write

rewardType: Physical, Digital, Experience, Discount, Cashback. fulfillmentType: Digital, Ship, InStore, External. status al escribir: Available o Disabled; OutOfStock lo calcula la plataforma según el stock.

curl -s -X POST "$BASE/api/v1/rewards" \
-H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{"programId":5,"name":"Café gratis","rewardType":"Physical","pointsCost":200,"stock":500,"fulfillmentType":"InStore"}'
  • programId, name y rewardType son obligatorios. imageUrl debe ser una URL http(s) absoluta.
  • PUT reemplaza los campos: lo que omitas toma su valor por defecto.
  • stock solo se lee al crear; después se mueve con los canjes y con el ajuste de stock del panel.
  • La entrega por tarjeta regalo y los ajustes mixtos de puntos más dinero solo se editan en el panel.
  • DELETE es un borrado lógico (204): los canjes pasados conservan la referencia.
  • Errores: reward_not_found, program_not_found, campaign_not_found (404), feature_disabled (403) y validation_failed (400).

Cupones​

Para canjear en caja usa los endpoints de la API pública. Aquí se gestionan los cupones que existen.

Método y rutaÁmbito
GET /api/v1/coupons?programId=&campaignId=&batchId=&status=&memberId=&code=coupons:read
GET /api/v1/coupons/{id}coupons:read
POST /api/v1/coupons/batchescoupons:manage
POST /api/v1/coupons/{id}/invalidatecoupons:manage
POST /api/v1/coupons/batches/{batchId}/invalidatecoupons:manage

Estados: Active, Used, Expired, Cancelled. Tipos (couponType): FixedDiscount, PercentageDiscount, FreeProduct, FreeShipping, Custom.

Emitir un lote​

curl -s -X POST "$BASE/api/v1/coupons/batches" \
-H "Authorization: Bearer $KEY" -H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"programId":5,"count":500,"couponType":"PercentageDiscount","discountValue":10,"prefix":"VERANO","codeLength":10,"maxUses":1,"validUntil":"2027-09-30"}'
# 201 {"batchId":"3F9A1C7E0B42","issued":500,"coupons":[{"id":812,"code":"VERANO-K7Q2M9XH4T"}, ...]}
  • Hasta 1 000 cupones por lote. Los códigos son aleatorios y únicos.
  • codeFormat: Alphanumeric (por defecto), Numeric o Alpha. codeLength de 6 a 20 (10 por defecto). Si es demasiado corto para count, se rechaza para que los códigos no sean fáciles de adivinar. prefix: hasta 10 letras o dígitos.
  • maxUses vacío = usos ilimitados; 1 para códigos de un solo uso. maxUsesPerMember, pointsCost, minTierLevel, validFrom y validUntil son opcionales.
  • Los cupones de un lote no salen en el portal del socio salvo que envíes isListed: true.
  • Un reintento con la misma Idempotency-Key y el mismo cuerpo devuelve el mismo lote.
  • Errores: 409 code_space_exhausted (no hay códigos libres suficientes: alarga codeLength o cambia el prefijo), 409 conflict (choque con otra petición simultánea: reintenta), 404 program_not_found / campaign_not_found, 403 feature_disabled.

Invalidar​

Un cupón Active pasa a Cancelled y ya no se puede canjear. Repetir la llamada no hace daño (invalidated: 0). Un cupón ya Used o Expired da 409 con code: coupon_not_active. La invalidación de un lote responde { "invalidated": N }; un lote desconocido da 404 con code: batch_not_found.

Sorteos​

GET /api/v1/campaigns/{id}/raffles (ámbito raffles:read) devuelve los sorteos ya realizados de la campaña, del más antiguo al más reciente, cada uno con sus ganadores y suplentes. No depende de que la página pública de ganadores esté activada.

curl -s "$BASE/api/v1/campaigns/17/raffles?limit=20" -H "Authorization: Bearer $KEY"

Cada sorteo incluye drawId, drawnAt, scope, storeId, slotKey, los recuentos pedidos y sorteados, eligiblePoolSize y las huellas de auditoría del acta (poolHashSha256, outputHashSha256, seedSha256; nunca la semilla). Cada ganador trae role, position, status, prizeId, notified y solo tus propias referencias (externalParticipantRef, memberExternalId, claimId) más el nombre y el correo enmascarados (maskedName, maskedEmail). No hay datos de contacto. Una campaña sin sorteos devuelve items vacío.

Errores: 404 campaign_not_found, 403 feature_disabled (campañas), 400 invalid_cursor / invalid_request.

Esquema de atributos de socio​

GET /api/v1/member-attributes (ámbito read) devuelve los atributos personalizados que tu cuenta ha definido para los socios, ordenados por displayOrder, para que sepas qué claves y tipos admite el objeto attributes de un socio.

curl -s "$BASE/api/v1/member-attributes" -H "Authorization: Bearer $KEY"

Cada elemento trae key, displayName, description, dataType, isRequired, allowedValues, displayOrder, isActive y creationTime. dataType es numérico: 1 texto, 2 número, 3 booleano, 4 fecha (yyyy-MM-dd) y 5 selección (el valor debe estar en allowedValues). Solo se devuelven los atributos activos. Las definiciones se gestionan en el panel, no por esta API; key y dataType no cambian una vez creada la definición.

Siguientes pasos​