POST /v1/activate

Actualizada el 3 min de lectura

Documentación preliminar: describe el producto previsto y cambiará antes de la beta.

Activa este equipo con email + clave y devuelve un token firmado.

POST /v1/activate
content-type: application/json

No hay claves de API: el email y la clave de la licencia son las credenciales. El cuerpo debe ser un objeto JSON. Los campos desconocidos se ignoran, y los textos vacíos o null cuentan como ausentes.

Petición

Campo Tipo Oblig. Validación
app string sí ^[a-z0-9-]{1,32}$
email string sí Hasta 254 caracteres. Se compara con el de la licencia sin espacios y en minúsculas
key string sí Hasta 64 caracteres. Se normaliza: mayúsculas, sin espacios ni guiones, O→0, I/L→1
dev string sí ^[0-9a-f]{64}$: SHA-256 del equipo, en hexadecimal en minúsculas
deviceName string no Hasta 100 caracteres
appVersion string no Hasta 32 caracteres
os string no Hasta 64 caracteres, como macOS 26.1
arch string no Hasta 16 caracteres, ^[A-Za-z0-9_-]+$ (arm64, x64)
locale string no Hasta 16 caracteres, ^[A-Za-z0-9_-]+$ (es-ES)
channel string no Hasta 16 caracteres, ^[a-z0-9-]+$ (direct, store)

El Worker guarda además, como prueba de la activación, la fecha, la dirección IP, el país y la red desde la que llegó la petición. Menciónalo en la política de privacidad de tu app.

Comprobaciones, en este orden

  1. Campos como arriba; si no, 400 bad_request.
  2. La app existe; si no, 404 unknown_app.
  3. Si la clave lleva el prefijo y la longitud de la app, se comprueba su carácter de control sin ir a la base de datos: 404 invalid_key si no cuadra.
  4. Se busca la licencia por el hash de la clave normalizada: 404 invalid_key si no está.
  5. El email tiene que ser el de la licencia: si no, 404 invalid_key (a propósito, no se distingue de una clave mala).
  6. La licencia tiene que ser de esta app: 403 wrong_app.
  7. Las claves de add-on no se activan: 409 addon_key.
  8. Estado: 403 revoked; 403 expired si está cancelada o caducada; 402 past_due si el pago falló y la gracia se agotó.
  9. Las claves de prueba solo valen en el equipo que las pidió: 409 trial_device.
  10. Equipos: activar un equipo que ya está activo no gasta otro puesto. Un equipo nuevo con todos los puestos ocupados da 409 seats_exhausted.

Respuesta 200

{
  "token": "eyJ2IjoxLCJhcHAiOiJteWFwcCIs….3vQ2JmXo8w…",
  "status": "active"
}
Campo Significado
token Token firmado. Guárdalo tal cual
status active, o past_due: el pago falló pero la gracia no ha terminado (avisar sin bloquear)

Las respuestas llevan content-type: application/json; charset=utf-8 y cache-control: no-store.

Errores

Los errores llegan como JSON con un código error estable y un message en inglés solo para depurar:

{ "error": "invalid_key", "message": "Invalid license key or email" }
error HTTP Cuándo
bad_request 400 Cuerpo o campos no válidos
unknown_app 404 La app no existe
invalid_key 404 Errata, clave desconocida o email distinto
wrong_app 403 La clave es de otra app
revoked 403 La licencia está anulada
expired 403 Suscripción cancelada o caducada, o prueba terminada
past_due 402 Pago fallido y gracia agotada
seats_exhausted 409 No quedan equipos libres
addon_key 409 La clave es de un add-on: activa la licencia principal
trial_device 409 La clave de prueba se pidió desde otro equipo
rate_limited 429 Demasiadas peticiones desde la misma IP; llega con Retry-After en segundos
internal 500 Error inesperado; reintenta con espera

Los fallos de red, los tiempos de espera o una respuesta que no es JSON no traen error: trátalos como internal. Un 429 sin cuerpo JSON viene del cortafuegos de Cloudflare y significa lo mismo que rate_limited.

Ejemplo: sin equipos libres

curl -sS -i https://licenses.example.com/v1/activate \
  -H 'content-type: application/json' \
  -d '{"app":"myapp","email":"ana@example.com","key":"MYAPP-7Q2KD-H9XNM-4TRVA-C8PWZ-K","dev":"0000000000000000000000000000000000000000000000000000000000000004"}'
HTTP/1.1 409 Conflict
content-type: application/json; charset=utf-8

{"error":"seats_exhausted","message":"All 3 seats are in use"}