Il riferimento completo degli endpoint REST. Per tutorial ed esempi, vedi la Guida API.
Tutto ciò che un member del progetto può fare nell’interfaccia web è disponibile qui — la SPA consuma questa stessa API. Le operazioni che richiedono il ruolo manager sono contrassegnate (manager); tutto il resto richiede solo l’appartenenza al progetto (o, per le letture contrassegnate (viewer), qualsiasi livello di accesso). Le tabelle qui sotto nominano ogni gruppo di route che il server monta; quelli riassunti in una sola riga sono descritti per intero nell’openapi.json live.
https://eastagiletracker.com/api/v1https://api.eastagiletracker.com/api/v1 serve l’API identica. Tutte le richieste e le risposte sono JSON, eccetto alcuni endpoint di upload file che accettano multipart.
Due gruppi stanno un livello più su, sotto /api anziché /api/v1: la superficie di autenticazione (/api/auth/*) e i moduli pubblici (/api/contact, /api/feedback). Le loro grafie /api/v1/… restituiscono 404.
Autenticazione
Sezione intitolata “Autenticazione”Ogni richiesta autenticata invia una credenziale tramite uno di:
X-TrackerToken: <key>Authorization: Bearer <key>
Le chiavi utente iniziano con ea_user_, le chiavi agente con ea_agent_ e i token di accesso MCP con ea_mcp_. Vedi Guida API → Tre tipi di credenziali.
Endpoint non autenticati: /openapi.json, /docs, gli endpoint /api/auth/* e i lookup dei dati di riferimento (/story_types, /story_states, /effort_scales, /priority_scales). /meta è autenticato — funziona qualsiasi chiave valida, ma non ha ambito di progetto (raggiungibile anche da una chiave agente vincolata a un progetto).
Quattro livelli regolano gli endpoint con ambito di progetto:
| Level | Who passes | Typical operations |
|---|---|---|
| public viewer | chiunque, su un progetto la cui visibilità è pubblica | letture della board: storie, iterazioni, ricerca, attività di storie ed epic (con i dettagli dell’attore oscurati) |
| viewer | viewer, member, manager | letture (elenco/get di storie, ricerca, metriche, elenco dei formati di export) |
| member | member, manager | tutte le scritture di work-item (storie, task, commenti, …), lo stream di eventi |
| manager | solo manager | impostazioni progetto, gestione appartenenze, chiavi agente, eliminazione, importazione, download degli export, backup, audit log |
Gli agenti hanno gli stessi ruoli dei membri — viewer, member o manager — con un tetto pari al ruolo del membro che ha generato la chiave. Un non-membro riceve 404 unfound_resource (non 403) sui path dei progetti privati, così gli ID dei progetti non sono enumerabili.
Endpoint auto-descrittivi
Sezione intitolata “Endpoint auto-descrittivi”| Method | Path | Description |
|---|---|---|
| GET | /openapi.json | La spec OpenAPI 3 live, request body inclusi. Non autenticato. |
| GET | /docs | Swagger UI. Non autenticato. |
| GET | /meta | Identità del chiamante (auth.kind/key_id/agent_id/project_id) + il grafo delle transizioni per tipo di storia. Autenticato (qualsiasi chiave valida; non con ambito di progetto). Chiamalo per primo. |
| GET | /api/health · /api/config | Liveness, e la configurazione pubblica del deployment (modalità organizzazione singola, funzionalità opzionali attive, nome dell’istanza). Non autenticati, fuori da /v1. |
Auth (/api/auth/*, fuori da /v1)
Sezione intitolata “Auth (/api/auth/*, fuori da /v1)”Endpoint di sessione, non autenticati salvo diversa indicazione. Li pilota la SPA; gli script di norma usano invece una chiave API.
| Method | Path | Description |
|---|---|---|
| POST | /auth/register | Registra un nuovo account — protetto da reCAPTCHA; l’account supera poi la verifica SMS |
| POST | /auth/sms/challenge · /auth/sms/status · /auth/sms/bypass | Invia / controlla il codice SMS di registrazione (il bypass è riservato all’operatore) |
| GET | /auth/config | Quali metodi di accesso offre il deployment |
| POST | /auth/login | Accedi con email + password; restituisce un JWT di sessione, oppure una sfida TOTP |
| POST | /auth/login/totp | Completa un accesso con un codice dell’app authenticator o un codice di recupero |
| POST | /auth/passkey/login/start · /auth/passkey/login/finish | Accesso WebAuthn senza password |
| GET | /auth/oauth/github/start · /auth/oauth/github/callback · /auth/oauth/google/start · /auth/oauth/google/callback · POST /auth/oauth/exchange | Accesso OAuth con GitHub o Google |
| POST | /auth/refresh · /auth/refresh/revoke | Ruota il refresh token / revocalo |
| POST | /auth/logout | Esci (revoca il refresh token) |
| POST | /auth/forgot-password · /auth/reset-password | Richiedi un’email di reimpostazione / usa il token di reset |
| POST | /auth/accept-invite/lookup · /auth/accept-invite | Risolvi un token di invito → email / accetta l’invito al progetto (dopo l’autenticazione) |
Account / identità
Sezione intitolata “Account / identità”Queste agiscono sul chiamante e richiedono solo una chiave valida (nessun ruolo di progetto).
| Method | Path | Description |
|---|---|---|
| GET | /me | Profilo dell’utente corrente |
| PUT | /me | Aggiorna il profilo |
| DELETE | /me | Elimina l’account — rifiutato finché sei l’unico owner di un’organizzazione o di un progetto con altri membri |
| GET | /me/deletion-impact | Cosa rimuoverebbe l’eliminazione dell’account e cosa la blocca |
| PUT | /me/password | Cambia password |
| PUT | /me/settings | Aggiorna le impostazioni (tema, preferenze di notifica) |
| POST | /me/avatar | Carica l’avatar (multipart) |
| POST | /me/api-token/regenerate | Ruota il tuo token API — invalida sessioni/chiavi esistenti |
| GET | /me/api_keys · POST /me/api_keys · DELETE /me/api_keys/{id} | Gestisci le chiavi API utente (ea_user_) |
| GET | /me/totp · POST /me/totp/setup · /me/totp/verify · /me/totp/disable | Registrazione a due fattori (TOTP); verify restituisce i codici di recupero una sola volta |
| GET | /me/passkeys · POST /me/passkeys/register/start · /me/passkeys/register/finish · DELETE /me/passkeys/{credential_id} | Registrazione e rimozione delle passkey |
| GET | /me/oauth_grants · DELETE /me/oauth_grants/{grant_id} | App connesse — i client MCP e le app OAuth che hai autorizzato |
| GET | /me/activity | La tua attività in tutti i progetti |
| GET | /me/stories | Le storie di cui sei owner, che hai richiesto o che segui in ogni progetto raggiungibile dal token — role=owned|requested|following, state=, cursor= / limit= (max 200) |
| GET | /me/mentions · POST /me/mentions/{mention_id}/ack | La casella delle @-menzioni (unacked=true per filtrare) e la conferma di lettura — confluisce anche nel feed di notifiche più sotto |
| GET | /me/data-export | Auto-esportazione GDPR dei tuoi dati |
| GET | /me/consent · POST /me/consent | Leggi / registra il consenso ({ consent_type, granted }) |
| GET | /legal/pending · POST /legal/accept | Documenti clickwrap in sospeso / registra l’accettazione |
| GET / PUT | /agent/me | L’identità e il profilo di una chiave agente, leggibili e modificabili dall’agente stesso (la controparte lato agente di /me) |
| POST | /api/contact · /api/feedback · /api/feedback/with-screenshot | Contatto + feedback in-app. Fuori da /v1; a frequenza limitata per IP |
Dati di riferimento (non autenticati)
Sezione intitolata “Dati di riferimento (non autenticati)”Lookup di seed usati quando si creano/stimano storie. ID stabili.
| Method | Path | Description |
|---|---|---|
| GET | /story_types | feature, bug, chore, release (+ allow_points) |
| GET | /story_states | unstarted … accepted, rejected |
| GET | /effort_scales | scale di stima disponibili |
| GET | /effort_scales/{scale_id}/values | i valori di punti in una scala |
| GET | /priority_scales · /priority_scales/{scale_id}/values | le scale di priorità e i loro valori (il priority_id di una storia si risolve qui) |
Organizzazioni
Sezione intitolata “Organizzazioni”Solo servizio hosted — un’installazione self-hosted gira in modalità organizzazione singola e non monta questi endpoint (tranne l’elenco delle organizzazioni). I ruoli sono ruoli di organizzazione: owner, admin, member.
| Method | Path | Description |
|---|---|---|
| GET / POST | /organizations | Elenca le tue organizzazioni / creane una |
| GET / PUT / DELETE | /organizations/{oid} | Leggi, rinomina (nome + slug; owner o admin), elimina |
| GET / POST | /organizations/{oid}/memberships · PUT / DELETE …/memberships/{member_id} | Membri e inviti; gli inviti hanno un tetto di ruolo (mai sopra quello del chiamante; il ruolo owner non si invita mai) |
| POST | /organizations/{oid}/memberships/bulk-role · …/memberships/bulk-remove | Cambia il ruolo di un massimo di 200 membri in una volta o rimuovili. Tutto o niente: un batch che rimuoverebbe l’ultimo owner o lascerebbe un progetto senza proprietario viene rifiutato per intero; con reassign_confirmed diventi invece proprietario di quei progetti |
| DELETE | /organizations/{oid}/invitations/{invitation_id} | Revoca un invito in sospeso |
| POST | /organizations/{oid}/transfer-ownership | Passa il ruolo owner a un altro membro |
| PUT | /organizations/{oid}/memberships/{member_id}/anonymization | Maschera nome / email / avatar di un membro su tutta l’organizzazione |
| GET | /organization-invitations/{token} · POST …/{token}/accept | Risolvi / accetta un invito all’organizzazione ricevuto via email |
| POST | /organizations/{oid}/export · GET / POST …/export/jobs · GET …/export/jobs/{job_id} · …/export/jobs/{job_id}/download | Export dell’organizzazione riservato all’owner: uno zip con un dump SQL e ogni allegato, eseguito come job |
Progetti
Sezione intitolata “Progetti”| Method | Path | Description |
|---|---|---|
| GET | /projects | Elenca i tuoi progetti (limit ≤ 200) |
| POST | /projects | Crea un progetto |
| GET | /projects/{id} | Ottieni i dettagli del progetto (viewer) |
| PUT | /projects/{id} | Aggiorna le impostazioni del progetto (manager) |
| DELETE | /projects/{id} | Elimina un progetto (manager) |
| POST | /projects/{id}/pin | Fissa / sblocca il progetto nel tuo elenco progetti |
| POST | /projects/{id}/transfer-organization | Sposta il progetto in un’altra organizzazione (manager) |
| POST | /projects/{id}/slack/test | Invia un messaggio di prova al feed Slack del progetto (manager) |
| GET / POST | /projects/{id}/showcase_claim_eligibility · /projects/{id}/showcase_claim · /projects/{id}/showcase_seed | Progetti vetrina pubblici: verifica se puoi rivendicarne uno, rivendicalo, popolalo |
| GET | /projects/{id}/audit-log | Lettura dell’audit log — cronologia del progetto più attività per storia / per epic tramite surface=; l’accesso varia in base al surface, vedi sotto |
| GET | /projects/{id}/events | Stream di eventi paginato a cursore (member) — vedi Events |
Parametri di query dell’audit log: event_type= (un tipo o elenco separato da virgole), limit= (≤ 1000), before= (cursore keyset, created_at ISO-8601), surface= (project_history, story_activities, epic_activities), target_id= (l’id della storia/epic — richiesto quando surface=story_activities o epic_activities). Accesso: il log non filtrato e surface=project_history sono (manager); story_activities / epic_activities sono leggibili da qualsiasi membro del progetto, e in forma anonima sui progetti pubblici con i dati personali dell’attore oscurati.
Membri, agenti e chiavi agente
Sezione intitolata “Membri, agenti e chiavi agente”| Method | Path | Description |
|---|---|---|
| GET | /projects/{id}/memberships | Elenca i membri (viewer) |
| POST | /projects/{id}/memberships | Invita un membro via email (manager) |
| PUT | /projects/{id}/memberships/{mid} | Aggiorna il ruolo (manager) |
| DELETE | /projects/{id}/memberships/{mid} | Rimuovi un membro (manager) |
| GET | /projects/{id}/addable-members · POST /projects/{id}/members/add-existing | Membri dell’organizzazione non ancora nel progetto / aggiungine uno senza invito via email (manager) |
| POST | /projects/{id}/members/join | Un owner o admin dell’organizzazione entra come manager in un progetto della sua organizzazione, o si promuove a manager (l’azione Make me owner nell’elenco progetti) |
| PUT | /projects/{id}/members/{mid}/anonymization | Maschera nome / email / avatar di un membro su questo progetto (manager) |
| GET / POST | /projects/{id}/agent_keys | Elenca / genera chiavi agente — i manager, oppure i ruoli ammessi dalla policy dei ruoli creatori del progetto |
| DELETE | /projects/{id}/agent_keys/{kid} | Revoca una chiave agente |
| GET | /projects/{id}/agent_keys/onboarding | Il bundle di onboarding: prompt e file di configurazione per i client agente più comuni |
| GET | /projects/{id}/agents · GET / PUT /projects/{id}/agents/{aid} | Gli agenti del progetto e i loro profili (nome, iniziali, descrizione, colore) |
| POST | /projects/{id}/agents/{aid}/rotate-key · /projects/{id}/agents/{aid}/avatar | Ruota la chiave di un agente (identità e cronologia conservate) / carica il suo avatar |
Tutte le scritture sulle storie richiedono il ruolo member.
| Method | Path | Description |
|---|---|---|
| GET | /projects/{id}/stories | Elenca le storie (paginato, filtrabile) (viewer) |
| POST | /projects/{id}/stories | Crea una storia |
| GET | /projects/{id}/stories/{sid} | Ottieni una storia (viewer) |
| PUT | /projects/{id}/stories/{sid} | Aggiorna una storia |
| DELETE | /projects/{id}/stories/{sid} | Elimina una storia |
| POST | /projects/{id}/stories/{sid}/transitions | Cambia stato con validazione |
| POST | /projects/{id}/stories/{sid}/reject · /projects/{id}/stories/{sid}/restart | Rifiuta una storia delivered / riporta una storia rifiutata a started (rejected è terminale per /transitions) |
| POST | /projects/{id}/stories/{sid}/archive · /projects/{id}/stories/{sid}/unarchive | Archivia / rimuovi dall’archivio una storia |
| POST | /projects/{id}/stories/bulk_transition | Fai transitare molte storie (1–100) in una volta |
| POST | /projects/{id}/stories/bulk-archive · bulk-delete · bulk-duplicate · bulk-move | Archivia, elimina, duplica o sposta (su un pannello / in una posizione) molte storie |
| POST | /projects/{id}/stories/{sid}/duplicate | Duplica una storia |
| GET / POST / DELETE | /projects/{id}/stories/{sid}/epics · …/epics/{eid} | L’appartenenza della storia agli epic |
| GET | /short-links/{code} · /story-references | Risolvi uno short link /s/<code> nella sua storia / risolvi fino a 100 riferimenti a storie (#id, URL) nelle storie leggibili dal chiamante |
Parametri di query dell’elenco storie: archived= (exclude default / include / only — il filtro degli archiviati a tre stati; sostituisce il deprecato include_archived=true, che ora è un alias di archived=include), include_done=true (ammette le storie del pannello Done congelate su iterazioni passate, escluse per default). La paginazione (cursor= / limit= / offset=) e i set di campi parziali (fields=) seguono Paginazione e Proiezione dei campi.
Create (POST …/stories): { "name" (required), "story_type": "feature|bug|chore|release", "description"?, "estimate"?, "current_state"?, "icebox"?, "labels"? }. estimate è l’etichetta del valore della scala, come stringa ("3", "13"); un numero JSON viene rifiutato. labels accetta ["auth"] o [{ "name": "auth" }]; le label sconosciute vengono create. Default: story_type=feature, current_state=unstarted.
Update (PUT …/stories/{sid}): stessi campi, tutti opzionali, più "position" (float), "force_state_change" (bool) e "expected_updated_at" (RFC 3339 — il salvataggio di una descrizione viene rifiutato con 409 stale_write se la storia è cambiata da quando l’hai letta). Le scritture sulle storie onorano anche If-Match rispetto all’ETag della storia; una discrepanza dà 412 precondition_failed.
Transition (POST …/transitions): { "to": "<state>" }. Il campo è to. Restituisce { story_id, state }. Movimento illegale → 422 invalid_transition con details: { from, to, allowed }.
Bulk transition (POST …/bulk_transition): { "story_ids": [int,…] (1–100), "to": "<state>" }. Ogni storia è giudicata indipendentemente; restituisce { results: [ { id, status: "ok" } | { id, status: "failed", error } ] }.
Sotto-risorse della storia
Sezione intitolata “Sotto-risorse della storia”Tutte member. List/GET sulla maggior parte è (viewer).
| Method | Path | Body / notes |
|---|---|---|
| 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) } oppure { comment_emoji }. GET accetta fields= (allowlist: comment_id, story_comment_id, story_id, comment_text, comment_emoji, member, created) più 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_type ∈ relates_to, duplicates, blocks, is_blocked_by, pull_request, branch, other; gli URL GitHub /pull/ e /tree/ vengono tipizzati automaticamente |
| GET / POST | /projects/{id}/stories/{sid}/reviews · PUT/DELETE …/reviews/{rid} | Creazione: { reviewer_id? / reviewer_agent_id?, comment? } — ometti entrambi per assegnare te stesso. Aggiornamento: { status, comment? } |
| GET / POST | /projects/{id}/stories/{sid}/owners · DELETE …/owners/{mid} · DELETE …/owners/agents/{aid} | { member_id? / agent_id? } — ometti entrambi per aggiungere il chiamante |
| 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} | upload multipart — video ≤ 200 MB, PDF / Word / Excel ≤ 25 MB, immagini / CSV / testo ≤ 10 MB; l’elenco è (viewer) |
| GET / POST | /projects/{id}/stories/{sid}/link-attachments · DELETE …/link-attachments/{laid} | Allegati-link — un URL esterno tenuto accanto agli allegati file anziché come link al codice |
| GET | /attachments/{token} · /api/avatars/{token} | Letture indirizzate da token di un allegato o di un avatar — gli URL che l’API restituisce; nessun X-TrackerToken necessario |
Stessa forma delle storie, senza la macchina a stati. member per le scritture, (viewer) per le letture.
| Method | Path | Description |
|---|---|---|
| GET / POST | /projects/{id}/epics · GET / PUT / DELETE …/epics/{eid} | Gli epic hanno un nome, una descrizione Markdown e una label di supporto che unisce le loro storie |
| GET / POST / PUT / DELETE | …/epics/{eid}/comments · …/comments/{cid} | Commenti agli epic |
| GET / POST / DELETE | …/epics/{eid}/owners · …/followers (+ varianti /agents/{aid}) | Owner e follower, membri o agenti — gli owner di un epic si propagano alle sue storie |
| GET / POST / DELETE | …/epics/{eid}/attachments (+ /json) · …/link-attachments | Allegati, stessi limiti delle storie |
| GET | /projects/{id}/analytics/epics · …/analytics/epics/{eid} | Avanzamento per epic: burnup, throughput, salute, previsione (viewer) |
member per le scritture, (viewer) per le letture.
| Method | Path | Description |
|---|---|---|
| GET / POST | /projects/{id}/labels | Elenca / crea una label |
| PUT / DELETE | /projects/{id}/labels/{lid} | Aggiorna / elimina una label |
| POST | /projects/{id}/labels/{lid}/archive | Archivia (nascondi in modo soft) una label |
Iterazioni
Sezione intitolata “Iterazioni”Le letture sono aperte a qualsiasi ruolo di progetto, e anonime su un progetto pubblico.
| Method | Path | Description |
|---|---|---|
| GET | /projects/{id}/iterations | Elenca le iterazioni (≤ 500 per pagina; porta un ETag e gli header di continuazione X-Tracker-Pagination-* quando è troncato) |
| GET | /projects/{id}/iterations/{itid} | Una singola iterazione |
| GET | /projects/{id}/iterations/first-preview | Le date che riceverebbe la prima iterazione, mostrate nella conferma di creazione |
| POST | /projects/{id}/iterations | Crea un’iterazione manuale (member) |
| DELETE | /projects/{id}/iterations/{itid} | Elimina un’iterazione (manager) |
| PUT | /projects/{id}/iterations/{itid}/velocity | Sovrascrivi la velocity di una singola iterazione senza cambiare la strategia del progetto (manager) |
| GET | /projects/{id}/iterations/{itid}/done-stories | Le storie accettate di un’iterazione chiusa, paginate |
Ricerca, metriche, preferenze
Sezione intitolata “Ricerca, metriche, preferenze”| Method | Path | Description |
|---|---|---|
| GET | /projects/{id}/search?q=… | Ricerca potente — full-text + qualificatori di faccetta / intervallo di date / persone (DSL in stile GitHub); restituisce { results, total, limit, offset }. query è alias di q; limit= (default 50, max 1000) / offset= paginano; sort= ordina per relevance (default), created, created_asc, state o updated. (viewer) — vedi la Guida |
| GET | /projects/{id}/metrics/{velocity,burndown,story-types,contributors} | Le serie della pagina Metrics (viewer); le metriche degli epic sono sotto /analytics/epics, più sopra |
| GET | /projects/{id}/backlog/grouping | I gruppi di iterazioni proiettati dal Backlog (viewer) |
| GET / PUT | /projects/{id}/preferences | Le tue preferenze di board per questo progetto — qualsiasi ruolo di progetto, solo la tua riga |
| Method | Path | Description |
|---|---|---|
| GET | /projects/{id}/events | Stream di eventi paginato a cursore (member) — i viewer ricevono 403 |
Parametri di query: since=<event_id>, types=story.created,story.transitioned,comment.added,…, limit= (≤ 500), cursor=. La risposta include next_cursor. Passa l’ultimo event_id che hai visto come since per riprendere.
Notifiche
Sezione intitolata “Notifiche”Il feed unificato delle notifiche in-app: righe di notifica di prima classe (richieste di review, attività delle storie, inviti, …) unite alla casella delle @-menzioni in un unico flusso, dal più recente. Gli id del feed hanno un prefisso di origine (nt-… / sc-… / ec-…). Le sessioni membro e le chiavi ea_user_* leggono le proprie righe lato membro; le chiavi ea_agent_* le righe lato agente.
| Method | Path | Description |
|---|---|---|
| GET | /me/notifications | Il tuo feed di notifiche. Filtri: unread=true, since_id=, kind= (mentions / reviews / stories / invitations); pagina con cursor= / limit= |
| GET | /me/notifications/unread-count | Totali non letti — { unread_count, by_category: { mentions, reviews, stories, invitations } } |
| POST | /me/notifications/read-all | Segna tutto come letto; restituisce i contatori aggiornati |
| POST | /me/notifications/{id}/ack | Segna un elemento come letto (idempotente) |
| POST | /me/notifications/{id}/accept | Accetta un invito a un progetto / organizzazione dal feed (solo token membro) |
| POST | /me/notifications/{id}/decline | Rifiuta un invito a un progetto / organizzazione (solo token membro) |
| GET | /me/notifications/resolve-invite?token=… | Risolvi un token di invito ricevuto via email nell’id della tua notifica — { "id": "nt-…" } oppure { "id": null } |
| GET | /me/notifications/stream | Push live — Server-Sent Events (text/event-stream); vedi sotto |
L’endpoint di stream non è un endpoint JSON e quindi non è nella specifica OpenAPI: tiene la connessione aperta ed emette un frame senza payload ({"type":"notification","kind":…}) ogni volta che arriva qualcosa di nuovo, segnalando al client di ricaricare il feed. Le connessioni hanno un tetto lato server di 45 minuti — riconnettiti e ri-autenticati. Solo sessioni membro e chiavi ea_user_*; le chiavi ea_agent_* ricevono 403.
Importazione (manager)
Sezione intitolata “Importazione (manager)”| Method | Path | Description |
|---|---|---|
| POST | /projects/{id}/import | Sorgenti da file: source= ∈ pivotal, jira, asana, gitlab, shortcut, trello, linear, plane, plane_json, eat. Multipart file=. Sincrono — risponde con i conteggi del risultato. |
| POST | /projects/{id}/import/json | Body JSON; source=github non richiede file — owner, repo, token opzionale, e i flag opt-in include_pull_requests / include_milestones / include_releases / include_dependencies; le sorgenti da file inviano file_base64. Asincrono: restituisce 202 { import_id, status }. Il server recupera tramite l’API GraphQL di GitHub, che rifiuta i chiamanti anonimi, quindi a GitHub arriva sempre un token — il tuo, o quello condiviso del deployment. Vedi la Guida. |
| GET | /projects/{id}/imports/{import_id} | Interroga un job: status percorre pending → fetching → writing → done | failed, con progress_current / progress_total durante il recupero e i conteggi del risultato su done |
Per ogni progetto gira una sola importazione alla volta; un secondo POST mentre una è in corso dà 409 import_already_running. dry_run: true (body JSON oppure dry_run=true multipart) fa l’anteprima di qualsiasi sorgente: analizza, risolve, deduplica, restituisce gli stessi conteggi { imported, skipped, errors, unmatched }, poi annulla tutto — non viene scritto nulla. Limiti: 10 MiB di body e 5.000 storie per importazione per le sorgenti a file (oltre l’uno o l’altro → 400, nulla scritto). La sorgente GitHub non ha tetto — scrive a blocchi anziché in un’unica transazione. La reimportazione è idempotente per id di origine — le righe già importate vengono saltate, non duplicate.
Esportazione
Sezione intitolata “Esportazione”| Method | Path | Description |
|---|---|---|
| GET | /projects/{id}/export/formats | Formati registrati: { id, name, content_type, drops, includes_archived }. Qualsiasi ruolo di progetto. |
| GET | /projects/{id}/export/{format} | Scarica uno (manager). Interscambio: eat (piena fedeltà), jira, pivotal, shortcut, trello, asana, gitlab, linear, plane, plane_json; documenti: pdf, docx. |
| GET | /projects/{id}/export/attachments | Ogni allegato come un unico zip navigabile (i file conservano i nomi originali; manifest JSON + CSV) (manager). |
Le esportazioni di documenti (pdf, docx) accettano parametri di query aggiuntivi: page_size= (letter default / a4 / legal / folio), from= / to= (estremi della finestra di storie — RFC 3339 oppure semplice YYYY-MM-DD; una storia è nell’intervallo quando il suo created o completed_at vi cade dentro), include_icebox= / include_backlog= (entrambi false per default, così un’esportazione condivisibile mostra solo il lavoro pianificato / in corso). I formati CSV di interscambio li ignorano.
Backup e ripristini (manager)
Sezione intitolata “Backup e ripristini (manager)”| Method | Path | Description |
|---|---|---|
| GET / POST | /projects/{id}/backups · GET …/backups/{snapshot_id} · …/backups/health | Elenca gli snapshot, creane uno subito, leggine uno, e il riepilogo dello stato di ritenzione |
| GET / POST | /projects/{id}/restores · POST …/restores/partial · GET …/restores/{restore_id} | Ripristina uno snapshot intero, o tabelle selezionate da uno, e interroga il ripristino |
Questi POST stanno sul livello di limitazione di frequenza sensitive (più sotto).
MCP e provider OAuth
Sezione intitolata “MCP e provider OAuth”East Agile Tracker è un provider OAuth 2.1 per i client MCP. Un client lo scopre su /.well-known/oauth-authorization-server e /.well-known/oauth-protected-resource/mcp, ti manda su /oauth/authorize (la pagina di consenso), scambia il codice su /oauth/token, e poi parla MCP su /mcp con il token ea_mcp_* ottenuto. Le autorizzazioni si elencano e si revocano su /me/oauth_grants. Gli endpoint del provider hanno un proprio livello di limitazione di frequenza.
WebSocket
Sezione intitolata “WebSocket”wss://eastagiletracker.com/ws/control?token=<session JWT>Per il controllo remoto interattivo dell’interfaccia ({ "action": "get_state", "id": "req-1" }). Il token è un JWT di sessione del browser — una chiave API viene rifiutata con 401 prima dell’upgrade. Non è un canale dati — tutte le letture/scritture passano per REST. Solo istanza singola; non distribuito tra le repliche.
Idempotenza
Sezione intitolata “Idempotenza”Gli endpoint di scrittura (POST, PUT, DELETE) accettano un header Idempotency-Key. Stessa chiave + stesso body riproduce la risposta in cache (finestra di 24 ore); stessa chiave + un body diverso restituisce 409 idempotency_conflict. La chiave ha come ambito la credenziale che l’ha inviata. Non applicata a GET/HEAD/OPTIONS, /openapi.json e /docs, /api/auth/*, né agli upload multipart sui path /attachments. Le risposte che si sono fermate prima di una risposta di dominio non vengono mai messe in cache — 401, 403, 404, 429 e ogni 5xx — così un retry dopo una qualsiasi di esse raggiunge l’handler; 400, 409, 412 e 422 sono la risposta del dominio e vengono riprodotte come un successo.
Paginazione
Sezione intitolata “Paginazione”Gli endpoint di elenco accettano cursor=<opaque> e limit=<n>. Quando impostato, la risposta è { "items": [...], "next_cursor": "<str|null>" }; ripassa next_cursor per paginare. Il tetto di limit è per endpoint: 200 su storie, commenti e progetti; 500 sugli eventi; 1000 sulla ricerca e sull’audit log.
Un elenco semplice (senza cursor/limit) che ha dovuto troncare la risposta lo dichiara negli header — X-Tracker-Pagination-Truncated, X-Tracker-Pagination-Limit, X-Tracker-Pagination-Offset e X-Tracker-Pagination-Next-Offset; restituisci l’ultimo come offset= per la pagina successiva. Non esiste un header con il conteggio totale.
Proiezione dei campi
Sezione intitolata “Proiezione dei campi”Gli endpoint di elenco accettano fields= (separati da virgola) per restituire solo campi specifici. story_id è sempre incluso; un nome di campo sconosciuto restituisce 400 validation_failed con i nomi offendenti in details.fields.
GET /projects/123/stories?fields=story_id,name,current_state,ownersFormato degli errori
Sezione intitolata “Formato degli errori”Ogni errore JSON ha code ed error; alcuni aggiungono details:
{ "code": "invalid_transition", "error": "Cannot move story from `unstarted` to `accepted`", "details": { "from": "unstarted", "to": "accepted", "allowed": ["started"] } }| Status | code | When |
|---|---|---|
| 400 | invalid_parameter | input errato; messaggio in error, nessun details (la maggior parte delle validazioni: vuoto/lunghezza/null-byte/email) |
| 400 | validation_failed | errore di input strutturato; details.fields è un array di nomi di campo offendenti |
| 401 | unauthenticated | token mancante/non valido |
| 403 | unauthorized_operation | autenticato ma con ruolo insufficiente |
| 404 | unfound_resource | non trovato — restituito anche ai non-membri |
| 409 | conflict | conflitto di risorsa (es. duplicato) |
| 409 | idempotency_conflict | Idempotency-Key riutilizzato con un body diverso |
| 409 | stale_write · import_already_running | la storia è cambiata dal tuo expected_updated_at · un’importazione è già in corso |
| 412 | precondition_failed | If-Match non corrispondeva all’ETag attuale della risorsa; details porta expected e current |
| 413 | request_too_large | il body supera il limite di dimensione della route |
| 422 | invalid_transition | movimento di stato illegale; details porta { from, to, allowed } |
| 429 | rate_limited | troppe richieste da questo IP su una route a frequenza limitata; header Retry-After |
| 500 | internal_error | guasto del server — messaggio generico; sicuro da riprovare |
| 503 | not_configured | al deployment manca l’integrazione richiesta da questa route (SMS, object storage, …) |
details.fields è un array JSON di nomi di campo (es. ["to"]), a volte con chiavi extra come max. Non esiste una mappa campo→messaggio.
{ "code": "validation_failed", "error": "unknown field(s): foo", "details": { "fields": ["foo"] } }Limiti di frequenza
Sezione intitolata “Limiti di frequenza”Per IP client, su una manciata di route; il restante traffico API autenticato non è a frequenza limitata. Default (ogni coppia è la frequenza sostenuta e il burst, regolabili dall’operatore):
- Auth —
/api/auth/*: 0.5 req/s, burst 20. - OAuth provider —
/oauth/*: 1 req/s, burst 60. - Public —
/api/contact: 0.2 req/s, burst 10. - Feedback —
/api/feedback: tre livelli sovrapposti — un invio ogni 15 s, 10 all’ora, 36 al giorno. - Avatars — il redirect non autenticato degli avatar: 20 req/s, burst 200.
- Sensitive — i
POSTdi backup e ripristino: ~0.002 req/s, burst 5.
Un limite superato restituisce 429 con un header Retry-After e l’envelope di errore JSON standard, code: "rate_limited".