Ir al contenido

Especificación de la API

Referencia completa de los endpoints REST de la API de East Agile Tracker: autenticación y roles, proyectos, historias, épicas, iteraciones, eventos, importar y exportar.

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://api.eastagiletracker.com/api/v1

La API tiene su propio host: eastagiletracker.com sirve la aplicación web y responde a /api/v1/… con 404. 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 el formulario público de contacto (/api/contact). 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 (autenticado; revoca el refresh token de esta sesión)
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)
POST/auth/oauth/link/preview · …/link/confirm · /auth/oauth/merge/preview · …/merge/confirmConectar una cuenta de GitHub o Google a una existente, o fusionar dos cuentas
POST/auth/email_addresses/primary/preview · …/primary/confirmCompletar un cambio de correo principal desde el enlace de confirmación enviado por correo

Estos actúan sobre el que llama y no necesitan rol de proyecto. Necesitan una identidad de miembro — una clave de agente recibe 401 (los agentes usan /agent/me). Las llamadas que cambian credenciales — contraseña, TOTP, passkeys, eliminación de la cuenta, un cambio de correo, regeneración del token — necesitan una sesión de navegador; una clave de API recibe 403.

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 / DELETE/me/avatarSubir (multipart) / quitar tu avatar
POST/me/api-token/regenerateCerrar todas las sesiones y devolver un token de sesión nuevo; las claves de API no se ven afectadas
GET / POST/me/email_addresses · GET …/email_addresses/offers · PUT …/email_addresses/primaryTus direcciones de correo; adoptar la dirección de un proveedor conectado como alternativa; solicitar un cambio de la principal (confirmado por correo)
GET/me/oauth_identities · DELETE …/oauth_identities/{provider} · POST …/oauth_identities/{provider}/linkCuentas de inicio de sesión conectadas (GitHub, Google): listar, desconectar, iniciar una vinculación
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/v1/feedback · /api/v1/feedback/with-screenshotContacto + feedback en la aplicación; 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
GET/organization-invitations/{token} · POST …/{token}/acceptResolver / aceptar una invitación de organización recibida por correo
GET/organizations/{oid}/export · 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 / DELETE/projects/{id}/pinFijar / desfijar el proyecto en tu lista de proyectos
POST/projects/{id}/transfer-organizationMover el proyecto a otra organización — quien llama debe ser Gestor del proyecto y owner o admin de la organización de destino
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 (sembrar es para los administradores del producto; cualquier otro recibe 404)
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= (50 por defecto, acotado a 1000), before= (cursor keyset <ISO-8601>|<audit_id> — pasa el next_cursor de la página anterior), 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 es (manager); surface=project_history es (viewer), con los correos de los miembros censurados cuando el proyecto los oculta; 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)
POST/projects/{id}/memberships/bulk-role · …/memberships/bulk-removeCambiar el rol de muchos miembros a la vez, o eliminarlos (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) y owner o admin de la organización
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)
GET / POST/projects/{id}/agent_keysListar (manager) / emitir claves de agente — las personas cuyo rol está en la política de roles creadores del proyecto; las claves de agente no pueden emitir
PUT / DELETE/projects/{id}/agent_keys/{kid}Editar el nombre, el rol o las pistas de respuesta de una clave / revocarla (manager)
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 / DELETE /projects/{id}/agents/{aid}Los agentes del proyecto y sus perfiles (nombre, iniciales, descripción, color); DELETE revoca el agente y todas sus claves — un manager o el creador del agente
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. Crear una historia con fechas retroactivas, procedencia de importación (import_source / import_external_id) u owners o solicitante externos necesita (manager).

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-unarchiveDesarchivar muchas historias
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
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 — codes= (códigos de enlace corto separados por comas) e ids= (project_id:story_id separados por comas) — a las historias que quien 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_version" (entero, la version de la historia — el guardado de una descripción se rechaza con 409 stale_write si la descripción cambió después de esa versión). El antiguo "expected_updated_at" (RFC 3339) está obsoleto y funciona una versión más: enviado con una descripción y sin expected_version, el guardado se rechaza con 409 stale_write salvo que coincida con el updated_at de la historia. 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). Eliminar un adjunto o un adjunto de enlace necesita a quien lo subió o (manager).

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}Crear: { url, link_type?, title? }; actualizar: { url, title? } — link_type ∈ relates_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}{ label_id } — una etiqueta existente del proyecto; crea etiquetas con POST /projects/{id}/labels, o envía labels en el PUT de la historia
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/v1/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 y para las listas de comentarios, owners y seguidores; (viewer) para la lista de epics, un epic individual y las listas de adjuntos.

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
GET / PUT / DELETE/projects/{id}/labels/{lid}Leer / 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-previewLa ventana, la velocidad proyectada y el llenado desde el Backlog de la iteración que crearía una escritura inicial, o de la iteración actual si existe (member)
POST/projects/{id}/iterationsCrear una iteración manual (manager)
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_id=<event_id> (since es el alias antiguo; since_id prevalece si se envían ambos), types=story.created,story.transitioned,comment.added,…, limit= (100 por defecto, ≤ 500), cursor=. La respuesta es { items, next_since_id, next_cursor }: guarda next_since_id y pásalo de vuelta como since_id para reanudar; next_cursor es null en la última página.

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, y include_comments (por defecto true); 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 JSON por proyecto a la vez; un segundo POST …/import/json mientras hay otra en curso es 409 import_already_running. Con check_unpointed_estimates: true (fuentes de archivo; se ignora para github), un archivo con estimaciones de bugs o tareas que el proyecto no puntúa se rechaza con 409 import_unpointed_estimates y details.count antes de iniciar ningún trabajo; sin la bandera, la importación descarta esas estimaciones y reporta { code: "estimates_unpointed_dropped", count } en los warnings del resultado. 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: un archivo de 10 MiB (400 import_too_large) y 5.000 historias por importación para las fuentes de archivo (400 import_too_many_records); no se escribe nada. Un cuerpo de solicitud de más de 16 MiB se rechaza antes con 413 request_too_large. 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
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.

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/*, a /mcp, a ninguna ruta que contenga /attachments ni a ninguna solicitud multipart/*; una solicitud sin credencial nunca se cachea. 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 el audit log. La búsqueda pagina con limit (≤ 1000) y offset en lugar de un cursor y devuelve { results, total, limit, offset }.

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 listados de historias, comentarios y proyectos aceptan fields= (separados por comas) para devolver solo campos específicos. El id del recurso siempre se incluye (story_id, story_comment_id y comment_id, project_id); 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. La validación de campos (en blanco/longitud/byte nulo/email) añade details.fields y details.constraint; solo un error de solicitud incorrecta simple omite details
400validation_failederror de entrada estructurado; details.fields es un array de los nombres de los campos infractores
400códigos de dominioun rechazo propio de la ruta con su propio código y sin details — p. ej. unsupported_export_format, import_too_large, import_unknown_source
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_running · import_unpointed_estimatesla descripción cambió después de tu expected_version (o expected_updated_at ya no coincide) · ya hay una importación en curso · el archivo tiene estimaciones de bugs o tareas que el proyecto no puntúa (details.count)
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
502upstream_errorfalló un servicio del que depende la ruta (p. ej. GitHub)
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/v1/feedback y /api/v1/feedback/with-screenshot: 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".