Ir al contenido

Especificación de la API

La referencia completa de endpoints REST. Para tutoriales y ejemplos, consulta la Guía de la API.

Todo lo que un miembro de un proyecto puede hacer en la interfaz web está disponible aquí — la SPA consume esta misma API. Las operaciones que requieren el rol de propietario (owner) están marcadas con (owner); todo lo demás solo necesita la membresía del proyecto (o, para las lecturas marcadas con (viewer), cualquier nivel de acceso).

https://eastagiletracker.com/api/v1

https://api.eastagiletracker.com/api/v1 sirve la API idéntica. Todas las solicitudes y respuestas son JSON, salvo unos pocos endpoints de subida de archivos que aceptan multipart.

Cada solicitud autenticada envía una clave de API mediante una de:

  • X-TrackerToken: <key>
  • Authorization: Bearer <key>

Las claves de usuario empiezan por ea_user_. Las claves de agente empiezan por ea_agent_. Consulta Guía de la API → Dos tipos de claves.

Endpoints sin autenticación: /openapi.json, /docs, los endpoints /auth/* y las búsquedas de datos de referencia (/story_types, /story_states, /effort_scales, …). /meta está autenticado — cualquier clave válida funciona, pero no tiene alcance de proyecto (una clave de agente atada a un proyecto también lo alcanza).

Tres niveles condicionan los endpoints con alcance de proyecto:

NivelQuién pasaOperaciones típicas
viewerviewer, member, ownerlecturas (listar/obtener historias, búsqueda, analíticas)
membermember, ownertodas las escrituras de elementos de trabajo (historias, tareas, comentarios, …)
ownersolo ownerconfiguración del proyecto, gestión de membresía, claves de agente, eliminación, importación, registro de auditoría

Un no miembro recibe 404 unfound_resource (no 403) en las rutas de proyecto, de modo que los IDs de proyecto no son enumerables.

MétodoRutaDescripción
GET/openapi.jsonLa especificación OpenAPI 3 en vivo. Sin autenticación.
GET/docsSwagger UI. Sin autenticación.
GET/metaIdentidad del que llama (auth.kind/key_id/agent_id/project_id) + el grafo de transiciones por tipo de historia. Autenticado (cualquier clave válida; sin alcance de proyecto). Llama a esto primero.

Estos actúan sobre el que llama y solo necesitan una clave válida (sin rol de proyecto).

MétodoRutaDescripción
POST/auth/registerRegistrar una cuenta nueva
POST/auth/loginIniciar sesión, devuelve un token de sesión
POST/auth/logoutCerrar sesión
POST/auth/forgot-passwordSolicitar un correo de restablecimiento de contraseña
POST/auth/reset-passwordUsar un token de restablecimiento para establecer una contraseña nueva
GET/auth/verify-emailVerificar una dirección de correo electrónico
POST/auth/accept-invite/lookupResolver un token de invitación → correo electrónico (sin autenticación)
POST/auth/accept-inviteAceptar una invitación a un proyecto (tras autenticarse)
GET/mePerfil del usuario actual
PUT/meActualizar el perfil
DELETE/meEliminar la cuenta
PUT/me/passwordCambiar la contraseña
PUT/me/settingsActualizar la configuración (tema, preferencias de notificación)
POST/me/avatarSubir avatar (multipart)
POST/me/api-token/regenerateRotar tu token de API — invalida las sesiones/claves existentes
GET/me/api_keys · POST /me/api_keys · DELETE /me/api_keys/{id}Gestionar las claves de API de usuario (ea_user_)
GET/me/activityTu actividad en todos los proyectos
GET/me/data-exportAutoexportación RGPD de tus datos
GET/me/consent · POST /me/consentLeer / registrar el consentimiento ({ consent_type, granted })
GET/legal/pending · POST /legal/acceptDocumentos de clickwrap pendientes / registrar la aceptación
POST/contact · POST /feedback · POST /feedback/with-screenshotContacto + feedback en la aplicación

Búsquedas de datos semilla usadas al crear/estimar historias. IDs estables.

MétodoRutaDescripción
GET/story_typesfeature, bug, chore, release (+ allow_points)
GET/story_statesunstarted … accepted, rejected
GET/effort_scalesescalas de estimación disponibles
GET/effort_scales/{scale_id}/valueslos valores de puntos de una escala
MétodoRutaDescripción
GET/projectsListar tus proyectos
POST/projectsCrear un proyecto
GET/projects/{id}Obtener los detalles del proyecto (viewer)
PUT/projects/{id}Actualizar la configuración del proyecto (owner)
DELETE/projects/{id}Eliminar un proyecto (owner)
GET/projects/{id}/audit-logFlujo de actividad del proyecto (owner)
GET/projects/{id}/eventsFlujo de eventos paginado por cursor (viewer) — consulta Eventos
MétodoRutaDescripción
GET/projects/{id}/membershipsListar miembros (viewer)
POST/projects/{id}/membershipsInvitar a un miembro por correo electrónico (owner)
PUT/projects/{id}/memberships/{mid}Actualizar el rol (owner)
DELETE/projects/{id}/memberships/{mid}Eliminar a un miembro (owner)
GET / POST/projects/{id}/agent_keysListar / generar claves de agente (owner)
DELETE/projects/{id}/agent_keys/{kid}Revocar una clave de agente (owner)

Todas las escrituras de historias necesitan el rol member.

MétodoRutaDescripción
GET/projects/{id}/storiesListar historias (paginadas, filtrables) (viewer)
POST/projects/{id}/storiesCrear una historia
GET/projects/{id}/stories/{sid}Obtener una historia (viewer)
PUT/projects/{id}/stories/{sid}Actualizar una historia
DELETE/projects/{id}/stories/{sid}Eliminar una historia
POST/projects/{id}/stories/{sid}/transitionsCambiar de estado con validación
POST/projects/{id}/stories/bulk_transitionTransicionar muchas historias (1–100) a la vez
POST/projects/{id}/stories/bulk-deleteEliminar muchas historias
POST/projects/{id}/stories/bulk-duplicateDuplicar muchas historias
POST/projects/{id}/stories/{sid}/duplicateDuplicar una historia

Crear (POST …/stories): { "name" (required), "story_type": "feature|bug|chore|release", "description"?, "estimate"?, "current_state"?, "icebox"?, "labels"? }. labels acepta ["auth"] o [{ "name": "auth" }]; las etiquetas desconocidas se crean. Predeterminados: story_type=feature, current_state=unstarted.

Actualizar (PUT …/stories/{sid}): los mismos campos, todos opcionales, más "position" (float) y "force_state_change" (bool).

Transicionar (POST …/transitions): { "to": "<state>", "reason"? }. El campo es to. Devuelve { story_id, state }. Movimiento ilegal → 422 invalid_transition con details: { from, to, allowed }.

Transición en bloque (POST …/bulk_transition): { "story_ids": [int,…] (1–100), "to": "<state>", "reason"? }. Cada historia se juzga de forma independiente; devuelve { results: [ { id, status: "ok" } | { id, status: "failed", error } ] }.

Todos member. List/GET en la mayoría es (viewer).

MétodoRutaCuerpo / notas
GET / POST/projects/{id}/stories/{sid}/tasks · PUT/DELETE …/tasks/{tid}{ description (or task_desc), complete?, task_order? }
GET / POST/projects/{id}/stories/{sid}/comments · PUT/DELETE …/comments/{cid}{ text (or comment_text) } o { comment_emoji }
GET / POST/projects/{id}/stories/{sid}/blockers · PUT/DELETE …/blockers/{bid}{ blocker_desc, resolved? }
GET / POST/projects/{id}/stories/{sid}/links · DELETE …/links/{lid}{ url, link_type?, title? }
GET / POST/projects/{id}/stories/{sid}/reviews · PUT/DELETE …/reviews/{rid}{ reviewer_id? / reviewer_agent_id?, status, comment? }
GET / POST/projects/{id}/stories/{sid}/owners · DELETE …/owners/{mid} · DELETE …/owners/agents/{aid}{ member_id? / agent_id? } — omite ambos para añadir al que llama
GET / POST/projects/{id}/stories/{sid}/followers · DELETE …/followers/{mid} · DELETE …/followers/agents/{aid}{ member_id? / agent_id? }
GET / POST/projects/{id}/stories/{sid}/labels · DELETE …/labels/{lid}{ name }
GET / POST/projects/{id}/stories/{sid}/attachments (+ /json) · DELETE …/attachments/{aid}subida multipart (≤ 2 GB cada uno); el listado es (viewer)

member para escrituras, (viewer) para lecturas.

MétodoRutaDescripción
GET / POST/projects/{id}/labelsListar / crear una etiqueta
PUT / DELETE/projects/{id}/labels/{lid}Actualizar / eliminar una etiqueta
POST/projects/{id}/labels/{lid}/archiveArchivar (ocultar de forma suave) una etiqueta
MétodoRutaDescripción
GET/projects/{id}/iterationsListar iteraciones (member)
POST/projects/{id}/iterationsCrear una iteración manual (owner)
DELETE/projects/{id}/iterations/{itid}Eliminar una iteración (owner)

Búsqueda, analíticas, métricas, preferencias

Sección titulada «Búsqueda, analíticas, métricas, preferencias»
MétodoRutaDescripción
GET/projects/{id}/search?query=…Búsqueda con sintaxis de filtros (viewer) — consulta la Guía
GET/projects/{id}/analytics/{overview,iteration,releases,activity,cycle-time,projections}Analíticas (viewer). El desglose por iteración toma ?iteration_id=
GET/projects/{id}/metrics/{velocity,burndown,story-types}Métricas (viewer)
GET / PUT/projects/{id}/preferencesTus preferencias de tablero para este proyecto (member)
MétodoRutaDescripción
GET/projects/{id}/eventsFlujo de eventos paginado por cursor (viewer)

Parámetros de consulta: since=<event_id>, types=story.created,story.transitioned,comment.created,…, limit=, cursor=. La respuesta incluye next_cursor. Pasa el último event_id que viste como since para reanudar.

MétodoRutaDescripción
POST/projects/{id}/import (+ /json)Subida multipart. source=pivotal, jira, asana, gitlab, shortcut, trello, linear, plane, plane_json.
wss://eastagiletracker.com/ws/control?token=<key>

Para control remoto interactivo de la interfaz ({ "action": "get_state", "id": "req-1" }). No es un canal de datos — todas las lecturas/escrituras pasan por REST. Solo de instancia única; no se distribuye entre réplicas.

Los endpoints de escritura (POST, PUT, DELETE) aceptan una cabecera Idempotency-Key. La misma clave + el mismo cuerpo reproduce la respuesta en caché (ventana de 24 horas); la misma clave + un cuerpo distinto devuelve 409 idempotency_conflict. No se aplica a GET/HEAD/OPTIONS, /auth/* ni a las rutas /attachments. Las respuestas 5xx nunca se cachean — un reintento llega al manejador.

Los endpoints de listado aceptan cursor=<opaque> y limit=<n≤200>. Cuando se establece, la respuesta es { "items": [...], "next_cursor": "<str|null>" }; pasa next_cursor de vuelta para paginar. Algunas listas también devuelven una cabecera X-Tracker-Pagination-Total.

Los endpoints de listado aceptan fields= (separados por comas) para devolver solo campos específicos. story_id siempre se incluye; un nombre de campo desconocido devuelve 400 validation_failed con los nombres infractores en details.fields.

GET /projects/123/stories?fields=story_id,name,current_state,owners

Cada error JSON tiene code y error; algunos añaden details:

{ "code": "invalid_transition",
"error": "Cannot move story from `unstarted` to `accepted`",
"details": { "from": "unstarted", "to": "accepted", "allowed": ["started"] } }
EstadocodeCuándo
400invalid_parameterentrada incorrecta; mensaje en error, sin details (la mayoría de las validaciones: en blanco/longitud/byte nulo/email)
400validation_failederror de entrada estructurado; details.fields es un array de los nombres de los campos infractores
401unauthenticatedtoken ausente/inválido
403unauthorized_operationautenticado pero con rol insuficiente
404unfound_resourceno encontrado — también se devuelve a los no miembros
409conflictconflicto de recurso (p. ej., duplicado)
409idempotency_conflictIdempotency-Key reutilizado con un cuerpo distinto
422invalid_transitionmovimiento de estado ilegal; details lleva { from, to, allowed }
500internal_errorfallo del servidor — mensaje genérico; seguro reintentar

details.fields es un array JSON de nombres de campos (p. ej., ["to"]), a veces con claves adicionales como max. No hay un mapa campo→mensaje.

{ "code": "validation_failed", "error": "unknown field(s): foo", "details": { "fields": ["foo"] } }

Por clave (por IP para los endpoints sin autenticación), cubo de tokens GCRA:

  • Auth/auth/*: 0.5 req/s, ráfaga 20.
  • Public/contact, /feedback: 0.2 req/s, ráfaga 10.
  • Sensitive — restablecimiento de contraseña: ~0.002 req/s, ráfaga 5.

Un límite superado devuelve 429 Too Many Requests con una cabecera Retry-After y un cuerpo en texto plano (no el sobre de error JSON).