Convenzioni#
Valgono per l’intera superficie. Leggerle una volta evita di riscoprirle risorsa per risorsa.
Versionamento#
La versione è nel percorso: /api/public/v1/.
Le modifiche additive — nuovi campi, nuovi endpoint, nuovi parametri facoltativi, nuovi valori di enum — vengono rilasciate dentro v1. Il tuo client deve tollerarle, e questo richiede due abitudini: ignora i campi di risposta che non riconosci e non bloccarti davanti a un valore di enum sconosciuto.
Rimozioni, ridenominazioni, cambi di tipo e tutto ciò che rende più stretta la convalida fanno salire la versione, con una finestra di sovrapposizione durante la quale entrambe sono attive.
Risposte degli elenchi#
Gli elenchi sono paginati a cursore e tornano sempre nella stessa busta:
{
"object": "list",
"data": [ { "object": "shift", "id": "0194d1c0-…", "…": "…" } ],
"has_more": true,
"next": "https://app.shiftavo.com/api/public/v1/shifts/?cursor=…"
}
nextè un URL opaco. Seguilo così com’è; non costruire né analizzare i cursori.Fermati quando
has_moreèfalse.page_sizecontrolla la lunghezza della pagina.
Il discriminatore object#
Ogni risorsa porta un campo "object" che ne indica il tipo — "shift", "location", "employee". Ramifica su quello invece che sulla forma del payload, in particolare negli handler dei webhook e nelle collezioni mescolate.
Errori#
Una sola busta, sempre:
{
"error": {
"type": "conflict_error",
"code": "not_cancellable",
"message": "…",
"details": [],
"param": "start_date"
}
}
detailsè sempre presente: un elenco di errori a livello di campo, vuoto quando l’errore non ha una scomposizione per campo.paramcompare solo quando è in errore esattamente un campo.
Ci sono sette tipi. Ciascuno corrisponde a uno stato, tranne invalid_request_error, il contenitore generico per i 4xx non tabulati, dove la distinzione è portata dal code:
|
Stato |
Note |
|---|---|---|
|
400 · 405 · 406 · 415 |
|
|
400 |
|
|
401 |
token mancante / non valido / scaduto |
|
403 |
|
|
404 |
restituito anche invece di 403 per i record al di fuori della tua azienda — nessuna fuga di informazioni sull’esistenza |
|
409 |
conflitti di stato: |
|
429 |
porta |
Ramifica su type per la classe di gestione, su code per il caso specifico. Entrambi sono stabili; message non lo è — consideralo solo come testo leggibile da una persona.
Idempotenza#
Invia un header Idempotency-Key sulle creazioni. Una richiesta ripetuta riproduce così il primo risultato invece di creare un secondo record.
Riutilizzare una chiave con un corpo diverso è un 400 idempotency_key_reused: la chiave identifica una richiesta, non un endpoint.
Tracciamento delle richieste#
Ogni risposta, di successo o di errore, porta un header X-Request-Id. Registralo. Se invii il tuo X-Request-Id, viene restituito così com’è, in modo che un id di traccia possa viaggiare da un’estremità all’altra. Citalo in qualsiasi richiesta di assistenza.
Transizioni del ciclo di vita#
Una transizione si attiva con un POST verso un sotto-percorso di azione: POST …/<id>/publish/, …/cancel/, …/revoke/.
Una transizione non applicabile — annullare una bozza, pubblicare qualcosa già pubblicato — è un 409 con un codice specifico (not_cancellable, not_publishable, not_unpublishable). Non è mai un 200 che non ha fatto nulla in silenzio.
Eliminazione#
DELETE restituisce uno stub, non un 204:
{ "id": "0194d1c0-…", "object": "shift", "deleted": true }
Un DELETE non accetta mai un corpo. Tutto ciò che serve a un’eliminazione sta nel percorso o nella stringa di query, così un client generato può esprimerle tutte. (HTTP lascia indefinita la semantica del corpo di un DELETE e OpenAPI 3.0 dice ai consumatori di ignorarlo, quindi un campo del corpo qui scomparirebbe silenziosamente dal tuo client e tornerebbe come un 400 che lo schema non saprebbe spiegare.)
L’eliminazione non è il verbo delicato#
Quando una risorsa offre entrambi, non sono sinonimi e la scelta sbagliata non è recuperabile:
Risorsa |
|
Il verbo delicato |
|---|---|---|
|
rimuove il turno; le persone assegnate a un turno pubblicato vengono avvisate e ogni offerta attiva nel mercato viene prima ritirata |
|
|
rimozione definitiva: l’accesso viene revocato, la persona esce da ogni collezione e un |
|
|
solo se in attesa o rifiutata — una richiesta confermata o revocata comporta una registrazione nel giornale, quindi è un |
|
Record ricorrenti#
Eliminare una singola occorrenza di un record ricorrente shifts o availability richiede un ambito esplicito:
DELETE …/<id>/?scope=this|following|all # shifts
DELETE …/<id>/?scope=single|following|all # availability
Se lo ometti su una serie ottieni un 409 delete_scope_required invece di un’ipotesi su cosa intendevi.
Limiti di frequenza#
Un 429 porta un header Retry-After. Rispettalo e applica un backoff esponenziale in caso di ripetizioni.