Salta ai contenuti

Specifica API

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/v1

https://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.

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:

LevelWho passesTypical operations
public viewerchiunque, su un progetto la cui visibilità è pubblicaletture della board: storie, iterazioni, ricerca, attività di storie ed epic (con i dettagli dell’attore oscurati)
viewerviewer, member, managerletture (elenco/get di storie, ricerca, metriche, elenco dei formati di export)
membermember, managertutte le scritture di work-item (storie, task, commenti, …), lo stream di eventi
managersolo managerimpostazioni 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.

MethodPathDescription
GET/openapi.jsonLa spec OpenAPI 3 live, request body inclusi. Non autenticato.
GET/docsSwagger UI. Non autenticato.
GET/metaIdentità 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/configLiveness, e la configurazione pubblica del deployment (modalità organizzazione singola, funzionalità opzionali attive, nome dell’istanza). Non autenticati, fuori da /v1.

Endpoint di sessione, non autenticati salvo diversa indicazione. Li pilota la SPA; gli script di norma usano invece una chiave API.

MethodPathDescription
POST/auth/registerRegistra un nuovo account — protetto da reCAPTCHA; l’account supera poi la verifica SMS
POST/auth/sms/challenge · /auth/sms/status · /auth/sms/bypassInvia / controlla il codice SMS di registrazione (il bypass è riservato all’operatore)
GET/auth/configQuali metodi di accesso offre il deployment
POST/auth/loginAccedi con email + password; restituisce un JWT di sessione, oppure una sfida TOTP
POST/auth/login/totpCompleta un accesso con un codice dell’app authenticator o un codice di recupero
POST/auth/passkey/login/start · /auth/passkey/login/finishAccesso WebAuthn senza password
GET/auth/oauth/github/start · /auth/oauth/github/callback · /auth/oauth/google/start · /auth/oauth/google/callback · POST /auth/oauth/exchangeAccesso OAuth con GitHub o Google
POST/auth/refresh · /auth/refresh/revokeRuota il refresh token / revocalo
POST/auth/logoutEsci (revoca il refresh token)
POST/auth/forgot-password · /auth/reset-passwordRichiedi un’email di reimpostazione / usa il token di reset
POST/auth/accept-invite/lookup · /auth/accept-inviteRisolvi un token di invito → email / accetta l’invito al progetto (dopo l’autenticazione)

Queste agiscono sul chiamante e richiedono solo una chiave valida (nessun ruolo di progetto).

MethodPathDescription
GET/meProfilo dell’utente corrente
PUT/meAggiorna il profilo
DELETE/meElimina l’account — rifiutato finché sei l’unico owner di un’organizzazione o di un progetto con altri membri
GET/me/deletion-impactCosa rimuoverebbe l’eliminazione dell’account e cosa la blocca
PUT/me/passwordCambia password
PUT/me/settingsAggiorna le impostazioni (tema, preferenze di notifica)
POST/me/avatarCarica l’avatar (multipart)
POST/me/api-token/regenerateRuota 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/disableRegistrazione 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/activityLa tua attività in tutti i progetti
GET/me/storiesLe 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}/ackLa casella delle @-menzioni (unacked=true per filtrare) e la conferma di lettura — confluisce anche nel feed di notifiche più sotto
GET/me/data-exportAuto-esportazione GDPR dei tuoi dati
GET/me/consent · POST /me/consentLeggi / registra il consenso ({ consent_type, granted })
GET/legal/pending · POST /legal/acceptDocumenti clickwrap in sospeso / registra l’accettazione
GET / PUT/agent/meL’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-screenshotContatto + feedback in-app. Fuori da /v1; a frequenza limitata per IP

Lookup di seed usati quando si creano/stimano storie. ID stabili.

MethodPathDescription
GET/story_typesfeature, bug, chore, release (+ allow_points)
GET/story_statesunstarted … accepted, rejected
GET/effort_scalesscale di stima disponibili
GET/effort_scales/{scale_id}/valuesi valori di punti in una scala
GET/priority_scales · /priority_scales/{scale_id}/valuesle scale di priorità e i loro valori (il priority_id di una storia si risolve qui)

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.

MethodPathDescription
GET / POST/organizationsElenca 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-removeCambia 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-ownershipPassa il ruolo owner a un altro membro
PUT/organizations/{oid}/memberships/{member_id}/anonymizationMaschera nome / email / avatar di un membro su tutta l’organizzazione
GET/organization-invitations/{token} · POST …/{token}/acceptRisolvi / 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}/downloadExport dell’organizzazione riservato all’owner: uno zip con un dump SQL e ogni allegato, eseguito come job
MethodPathDescription
GET/projectsElenca i tuoi progetti (limit ≤ 200)
POST/projectsCrea 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}/pinFissa / sblocca il progetto nel tuo elenco progetti
POST/projects/{id}/transfer-organizationSposta il progetto in un’altra organizzazione (manager)
POST/projects/{id}/slack/testInvia un messaggio di prova al feed Slack del progetto (manager)
GET / POST/projects/{id}/showcase_claim_eligibility · /projects/{id}/showcase_claim · /projects/{id}/showcase_seedProgetti vetrina pubblici: verifica se puoi rivendicarne uno, rivendicalo, popolalo
GET/projects/{id}/audit-logLettura 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}/eventsStream 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.

MethodPathDescription
GET/projects/{id}/membershipsElenca i membri (viewer)
POST/projects/{id}/membershipsInvita 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-existingMembri dell’organizzazione non ancora nel progetto / aggiungine uno senza invito via email (manager)
POST/projects/{id}/members/joinUn 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}/anonymizationMaschera nome / email / avatar di un membro su questo progetto (manager)
GET / POST/projects/{id}/agent_keysElenca / 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/onboardingIl 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}/avatarRuota la chiave di un agente (identità e cronologia conservate) / carica il suo avatar

Tutte le scritture sulle storie richiedono il ruolo member.

MethodPathDescription
GET/projects/{id}/storiesElenca le storie (paginato, filtrabile) (viewer)
POST/projects/{id}/storiesCrea 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}/transitionsCambia stato con validazione
POST/projects/{id}/stories/{sid}/reject · /projects/{id}/stories/{sid}/restartRifiuta una storia delivered / riporta una storia rifiutata a started (rejected è terminale per /transitions)
POST/projects/{id}/stories/{sid}/archive · /projects/{id}/stories/{sid}/unarchiveArchivia / rimuovi dall’archivio una storia
POST/projects/{id}/stories/bulk_transitionFai transitare molte storie (1–100) in una volta
POST/projects/{id}/stories/bulk-archive · bulk-delete · bulk-duplicate · bulk-moveArchivia, elimina, duplica o sposta (su un pannello / in una posizione) molte storie
POST/projects/{id}/stories/{sid}/duplicateDuplica una storia
GET / POST / DELETE/projects/{id}/stories/{sid}/epics · …/epics/{eid}L’appartenenza della storia agli epic
GET/short-links/{code} · /story-referencesRisolvi 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 } ] }.

Tutte member. List/GET sulla maggior parte è (viewer).

MethodPathBody / 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_typerelates_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.

MethodPathDescription
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-attachmentsAllegati, 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.

MethodPathDescription
GET / POST/projects/{id}/labelsElenca / crea una label
PUT / DELETE/projects/{id}/labels/{lid}Aggiorna / elimina una label
POST/projects/{id}/labels/{lid}/archiveArchivia (nascondi in modo soft) una label

Le letture sono aperte a qualsiasi ruolo di progetto, e anonime su un progetto pubblico.

MethodPathDescription
GET/projects/{id}/iterationsElenca 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-previewLe date che riceverebbe la prima iterazione, mostrate nella conferma di creazione
POST/projects/{id}/iterationsCrea un’iterazione manuale (member)
DELETE/projects/{id}/iterations/{itid}Elimina un’iterazione (manager)
PUT/projects/{id}/iterations/{itid}/velocitySovrascrivi la velocity di una singola iterazione senza cambiare la strategia del progetto (manager)
GET/projects/{id}/iterations/{itid}/done-storiesLe storie accettate di un’iterazione chiusa, paginate
MethodPathDescription
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/groupingI gruppi di iterazioni proiettati dal Backlog (viewer)
GET / PUT/projects/{id}/preferencesLe tue preferenze di board per questo progetto — qualsiasi ruolo di progetto, solo la tua riga
MethodPathDescription
GET/projects/{id}/eventsStream 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.

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.

MethodPathDescription
GET/me/notificationsIl tuo feed di notifiche. Filtri: unread=true, since_id=, kind= (mentions / reviews / stories / invitations); pagina con cursor= / limit=
GET/me/notifications/unread-countTotali non letti — { unread_count, by_category: { mentions, reviews, stories, invitations } }
POST/me/notifications/read-allSegna tutto come letto; restituisce i contatori aggiornati
POST/me/notifications/{id}/ackSegna un elemento come letto (idempotente)
POST/me/notifications/{id}/acceptAccetta un invito a un progetto / organizzazione dal feed (solo token membro)
POST/me/notifications/{id}/declineRifiuta 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/streamPush 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.

MethodPathDescription
POST/projects/{id}/importSorgenti 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/jsonBody 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.

MethodPathDescription
GET/projects/{id}/export/formatsFormati 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/attachmentsOgni 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.

MethodPathDescription
GET / POST/projects/{id}/backups · GET …/backups/{snapshot_id} · …/backups/healthElenca 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).

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.

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.

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.

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.

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,owners

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"] } }
StatuscodeWhen
400invalid_parameterinput errato; messaggio in error, nessun details (la maggior parte delle validazioni: vuoto/lunghezza/null-byte/email)
400validation_failederrore di input strutturato; details.fields è un array di nomi di campo offendenti
401unauthenticatedtoken mancante/non valido
403unauthorized_operationautenticato ma con ruolo insufficiente
404unfound_resourcenon trovato — restituito anche ai non-membri
409conflictconflitto di risorsa (es. duplicato)
409idempotency_conflictIdempotency-Key riutilizzato con un body diverso
409stale_write · import_already_runningla storia è cambiata dal tuo expected_updated_at · un’importazione è già in corso
412precondition_failedIf-Match non corrispondeva all’ETag attuale della risorsa; details porta expected e current
413request_too_largeil body supera il limite di dimensione della route
422invalid_transitionmovimento di stato illegale; details porta { from, to, allowed }
429rate_limitedtroppe richieste da questo IP su una route a frequenza limitata; header Retry-After
500internal_errorguasto del server — messaggio generico; sicuro da riprovare
503not_configuredal 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"] } }

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 POST di 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".