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 manager están marcadas con (manager); todo lo demás solo necesita la membresía del proyecto (o, para las lecturas marcadas con (viewer), cualquier nivel de acceso). Las tablas de abajo nombran todos los grupos de rutas que monta el servidor; los que se resumen en una sola línea están descritos por completo en el openapi.json en vivo.

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.

Dos grupos quedan un nivel más arriba, bajo /api en lugar de /api/v1: la superficie de autenticación (/api/auth/*) y los formularios públicos (/api/contact, /api/feedback). Sus grafías /api/v1/… devuelven 404.

Cada solicitud autenticada envía una credencial mediante una de:

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

Las claves de usuario empiezan por ea_user_, las de agente por ea_agent_ y los tokens de acceso MCP por ea_mcp_. Consulta Guía de la API → Tres tipos de credenciales.

Endpoints sin autenticación: /openapi.json, /docs, los endpoints /api/auth/* y las búsquedas de datos de referencia (/story_types, /story_states, /effort_scales, /priority_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).

Cuatro niveles condicionan los endpoints con alcance de proyecto:

NivelQuién pasaOperaciones típicas
public viewercualquiera, en un proyecto cuya visibilidad sea públicalecturas del tablero: historias, iteraciones, búsqueda, actividad de historias y epics (con los detalles del actor censurados)
viewerviewer, member, managerlecturas (listar/obtener historias, búsqueda, métricas, lista de formatos de exportación)
membermember, managertodas las escrituras de elementos de trabajo (historias, tareas, comentarios, …), el flujo de eventos
managersolo managerconfiguración del proyecto, gestión de membresía, claves de agente, eliminación, importación, descargas de exportación, copias de seguridad, registro de auditoría

Los agentes tienen los mismos roles que los miembros — viewer, member o manager — con el tope del rol del miembro que emitió la clave. Un no miembro recibe 404 unfound_resource (no 403) en las rutas de proyectos privados, de modo que los IDs de proyecto no son enumerables.

MétodoRutaDescripción
GET/openapi.jsonLa especificación OpenAPI 3 en vivo, con los cuerpos de las solicitudes incluidos. 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.
GET/api/health · /api/configComprobación de vida, y la configuración pública del despliegue (modo de organización única, funciones opcionales activadas, nombre de la instancia). Sin autenticación, fuera de /v1.

Endpoints de sesión, sin autenticación salvo que se indique lo contrario. La SPA es quien los usa; los scripts normalmente usan una clave de API en su lugar.

MétodoRutaDescripción
POST/auth/registerRegistrar una cuenta nueva — protegido por reCAPTCHA; después la cuenta pasa el desafío por SMS
POST/auth/sms/challenge · /auth/sms/status · /auth/sms/bypassEnviar / comprobar el código SMS del registro (el bypass está restringido al operador)
GET/auth/configQué métodos de inicio de sesión ofrece el despliegue
POST/auth/loginIniciar sesión con correo electrónico + contraseña; devuelve un JWT de sesión o un desafío TOTP
POST/auth/login/totpCompletar un inicio de sesión con un código del autenticador o un código de recuperación
POST/auth/passkey/login/start · /auth/passkey/login/finishInicio de sesión WebAuthn sin contraseña
GET/auth/oauth/github/start · /auth/oauth/github/callback · /auth/oauth/google/start · /auth/oauth/google/callback · POST /auth/oauth/exchangeInicio de sesión OAuth con GitHub o Google
POST/auth/refresh · /auth/refresh/revokeRotar el refresh token / revocarlo
POST/auth/logoutCerrar sesión (revoca el refresh token)
POST/auth/forgot-password · /auth/reset-passwordSolicitar un correo de restablecimiento / usar el token de restablecimiento
POST/auth/accept-invite/lookup · /auth/accept-inviteResolver un token de invitación → correo electrónico / aceptar la invitación al proyecto (tras autenticarse)

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

MétodoRutaDescripción
GET/mePerfil del usuario actual
PUT/meActualizar el perfil
DELETE/meEliminar la cuenta — se rechaza mientras seas el único owner de una organización o de un proyecto con otros miembros
GET/me/deletion-impactQué eliminaría borrar la cuenta y qué lo impide
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/totp · POST /me/totp/setup · /me/totp/verify · /me/totp/disableAlta del doble factor (TOTP); verify devuelve los códigos de recuperación una sola vez
GET/me/passkeys · POST /me/passkeys/register/start · /me/passkeys/register/finish · DELETE /me/passkeys/{credential_id}Alta y eliminación de passkeys
GET/me/oauth_grants · DELETE /me/oauth_grants/{grant_id}Aplicaciones conectadas — los clientes MCP y las aplicaciones OAuth que has autorizado
GET/me/activityTu actividad en todos los proyectos
GET/me/storiesHistorias de las que eres owner, que solicitaste o que sigues, en todos los proyectos a los que llega el token — role=owned|requested|following, state=, cursor= / limit= (máx. 200)
GET/me/mentions · POST /me/mentions/{mention_id}/ackLa bandeja de @-menciones (unacked=true para filtrar) y su confirmación de lectura — también incorporada al feed de notificaciones de más abajo
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
GET / PUT/agent/meLa identidad y el perfil propios de una clave de agente, que el agente puede leer y editar (la contraparte de /me del lado del agente)
POST/api/contact · /api/feedback · /api/feedback/with-screenshotContacto + feedback en la aplicación. Fuera de /v1; con límite de tasa por IP

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
GET/priority_scales · /priority_scales/{scale_id}/valueslas escalas de prioridad y sus valores (el priority_id de una historia se resuelve aquí)

Solo en el servicio alojado — una instalación autoalojada funciona en modo de organización única y no los monta (salvo la lista de organizaciones). Los roles son roles de organización: owner, admin, member.

MétodoRutaDescripción
GET / POST/organizationsListar tus organizaciones / crear una
GET / PUT / DELETE/organizations/{oid}Leer, renombrar (nombre + slug; owner o admin), eliminar
GET / POST/organizations/{oid}/memberships · PUT / DELETE …/memberships/{member_id}Miembros e invitaciones; las invitaciones llevan un tope de rol (nunca por encima del de quien invita; el owner nunca se invita)
POST/organizations/{oid}/memberships/bulk-role · …/memberships/bulk-removeCambia el rol de hasta 200 miembros a la vez o elimínalos. Todo o nada: un lote que eliminaría al último owner o dejaría un proyecto sin propietario se rechaza entero; con reassign_confirmed pasas a ser propietario de esos proyectos
DELETE/organizations/{oid}/invitations/{invitation_id}Revocar una invitación pendiente
POST/organizations/{oid}/transfer-ownershipCeder el rol de owner a otro miembro
PUT/organizations/{oid}/memberships/{member_id}/anonymizationEnmascarar el nombre / correo / avatar de un miembro en toda la organización
GET/organization-invitations/{token} · POST …/{token}/acceptResolver / aceptar una invitación de organización recibida por correo
POST/organizations/{oid}/export · GET / POST …/export/jobs · GET …/export/jobs/{job_id} · …/export/jobs/{job_id}/downloadExportación de la organización, solo para el owner: un zip con un volcado SQL y todos los adjuntos, ejecutada como un job
MétodoRutaDescripción
GET/projectsListar tus proyectos (limit ≤ 200)
POST/projectsCrear un proyecto
GET/projects/{id}Obtener los detalles del proyecto (viewer)
PUT/projects/{id}Actualizar la configuración del proyecto (manager)
DELETE/projects/{id}Eliminar un proyecto (manager)
POST/projects/{id}/pinFijar / desfijar el proyecto en tu lista de proyectos
POST/projects/{id}/transfer-organizationMover el proyecto a otra organización (manager)
POST/projects/{id}/slack/testEnviar un mensaje de prueba al feed de Slack del proyecto (manager)
GET / POST/projects/{id}/showcase_claim_eligibility · /projects/{id}/showcase_claim · /projects/{id}/showcase_seedProyectos públicos de muestra: comprobar si puedes reclamar uno, reclamarlo, sembrarlo
GET/projects/{id}/audit-logLectura del audit log — historial del proyecto más actividad por historia / por epic vía surface=; el acceso varía según el surface, ver abajo
GET/projects/{id}/eventsFlujo de eventos paginado por cursor (member) — consulta Eventos

Parámetros de consulta del audit log: event_type= (un tipo o lista separada por comas), limit= (≤ 1000), before= (cursor keyset, created_at ISO-8601), surface= (project_history, story_activities, epic_activities), target_id= (el id de la historia/epic — requerido cuando surface=story_activities o epic_activities). Acceso: el log sin filtrar y surface=project_history son (manager); story_activities / epic_activities puede leerlos cualquier miembro del proyecto, y de forma anónima en proyectos públicos con la PII del actor censurada.

MétodoRutaDescripción
GET/projects/{id}/membershipsListar miembros (viewer)
POST/projects/{id}/membershipsInvitar a un miembro por correo electrónico (manager)
PUT/projects/{id}/memberships/{mid}Actualizar el rol (manager)
DELETE/projects/{id}/memberships/{mid}Eliminar a un miembro (manager)
GET/projects/{id}/addable-members · POST /projects/{id}/members/add-existingMiembros de la organización que aún no están en el proyecto / añadir uno sin invitación por correo (manager)
POST/projects/{id}/members/joinUn owner o admin de la organización se une a un proyecto de su organización como manager, o se asciende a sí mismo a manager (la acción Make me owner de la lista de proyectos)
PUT/projects/{id}/members/{mid}/anonymizationEnmascarar el nombre / correo / avatar de un miembro en este proyecto (manager)
GET / POST/projects/{id}/agent_keysListar / emitir claves de agente — los managers, o los roles que admita la política de roles creadores del proyecto
DELETE/projects/{id}/agent_keys/{kid}Revocar una clave de agente
GET/projects/{id}/agent_keys/onboardingEl paquete de incorporación: prompts y archivos de configuración para los clientes de agentes más comunes
GET/projects/{id}/agents · GET / PUT /projects/{id}/agents/{aid}Los agentes del proyecto y sus perfiles (nombre, iniciales, descripción, color)
POST/projects/{id}/agents/{aid}/rotate-key · /projects/{id}/agents/{aid}/avatarRotar la clave de un agente (se conservan su identidad y su historial) / subir su avatar

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/{sid}/reject · /projects/{id}/stories/{sid}/restartRechazar una historia entregada / devolver una rechazada a started (rejected es terminal para /transitions)
POST/projects/{id}/stories/{sid}/archive · /projects/{id}/stories/{sid}/unarchiveArchivar / desarchivar una historia
POST/projects/{id}/stories/bulk_transitionTransicionar muchas historias (1–100) a la vez
POST/projects/{id}/stories/bulk-archive · bulk-delete · bulk-duplicate · bulk-moveArchivar, eliminar, duplicar o mover (a un panel / una posición) muchas historias
POST/projects/{id}/stories/{sid}/duplicateDuplicar una historia
GET / POST / DELETE/projects/{id}/stories/{sid}/epics · …/epics/{eid}La pertenencia de la historia a epics
GET/short-links/{code} · /story-referencesResolver un enlace corto /s/<code> a su historia / resolver hasta 100 referencias a historias (#id, URLs) a las historias que el que llama puede leer

Parámetros de query para listas de historias: archived= (exclude por defecto / include / only — el filtro de archivado de tres estados; sustituye al obsoleto include_archived=true, que ahora es un alias de archived=include), include_done=true (admite historias del panel Done congeladas en iteraciones pasadas, excluidas por defecto). La paginación (cursor= / limit= / offset=) y los conjuntos reducidos de campos (fields=) siguen Paginación y Proyección de campos.

Crear (POST …/stories): { "name" (required), "story_type": "feature|bug|chore|release", "description"?, "estimate"?, "current_state"?, "icebox"?, "labels"? }. estimate es la etiqueta del valor de la escala, como cadena ("3", "13"); un número JSON se rechaza. 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), "force_state_change" (bool) y "expected_updated_at" (RFC 3339 — el guardado de una descripción se rechaza con 409 stale_write si la historia cambió desde que la leíste). Las escrituras de historias también respetan If-Match frente al ETag de la historia; una discrepancia es 412 precondition_failed.

Transicionar (POST …/transitions): { "to": "<state>" }. 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>" }. 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 acepta fields= (lista permitida: comment_id, story_comment_id, story_id, comment_text, comment_emoji, member, created) además de cursor= / limit= (≤ 200) / order=asc|desc
GET / POST/projects/{id}/stories/{sid}/blockers · PUT/DELETE …/blockers/{bid}{ blocker_desc, resolved? }
GET / POST/projects/{id}/stories/{sid}/links · PUT/DELETE …/links/{lid}{ url, link_type?, title? }link_typerelates_to, duplicates, blocks, is_blocked_by, pull_request, branch, other; las URLs /pull/ y /tree/ de GitHub se tipifican automáticamente
GET / POST/projects/{id}/stories/{sid}/reviews · PUT/DELETE …/reviews/{rid}Crear: { reviewer_id? / reviewer_agent_id?, comment? } — omite ambos para asignarte a ti mismo. Actualizar: { 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 — vídeo ≤ 200 MB, PDF / Word / Excel ≤ 25 MB, imágenes / CSV / texto ≤ 10 MB; el listado es (viewer)
GET / POST/projects/{id}/stories/{sid}/link-attachments · DELETE …/link-attachments/{laid}Adjuntos de enlace — una URL externa que se guarda junto a los adjuntos de archivo, en lugar de como enlace de código
GET/attachments/{token} · /api/avatars/{token}Lecturas direccionadas por token de un adjunto o de un avatar — las URLs que entrega la API; no se necesita X-TrackerToken

La misma forma que las historias, sin la máquina de estados. member para escrituras, (viewer) para lecturas.

MétodoRutaDescripción
GET / POST/projects/{id}/epics · GET / PUT / DELETE …/epics/{eid}Los epics tienen un nombre, una descripción en Markdown y una etiqueta de respaldo que agrupa sus historias
GET / POST / PUT / DELETE…/epics/{eid}/comments · …/comments/{cid}Comentarios de epics
GET / POST / DELETE…/epics/{eid}/owners · …/followers (+ variantes /agents/{aid})Owners y followers, miembros o agentes — los owners de un epic se propagan a sus historias
GET / POST / DELETE…/epics/{eid}/attachments (+ /json) · …/link-attachmentsAdjuntos, con los mismos límites que las historias
GET/projects/{id}/analytics/epics · …/analytics/epics/{eid}Progreso por epic: burnup, rendimiento, salud, pronóstico (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

Las lecturas están abiertas a cualquier rol del proyecto, y son anónimas en un proyecto público.

MétodoRutaDescripción
GET/projects/{id}/iterationsListar iteraciones (≤ 500 por página; lleva un ETag y las cabeceras de continuación X-Tracker-Pagination-* cuando se trunca)
GET/projects/{id}/iterations/{itid}Una iteración
GET/projects/{id}/iterations/first-previewLas fechas que recibiría la primera iteración, mostradas en la confirmación de siembra
POST/projects/{id}/iterationsCrear una iteración manual (member)
DELETE/projects/{id}/iterations/{itid}Eliminar una iteración (manager)
PUT/projects/{id}/iterations/{itid}/velocityAnular la velocidad de una iteración sin cambiar la estrategia del proyecto (manager)
GET/projects/{id}/iterations/{itid}/done-storiesLas historias aceptadas de una iteración cerrada, paginadas
MétodoRutaDescripción
GET/projects/{id}/search?q=…Búsqueda potente — texto completo + calificadores de facetas / rangos de fechas / personas (DSL al estilo de GitHub); devuelve { results, total, limit, offset }. query es un alias de q; limit= (50 por defecto, máx. 1000) / offset= paginan; sort= ordena por relevance (por defecto), created, created_asc, state o updated. (viewer) — consulta la Guía
GET/projects/{id}/metrics/{velocity,burndown,story-types,contributors}Las series de la página Metrics (viewer); las métricas de epics están bajo /analytics/epics, más arriba
GET/projects/{id}/backlog/groupingLos grupos de iteraciones proyectados del Backlog (viewer)
GET / PUT/projects/{id}/preferencesTus preferencias de tablero para este proyecto — cualquier rol del proyecto, solo tu propia fila
MétodoRutaDescripción
GET/projects/{id}/eventsFlujo de eventos paginado por cursor (member) — los viewers reciben 403

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

El feed unificado de notificaciones in-app: filas de notificación de primera clase (solicitudes de revisión, actividad de stories, invitaciones, …) fusionadas con la bandeja de @-menciones en un único flujo, de más reciente a más antiguo. Los ids del feed llevan prefijo de origen (nt-… / sc-… / ec-…). Las sesiones de miembro y las claves ea_user_* leen sus filas de miembro; las claves ea_agent_* sus filas de agente.

MétodoRutaDescripción
GET/me/notificationsTu feed de notificaciones. Filtros: unread=true, since_id=, kind= (mentions / reviews / stories / invitations); pagina con cursor= / limit=
GET/me/notifications/unread-countTotales sin leer — { unread_count, by_category: { mentions, reviews, stories, invitations } }
POST/me/notifications/read-allMarcar todo como leído; devuelve los contadores actualizados
POST/me/notifications/{id}/ackMarcar un elemento como leído (idempotente)
POST/me/notifications/{id}/acceptAceptar una invitación a un proyecto / organización desde el feed (solo tokens de miembro)
POST/me/notifications/{id}/declineRechazar una invitación a un proyecto / organización (solo tokens de miembro)
GET/me/notifications/resolve-invite?token=…Resolver un token de invitación enviado por correo al id de tu notificación — { "id": "nt-…" } o { "id": null }
GET/me/notifications/streamPush en vivo — Server-Sent Events (text/event-stream); ver abajo

El endpoint de stream no es un endpoint JSON y por eso no está en la especificación OpenAPI: mantiene la conexión abierta y emite un frame sin payload ({"type":"notification","kind":…}) cada vez que llega algo nuevo, indicando al cliente que recargue el feed. Las conexiones se cortan en el servidor a los 45 minutos — reconecta y vuelve a autenticarte. Solo sesiones de miembro y claves ea_user_*; las claves ea_agent_* reciben 403.

MétodoRutaDescripción
POST/projects/{id}/importFuentes de archivo: source=pivotal, jira, asana, gitlab, shortcut, trello, linear, plane, plane_json, eat. Multipart file=. Síncrona — responde con los recuentos del resultado.
POST/projects/{id}/import/jsonCuerpo JSON; source=github no necesita archivo — owner, repo, token opcional, y las banderas opcionales include_pull_requests / include_milestones / include_releases / include_dependencies; las fuentes de archivo envían file_base64. Asíncrona: devuelve 202 { import_id, status }. El servidor descarga por la API GraphQL de GitHub, que rechaza a los llamantes anónimos, así que siempre llega un token a GitHub: el tuyo o el compartido del despliegue. Consulta la Guía.
GET/projects/{id}/imports/{import_id}Consultar un job: status avanza pending → fetching → writing → done | failed, con progress_current / progress_total durante la descarga y los recuentos del resultado en done

Solo se ejecuta una importación por proyecto a la vez; un segundo POST mientras hay otra en curso es 409 import_already_running. dry_run: true (cuerpo JSON o dry_run=true multipart) hace una vista previa de cualquier fuente: analiza, resuelve, de-duplica, devuelve los mismos recuentos { imported, skipped, errors, unmatched } y luego revierte — no se escribe nada. Límites: cuerpo de 10 MiB y 5.000 historias por importación para las fuentes de archivo (superar cualquiera → 400, sin escribir nada). La fuente GitHub no tiene tope — confirma por bloques en lugar de en una sola transacción. La reimportación es idempotente por id de origen — las filas ya importadas se saltan, no se duplican.

MétodoRutaDescripción
GET/projects/{id}/export/formatsFormatos registrados: { id, name, content_type, drops, includes_archived }. Cualquier rol del proyecto.
GET/projects/{id}/export/{format}Descargar uno (manager). Intercambio: eat (fidelidad completa), jira, pivotal, shortcut, trello, asana, gitlab, linear, plane, plane_json; documentos: pdf, docx.
GET/projects/{id}/export/attachmentsTodos los adjuntos como un único zip navegable (los archivos conservan sus nombres originales; manifiesto en JSON + CSV) (manager).

Las exportaciones de documentos (pdf, docx) toman parámetros de query adicionales: page_size= (letter por defecto / a4 / legal / folio), from= / to= (límites de la ventana de historias — RFC 3339 o YYYY-MM-DD a secas; una historia está en rango cuando su created o completed_at cae dentro), include_icebox= / include_backlog= (ambos false por defecto, de modo que una exportación compartible muestra solo trabajo planificado / en curso). Los formatos CSV de intercambio los ignoran.

Copias de seguridad y restauraciones (manager)

Sección titulada «Copias de seguridad y restauraciones (manager)»
MétodoRutaDescripción
GET / POST/projects/{id}/backups · GET …/backups/{snapshot_id} · …/backups/healthListar snapshots, tomar uno ahora, leer uno, y el resumen de salud de la retención
GET / POST/projects/{id}/restores · POST …/restores/partial · GET …/restores/{restore_id}Restaurar un snapshot completo, o tablas seleccionadas de uno, y consultar el estado de la restauración

Los POST están en el nivel de límite de tasa sensitive (abajo).

East Agile Tracker es un proveedor OAuth 2.1 para clientes MCP. Un cliente lo descubre en /.well-known/oauth-authorization-server y /.well-known/oauth-protected-resource/mcp, te envía a /oauth/authorize (la página de consentimiento), canjea el código en /oauth/token y luego habla MCP en /mcp con el token ea_mcp_* resultante. Las autorizaciones se listan y se revocan en /me/oauth_grants. Los endpoints del proveedor tienen su propio nivel de límite de tasa.

wss://eastagiletracker.com/ws/control?token=<session JWT>

Para control remoto interactivo de la interfaz ({ "action": "get_state", "id": "req-1" }). El token es un JWT de sesión del navegador — una clave de API se rechaza con 401 antes del upgrade. 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. La clave está acotada a la credencial que la envió. No se aplica a GET/HEAD/OPTIONS, a /openapi.json y /docs, a /api/auth/* ni a las subidas multipart en rutas /attachments. Las respuestas que no llegaron a dar una respuesta del dominio nunca se cachean — 401, 403, 404, 429 y todos los 5xx —, de modo que un reintento tras cualquiera de ellas llega al manejador; 400, 409, 412 y 422 son la respuesta del dominio y se reproducen igual que un éxito.

Los endpoints de listado aceptan cursor=<opaque> y limit=<n>. Cuando se establecen, la respuesta es { "items": [...], "next_cursor": "<str|null>" }; pasa next_cursor de vuelta para paginar. El tope de limit depende del endpoint: 200 en historias, comentarios y proyectos; 500 en eventos; 1000 en búsqueda y en el audit log.

Una lista simple (sin cursor/limit) que tuvo que truncar su respuesta lo indica en cabeceras — X-Tracker-Pagination-Truncated, X-Tracker-Pagination-Limit, X-Tracker-Pagination-Offset y X-Tracker-Pagination-Next-Offset; pasa esta última de vuelta como offset= para la página siguiente. No hay cabecera de recuento 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
409stale_write · import_already_runningla historia cambió desde tu expected_updated_at · ya hay una importación en curso
412precondition_failedIf-Match no coincidió con el ETag actual del recurso; details lleva expected y current
413request_too_largeel cuerpo supera el límite de tamaño de la ruta
422invalid_transitionmovimiento de estado ilegal; details lleva { from, to, allowed }
429rate_limiteddemasiadas solicitudes desde esta IP en una ruta con límite de tasa; cabecera Retry-After
500internal_errorfallo del servidor — mensaje genérico; seguro reintentar
503not_configuredal despliegue le falta la integración que necesita esta ruta (SMS, almacenamiento de objetos, …)

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 IP de cliente, en un puñado de rutas; el resto del tráfico autenticado de la API no tiene límite de tasa. Valores por defecto (cada par es la tasa sostenida y la ráfaga, ajustables por el operador):

  • Auth/api/auth/*: 0.5 req/s, ráfaga 20.
  • OAuth provider/oauth/*: 1 req/s, ráfaga 60.
  • Public/api/contact: 0.2 req/s, ráfaga 10.
  • Feedback/api/feedback: tres niveles apilados — un envío cada 15 s, 10 por hora, 36 por día.
  • Avatars — la redirección de avatares sin autenticación: 20 req/s, ráfaga 200.
  • Sensitive — los POST de copia de seguridad y restauración: ~0.002 req/s, ráfaga 5.

Un límite superado devuelve 429 con una cabecera Retry-After y el sobre de error JSON estándar, code: "rate_limited".