Salta ai contenuti

Guida API

Primi passi con la REST API di East Agile Tracker: chiavi utente e agente, creare e spostare story, scritture idempotenti, ricerca, eventi e paginazione.

L’API di East Agile Tracker è progettata tanto per gli agenti quanto per gli umani. Tutto ciò che puoi fare nell’interfaccia, puoi farlo via API — e alcune cose che l’interfaccia non espone sono lì anch’esse.

Questa guida ti porta da zero allo “scripting del tuo backlog” in meno di dieci minuti. Per il riferimento completo degli endpoint, vedi Specifica API.

Ti autentichi con una chiave nell’header X-TrackerToken. Ci sono due tipi di chiave che generi tu stesso, e un terzo che un client MCP ottiene per te:

  • Chiavi utente (ea_user_…) — Agiscono come te. Creale in Account Settings → API Keys. Usale per script personali, strumenti da CLI, integrazioni.
  • Chiavi agente (ea_agent_…) — Agiscono come un agente con nome in un progetto. Creale in Project Settings → Agent. Usale per agenti IA — Claude Code, Codex, il tuo — che dovrebbero partecipare al progetto come compagni di squadra con nome.
  • Token MCP (ea_mcp_…) — Token di accesso OAuth 2.1 rilasciati a un client MCP (Claude, un IDE) dopo che lo hai approvato nella pagina di consenso. Agiscono come te, e puoi revocarli in Account Settings → API Keys → Connected apps.

La finestra mostrata una sola volta dopo la creazione di una chiave API personale nelle impostazioni dell'account, con la chiave oscurata

Il modulo di creazione chiave della scheda Agent con un nome e il ruolo member selezionato, sotto le istruzioni di configurazione

Le differenze tra i due che generi tu:

Chiave utenteChiave agente
AmbitoTutti i tuoi progettiUn progetto specifico
Identità nell’audit logIl tuo nomeIl nome dell’agente
RuoloIl tuo ruolo in ciascun progettoImpostato alla creazione della chiave (viewer, member o manager — mai sopra il ruolo di chi genera la chiave)
RevocaRevoca una chiave; mantieni l’accesso tramite altre chiavi/sessioniRevoca o ruota una chiave; l’agente perde immediatamente l’accesso
Ideale perAutomazione personale, scriptAgenti IA che dovrebbero essere distinguibili da te nella cronologia

Authorization: Bearer … funziona anche se preferisci quello stile di header.

Ottieni i tuoi progetti:

Terminal window
curl https://api.eastagiletracker.com/api/v1/projects \
-H "X-TrackerToken: $TRACKER_TOKEN"

Oppure, per una chiave agente, elenca il progetto a cui è vincolata:

Terminal window
curl https://api.eastagiletracker.com/api/v1/projects \
-H "X-TrackerToken: ea_agent_xxxxx"

L’API è JSON, in stile REST, versionata su /api/v1/. Stesse forme per umani e agenti.

Terminal window
curl -X POST https://api.eastagiletracker.com/api/v1/projects \
-H "X-TrackerToken: $TRACKER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Onboarding redesign",
"description": "Q3 redesign of new-user onboarding",
"iteration_length_weeks": 1
}'

La risposta include il project_id e i default di iterazione applicati dal server (iteration_start_day, initial_velocity, iteration_length_weeks); GET /projects/{id} restituisce le impostazioni complete.

Terminal window
curl -X POST https://api.eastagiletracker.com/api/v1/projects/$PROJECT_ID/stories \
-H "X-TrackerToken: $TRACKER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Add OAuth login for Google",
"description": "## Acceptance\n- Google button on /login\n- Redirect back to original URL",
"story_type": "feature",
"estimate": "3",
"labels": ["auth"]
}'

estimate è l’etichetta del valore della scala, come stringa — "3", oppure "13" sulla scala Fibonacci — perché deve corrispondere a un punto della scala del progetto. Un numero JSON viene rifiutato.

L’endpoint di transizione valida il movimento richiesto e restituisce gli stati successivi consentiti in caso di errore:

Terminal window
curl -X POST https://api.eastagiletracker.com/api/v1/projects/$PROJECT_ID/stories/$STORY_ID/transitions \
-H "X-TrackerToken: $TRACKER_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "to": "started" }'

Il campo è to (non to_state). Se il movimento è illegale — diciamo che hai provato a saltare da unstarted dritto ad accepted — la risposta è 422 invalid_transition con dettagli d’errore strutturati:

{
"code": "invalid_transition",
"error": "Cannot move story from `unstarted` to `accepted`",
"details": { "from": "unstarted", "to": "accepted", "allowed": ["started"] }
}

Questa è una delle piccole cose che rendono l’API a misura di agente: un agente può leggere details.allowed e scegliere il movimento successivo giusto senza dover analizzare prosa.

rejected è terminale per l’endpoint di transizione, che rifiuta anche to: "rejected". Rifiuta una storia delivered con POST …/stories/{sid}/reject; rimetti al lavoro una storia rifiutata con POST …/stories/{sid}/restart, che funziona finché la storia è nell’iterazione corrente.

Terminal window
curl -X POST https://api.eastagiletracker.com/api/v1/projects/$PROJECT_ID/stories/$STORY_ID/comments \
-H "X-TrackerToken: $TRACKER_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "text": "Investigation done. Picking this up." }'

Il commento è attribuito a chiunque possieda la chiave API — se è una chiave agente, l’autore del commento è l’agente.

Gli endpoint di scrittura accettano un header Idempotency-Key (esclusi gli upload di allegati). Riprova la stessa chiave con lo stesso body entro 24 ore e riottieni la stessa risposta. Riprova la stessa chiave con un body diverso e ottieni un 409 idempotency_conflict:

Terminal window
curl -X POST https://api.eastagiletracker.com/api/v1/projects/$PROJECT_ID/stories \
-H "X-TrackerToken: $TRACKER_TOKEN" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{ "name": "Refactor auth middleware", "story_type": "chore" }'

Questo è cruciale per gli agenti nei loop di retry — vai in crash a metà scrittura, riprova con la stessa chiave, nessuna storia duplicata. Una risposta 5xx, 401, 403, 404 o 429 non viene mai messa in cache, quindi riprovare dopo una di esse esegue di nuovo la richiesta.

Sposta molte storie in una sola volta. Ogni storia è giudicata indipendentemente; un movimento illegale non fa fallire gli altri.

Terminal window
curl -X POST https://api.eastagiletracker.com/api/v1/projects/$PROJECT_ID/stories/bulk_transition \
-H "X-TrackerToken: $TRACKER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"story_ids": [101, 102, 103],
"to": "delivered"
}'

Per gli agenti che vogliono reagire a ciò che fanno gli umani, fai il polling dell’endpoint degli eventi:

Terminal window
curl "https://api.eastagiletracker.com/api/v1/projects/$PROJECT_ID/events?since_id=$LAST_EVENT_ID&types=story.created,story.transitioned,comment.added" \
-H "X-TrackerToken: $TRACKER_TOKEN"

La risposta è { items, next_since_id, next_cursor }: ogni evento riporta l’attore, la risorsa e la modifica. Memorizza next_since_id e restituiscilo come since_id al polling successivo per riprendere da dove avevi lasciato (since è accettato come alias più vecchio). Nessun webhook, nessuno scraping, nessun evento perso. Lo stream richiede il ruolo member — un viewer riceve 403.

GET /projects/{id}/search?q=<query> esegue una potente ricerca full-text + strutturata sulle storie del progetto. Il linguaggio di query è modellato sui qualificatori della ricerca issue di GitHub — quindi la sintassi che tu (o un agente IA) già conosci da GitHub si applica quasi del tutto.

Terminal window
curl -G "https://api.eastagiletracker.com/api/v1/projects/$PROJECT_ID/search" \
-H "X-TrackerToken: $TRACKER_TOKEN" \
--data-urlencode 'q=payment crash type:bug,chore owner:@me created:>2026-05-01'

La risposta è un involucro JSON, con le storie ordinate per rilevanza quando la query contiene testo libero e nell’ordine della board altrimenti:

{ "results": [ /* canonical story objects */ ], "total": 42, "limit": 50, "offset": 0 }

total è il conteggio completo delle corrispondenze, non la dimensione della pagina. Pagina con limit (default 50, max 1000) e offset; ordina con sort=relevance (default), created, created_asc, updated o state.

  • Testo libero corrisponde a titolo, riferimento e descrizione di una storia (full-text, con stemming e ranking). Racchiudi una frase esatta tra "virgolette".
  • Qualificatori sono field:value. Separa le alternative con una virgola (OR all’interno di un campo): type:bug,chore. Separa i qualificatori con spazi (AND tra di loro).
  • Nega qualsiasi termine o qualificatore con un - iniziale: -label:wontfix.
  • Intervalli per date e punti: inclusivo a..b, oppure aperto >x / <x.
QualificatoreEsempioCorrisponde a
type:type:bug,choretipo/i di storia
state:state:started,finishedstato/i del flusso di lavoro
label:label:"my label"una label
epic:epic:"Checkout"le storie di un epic
priority:priority:p1priorità
points:points:3 · points:1..5 · points:>3valore o intervallo di stima
iteration:iteration:42id dell’iterazione
created: updated: started: completed: release:created:2026-05-01..2026-06-01 · updated:>2026-06-01una data o un intervallo (granularità giornaliera); release: è la data di release della storia
owner: requester: follower: reviewer: commenter: mention:owner:claire · owner:@meuna persona per nome o email — membri e agenti, mention: incluso; @me sei tu (con una chiave agente, il membro che ha creato la chiave)
has:blockerhas:blockerha un blocker aperto
is:is:unestimated · is:icebox · is:backlog · is:blockedun flag

mywork: è un alias di owner: — mywork:me equivale a owner:@me. Il vecchio qualificatore scheduled: è ritirato e viene ignorato silenziosamente; usa release:.

L’OR con virgola (type:bug,chore) vale per i qualificatori a faccetta; i qualificatori sulle persone (owner: requester: follower: reviewer: commenter: mention:) accettano un solo valore.

payment crash testo libero "payment" AND "crash"
"exact phrase" una frase
type:bug,chore state:started bug o chore che sono started
owner:@me -label:wontfix miei, esclusa la label wontfix
points:3..8 created:2026-05-01..2026-05-31 stimati 3-8, creati a maggio
follower:tomas has:blocker tomas li segue ed è bloccato
is:backlog updated:>2026-06-01 elementi del backlog toccati dal 1° giugno

La stessa stringa di query pilota la casella di ricerca della board (che apre una colonna di risultati live) e questa API — una sola grammatica per umani e agenti. La ricerca nel contenuto di commenti, task e blocker è nella roadmap; oggi il testo libero copre titolo, riferimento e descrizione della storia stessa.

La spec OpenAPI 3 live è su:

https://api.eastagiletracker.com/api/v1/openapi.json

Swagger UI è su:

https://api.eastagiletracker.com/api/v1/docs/

/openapi.json e /docs non sono autenticati — un agente può leggere il contratto prima di avere una chiave. Una volta che ne possiede una, /api/v1/meta (che richiede una chiave valida) restituisce la sua identità e il grafo delle transizioni per ciascun tipo di storia; anche i lookup dei dati di riferimento (/story_types, /story_states, /effort_scales, /priority_scales) non sono autenticati. Insieme consentono agli agenti di rispondere a “cosa posso fare qui?” senza 403 per tentativi ed errori.

L’openapi.json servito include gli schemi dei request body per gli endpoint di scrittura, compreso il maxLength di ogni campo, così un client può validare prima di inviare. La Specifica riassume le stesse forme.

Se stai facendo lo scripting di una migrazione in blocco:

Terminal window
curl -X POST https://api.eastagiletracker.com/api/v1/projects/$PROJECT_ID/import \
-H "X-TrackerToken: $TRACKER_TOKEN" \
-F "source=pivotal" \
-F "file=@pivotal_export.csv"

Sorgenti da file supportate: pivotal, jira, asana, gitlab, shortcut, trello, linear, plane, plane_json, eat (l’export nativo di East Agile Tracker — il formato per il round-trip). L’endpoint multipart è sincrono e risponde con i conteggi del risultato.

GitHub importa dall’API anziché da un file, tramite l’endpoint JSON — niente file, solo le coordinate del repository:

Terminal window
curl -X POST https://api.eastagiletracker.com/api/v1/projects/$PROJECT_ID/import/json \
-H "X-TrackerToken: $TRACKER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"source": "github",
"owner": "octocat",
"repo": "hello-world",
"token": "ghp_…",
"include_pull_requests": false,
"include_milestones": false,
"include_releases": false,
"include_dependencies": false
}'

L’endpoint JSON è asincrono: risponde 202 con { "import_id", "status" } e tu interroghi GET /projects/{id}/imports/{import_id} finché il job non raggiunge done o failed. Per ogni progetto gira una sola importazione alla volta — una seconda chiamata mentre una è in corso dà 409 import_already_running. L’intero ciclo, con i campi di avanzamento del job, è in Popolare un progetto da un repo GitHub.

Il token è opzionale nella richiesta, ma il recupero si autentica sempre — passa dall’API GraphQL di GitHub, che non ha un livello anonimo. Ometti token e il server sostituisce il proprio token di piattaforma: solo repository pubblici, condiviso da ogni chiamante e rifiutato con import_github_shared_quota_low quando il suo budget GraphQL scende sotto i 500 punti. Un repository privato, o un deployment che non ha configurato alcun token di piattaforma (import_github_no_token), richiede il tuo. Qualunque token venga usato, serve solo per le chiamate a GitHub e non viene mai memorizzato né restituito. Il dettaglio completo, incluso il tetto REST non autenticato di 60 richieste di GitHub, è in Popolare un progetto da un repo GitHub.

Anteprima dry-run. Aggiungi "dry_run": true (JSON) oppure -F "dry_run=true" (multipart) a qualsiasi sorgente. L’importazione analizza, risolve e deduplica esattamente come un’esecuzione reale, produce gli stessi conteggi di risultato (imported, skipped, errors, unmatched), poi annulla tutto — non viene scritto nulla. Sull’endpoint JSON i conteggi arrivano sul job interrogato, dry run o meno.

Limiti. Un file di importazione è limitato a 10 MiB e 5.000 storie (400 import_too_large / import_too_many_records, nulla viene scritto); un’importazione da GitHub non ha limite di storie. Reimportare un file è sicuro — le righe già importate (individuate dall’id di origine) vengono saltate, non duplicate.

Qualsiasi ruolo di progetto può elencare i formati; scaricarne uno richiede il ruolo manager:

Terminal window
# I formati di export registrati: { id, name, content_type, drops, includes_archived }
curl https://api.eastagiletracker.com/api/v1/projects/$PROJECT_ID/export/formats \
-H "X-TrackerToken: $TRACKER_TOKEN"
# Scarica un formato (eat è il CSV per il round-trip a piena fedeltà)
curl https://api.eastagiletracker.com/api/v1/projects/$PROJECT_ID/export/eat \
-H "X-TrackerToken: $TRACKER_TOKEN" -o project-export.csv

Id dei formati di interscambio: eat, jira, pivotal, shortcut, trello, asana, gitlab, linear, plane, plane_json, più i formati documento pdf e docx. Ogni allegato è scaricabile come un unico zip da GET /projects/{id}/export/attachments.

Tutti gli errori sono JSON con come minimo:

{
"code": "invalid_transition",
"error": "Cannot move story from `unstarted` to `accepted`"
}

Molte risposte d’errore includono anche un oggetto details — details.fields (un array di nomi di campo offendenti) su validation_failed, e details.allowed (accanto a from/to) su 422 invalid_transition. Usali. Un 429 rate_limited porta un header Retry-After nello stesso involucro JSON.

Gli endpoint di elenco accettano limit e cursor. Il cursore è opaco; passa il next_cursor della risposta precedente. Il tetto di limit è per endpoint — 200 su storie, commenti e progetti, 500 sugli eventi, 1000 sull’audit log; la ricerca pagina con limit (≤ 1000) e offset. Un elenco semplice (senza cursore) che ha dovuto troncare la risposta lo dichiara negli header: X-Tracker-Pagination-Truncated, -Limit, -Offset e -Next-Offset, che restituisci come offset= per la pagina successiva. Non esiste un header con il conteggio totale.