API de Skillion
Lee y cambia tus propias skills, tareas y rutinas de time blocking desde tus scripts, atajos e integraciones. Incluida en Skillion Premium.
https://api.skillion.app/functions/v1/api/v1 En esta página
Tu primera llamada
-
Saca tu clave en la app: Ajustes → toca 7 veces la versión → API para desarrolladores. Paso a paso, con todo detalle
Ojo
Necesitas Skillion Premium y una cuenta registrada (no de invitado). Sin Premium la fila sale con candado y abre la pantalla de suscripción.
-
Pide tu perfil:
export SKILLION_KEY="sk_live_…" # la tuya, de la app curl https://api.skillion.app/functions/v1/api/v1/me \ -H "Authorization: Bearer $SKILLION_KEY" -
Te devuelve tu nivel, XP, monedas y energía, y tus límites:
{ "data": { "level": 7, "total_xp": 140, "xp_to_next_level": 350, "coins": 820, "energy_points": 64, "max_energy": 100, "health_points": 100, "max_health": 100, "nickname": "Luna", "updated_at": "2026-10-21T18:00:00Z" }, "limits": { "per_minute": 120, "per_day": 10000, "page_max": 100 } }
Tu clave
-
Abre Skillion y ve a Ajustes.
-
Baja hasta el final y toca siete veces seguidas la línea «Versión …», con menos de 2 segundos entre toque y toque. El móvil vibra y sale «Opciones de desarrollador activadas».
-
En Ajustes aparece una fila nueva: «API para desarrolladores». Ábrela.
-
Toca «Crear clave», ponle un nombre y toca «Crear». Después toca «Copiar»: la clave solo se enseña una vez. Puedes tener hasta 5 activas y revocar cualquiera desde esa pantalla.
Ojo
Trata la clave como una contraseña: quien la tenga puede cambiar tus tareas. Si se te escapa, revócala en esa misma pantalla.
Autenticación
Manda la clave como token Bearer en cada petición. Todos los cuerpos van en JSON.
curl https://api.skillion.app/functions/v1/api/v1/me \
-H "Authorization: Bearer sk_live_…" Completar una tarea no es inmediato
Ojo
El XP, las monedas, la energía y las rachas los calcula la app, así que completar por la API es una petición: recibes 202 al momento y tu móvil la aplica la próxima vez que Skillion sincroniza (al abrirla). Síguela en /v1/completions: pending → claimed → applied, o rejected con el motivo (already_completed, insufficient_energy…).
Las tareas de un pacto se completan con un check-in en la app y los malos hábitos nunca se completan: las dos dan 409.
Endpoints
| Método | Ruta | Qué hace |
|---|---|---|
| Cuenta | ||
| GET | /v1/me | Tu nivel, XP, monedas, energía y los límites |
| Skills | ||
| GET | /v1/skills | Lista las skills |
| POST | /v1/skills | Crea una skill |
| GET | /v1/skills/{id} | Una skill |
| PATCH | /v1/skills/{id} | Cambia una skill |
| DELETE | /v1/skills/{id} | Archiva una skill |
| Tareas | ||
| GET | /v1/tasks | Lista las tareas, cada una con sus skills |
| POST | /v1/tasks | Crea una tarea |
| GET | /v1/tasks/{id} | Una tarea |
| PATCH | /v1/tasks/{id} | Cambia una tarea |
| DELETE | /v1/tasks/{id} | Borra o archiva una tarea |
| POST | /v1/tasks/{id}/complete | Pide a la app que la complete (202) |
| Completados | ||
| GET | /v1/completions | Tus peticiones de completar y qué pasó |
| Rutinas | ||
| GET | /v1/routines | Lista las rutinas, cada una con sus bloques |
| POST | /v1/routines | Crea una rutina con sus bloques |
| GET | /v1/routines/{id} | Una rutina |
| PATCH | /v1/routines/{id} | Cambia una rutina o sus bloques |
| DELETE | /v1/routines/{id} | Borra una rutina y sus bloques |
| Solo lectura | ||
| GET | /v1/categories | Tus categorías (solo lectura) |
| GET | /v1/history | Tu historial: completados, subidas de nivel… (solo lectura) |
Listas y paginado
Las listas vienen del cambio más antiguo al más nuevo. Devuelve next_cursor como cursor hasta que sea null. Lo borrado y lo archivado no sale salvo que lo pidas.
| Parámetro | Regla |
|---|---|
limit | de 1 a 100, 50 por defecto |
cursor | el next_cursor de la página anterior |
updated_since | fecha ISO: solo lo que cambió después |
include_deleted | true para traer también lo borrado (con deleted_at) |
include_archived | true para traer también skills y tareas archivadas |
category_id, status | solo tareas: filtra por categoría o estado |
event_type | solo historial, por ejemplo taskCompleted |
Campos de tarea que puedes poner
| Campo | Regla |
|---|---|
titleobligatorio | texto, 1–128 |
description | texto hasta 2.000 o null |
category_id | una de tus categorías; por defecto, la primera predeterminada |
skills | [{"skill_id": "…", "xp": -100…100}], hasta 20; xp vale 10 por defecto, negativo resta XP |
difficulty | very_easy, easy, medium (por defecto), hard, very_hard |
is_recurring | true / false |
recurrence_pattern | daily, weekly, monthly, yearly, custom (obligatorio si es recurrente) |
custom_days | "1,3,5": 1 = lunes … 7 = domingo (para custom) |
deadline | fecha ISO, solo para tareas no recurrentes |
Ver 15 campos más
| Campo | Regla |
|---|---|
priority | 1–5, 3 por defecto |
energy_cost | -50…50, -1 por defecto (negativo gasta energía, positivo la da) |
coins | 0–100, 10 por defecto |
health_effect | -50…50 |
estimated_duration | segundos, 0–86.400 |
recurrence_target | 1–99 veces por periodo |
recurrence_day_of_month | 1–31 (monthly, yearly) |
recurrence_month | 1–12 (yearly) |
recurrence_end_date | fecha ISO |
daily_deadline_time | "HH:mm" |
reminder_mode | auto (por defecto), at_deadline, before_15, before_60, morning, custom, none |
reminder_minute | 0–1439, minuto del día (con custom) |
is_penalty | true: pierdes recompensa si pasa el plazo |
icon | un emoji, o assets/images/icons/<nombre>.png |
color | #RRGGBB |
Solo title es obligatorio. Lo demás (XP, nivel, estado, monedas ganadas) lo calcula la app y es de solo lectura. Un campo desconocido da 422, así que una errata nunca pasa desapercibida.
Campos de skill que puedes poner
| Campo | Regla |
|---|---|
name | texto, 1–32 (obligatorio) |
description | texto hasta 500 o null |
icon | un emoji, o assets/images/icons/<nombre>.png |
color | #RRGGBB o null |
is_active | true / false |
level, total_xp (el XP dentro del nivel actual) y las rachas son de solo lectura. Las cuentas gratis mantienen 3 skills activas, pero la API en sí es solo Premium.
Rutinas y sus bloques
Una rutina es un día de la pantalla de time blocking («Lunes», «Día de trabajo»…) con sus bloques dentro. Una semana entera son siete POST /v1/routines, uno por día.
Rutina
| Campo | Regla |
|---|---|
name | texto, 1–64 (obligatorio) |
blocks | lista de hasta 96 bloques (abajo) |
is_default | true: la que usa la app por defecto; marcar una desmarca las demás. Si aún no tienes ninguna, la nueva lo es |
icon | un emoji, o assets/images/icons/<nombre>.png, o null |
color | #RRGGBB o null |
Cada bloque de blocks
| Campo | Regla |
|---|---|
start_minute | minutos desde medianoche, 0–2879 de 5 en 5 (420 = 07:00; 1440 o más es el día siguiente, para una rutina que pasa de medianoche) |
duration_minute | 5–1440 de 5 en 5 |
title | texto, 1–128; obligatorio si el bloque no lleva task_id |
task_id | una de tus tareas: el bloque es esa tarea |
note | texto hasta 2.000 o null |
icon | un emoji, o assets/images/icons/<nombre>.png, o null |
color | #RRGGBB o null |
is_pinned | true: «Empezar ahora» no lo mueve |
id | solo para cambiar un bloque que la rutina ya tiene |
blocks es siempre la lista entera: un bloque con id se actualiza, uno sin id se crea y el que no mandes se borra. Si no mandas blocks en un PATCH, se quedan como están. Los bloques vuelven con start_time y end_time («07:30») para leerlos fácil.
Ojo
Qué rutina enseña hoy tu móvil se elige en el móvil: la API crea y cambia rutinas, pero no cambia la que ves.
Errores
Los errores tienen siempre la misma forma. details dice qué campo no es válido.
{
"error": {
"code": "invalid_field",
"message": "Some fields are not valid.",
"details": { "custom_days": "must be weekdays like \"1,3,5\" …" }
}
} | HTTP | Código | Qué hace |
|---|---|---|
| 400 | invalid_request / invalid_json | parámetro o cuerpo incorrecto |
| 401 | unauthorized | clave ausente, mal formada o revocada |
| 403 | premium_required | la cuenta no es Premium |
| 404 | not_found | no existe (o no es tuyo) |
| 409 | pact_task / bad_habit / too_many_pending | ese cambio es de la app |
| 413 | payload_too_large | cuerpo de más de 64 KB |
| 422 | invalid_field | mira details |
| 429 | rate_limited | espera retry-after segundos |
| 405 | method_not_allowed | ese método no existe en esa ruta |
| 500 | internal | fallo nuestro: prueba más tarde |
Límites
- 120 peticiones por minuto
- por cuenta, todas tus claves juntas
- 10.000 peticiones al día
- por cuenta, el día empieza a las 00:00 UTC
- 100 elementos por página
- 50 por defecto
- 64 KB por cuerpo
Si te pasas recibes 429 con la cabecera retry-after (segundos). Cada respuesta lleva x-ratelimit-limit y x-ratelimit-remaining.
Borrar
DELETE hace lo mismo que la app: las skills y las tareas recurrentes se archivan (su historial se queda) y las tareas sueltas se borran, y una rutina se borra con sus bloques. En todos los casos desaparecen de tu móvil en el siguiente sync.
Conviene saber
- Los cambios llegan a tu móvil en el siguiente sync, normalmente al abrir la app.
- Si editas la misma tarea en el móvil antes de que sincronice, gana el móvil.
- Las tareas recurrentes reciben su próxima fecha desde tu móvil, que sabe tu zona horaria y tu hora de reinicio.
- Las fechas van en ISO 8601 con zona horaria, por ejemplo
2026-10-21T18:00:00Z.
Ejemplos
Crear una tarea recurrente unida a una skill
curl -X POST https://api.skillion.app/functions/v1/api/v1/tasks \
-H "Authorization: Bearer $SKILLION_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Gym",
"is_recurring": true,
"recurrence_pattern": "custom",
"custom_days": "1,3,5",
"difficulty": "hard",
"skills": [{ "skill_id": "<skill id>", "xp": 20 }]
}' Completarla (desde un atajo, un cron, un botón)
curl -X POST https://api.skillion.app/functions/v1/api/v1/tasks/<task id>/complete \
-H "Authorization: Bearer $SKILLION_KEY"
# 202 → { "data": { "id": "…", "status": "pending", … } } Leer todo lo que cambió desde ayer
const since = new Date(Date.now() - 86_400_000).toISOString()
let cursor = null
do {
const url = new URL('https://api.skillion.app/functions/v1/api/v1/tasks')
url.searchParams.set('updated_since', since)
if (cursor) url.searchParams.set('cursor', cursor)
const res = await fetch(url, { headers: { Authorization: `Bearer ${process.env.SKILLION_KEY}` } })
const page = await res.json()
for (const task of page.data) console.log(task.title, task.status)
cursor = page.next_cursor
} while (cursor) Organizar la semana: una rutina por día
const days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
for (const name of days) {
const weekend = name === 'Saturday' || name === 'Sunday'
const res = await fetch('https://api.skillion.app/functions/v1/api/v1/routines', {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.SKILLION_KEY}`, 'Content-Type': 'application/json' },
body: JSON.stringify({
name,
blocks: [
{ start_minute: weekend ? 540 : 420, duration_minute: 30, title: 'Read' },
{ start_minute: 1080, duration_minute: 60, title: 'Gym', color: '#F29092' },
{ start_minute: 1380, duration_minute: 60, title: 'Wind down' },
],
}),
})
if (!res.ok) console.error(name, (await res.json()).error)
} ¿Dudas? Escríbenos desde la página de contacto. Contacto