POST /v1/activate
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
- Campos como arriba; si no,
400 bad_request. - La app existe; si no,
404 unknown_app. - 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_keysi no cuadra. - Se busca la licencia por el hash de la clave normalizada:
404 invalid_keysi no está. - El email tiene que ser el de la licencia: si no,
404 invalid_key(a propósito, no se distingue de una clave mala). - La licencia tiene que ser de esta app:
403 wrong_app. - Las claves de add-on no se activan:
409 addon_key. - Estado:
403 revoked;403 expiredsi está cancelada o caducada;402 past_duesi el pago falló y la gracia se agotó. - Las claves de prueba solo valen en el equipo que las pidió:
409 trial_device. - 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"}