Autenticazione

Chiavi API, scope, limiti di richieste, cache, errori e idempotenza nell'API pubblica di Sway.

Ogni richiesta porta una chiave API come token Bearer:

curl https://www.sway.events/api/v1/artists \
  -H "Authorization: Bearer sway_sk_YOUR_KEY"

La chiave identifica la tua crew. Non esiste alcun parametro di tenant da nessuna parte nell'API: cosa può vedere una chiave lo decide la crew a cui appartiene, mai la richiesta.


Due tipi di chiave

Chiave segretaChiave pubblicabile
Prefissosway_sk_sway_pk_
Gira suIl tuo serverIl browser di un visitatore
ScopeQualsiasiSolo le letture pubbliche: read:profile, read:artists, read:promoters, read:events, read:venues, read:content, read:shop
Dove rispondeOvunqueSolo sui websites elencati sulla chiave (verificati contro l'Origin del browser)
Inviata comeAuthorization: Bearer ...Authorization: Bearer ..., oppure ?key= quando un header non è praticabile

Usa una chiave segreta per tutto quello che fa il tuo server, e per qualsiasi cosa scriva, venda biglietti o tocchi i membri. Non spedirla mai a un browser o a un'app: chiunque possa leggere le tue pagine può leggere la chiave.

Usa una chiave pubblicabile quando un browser deve chiamare l'API direttamente, per esempio un sito statico senza server. Legge solo quello che Sway mostra già pubblicamente, risponde solo sui propri siti, e riceve una quota più piccola per indirizzo di visitatore, oltre a quella della chiave.

Una chiave segreta inviata come ?key= viene rifiutata con 400 key_in_query, perché gli URL finiscono nei log e nelle cronologie del browser. Se succede, la chiave va trattata come trapelata: ruotala.

Ciclo di vita di una chiave

Le chiavi si gestiscono in Admin della crew → API (piani Studio e Roster, permesso Gestire API).

AzioneCosa succede
CreaNome, tipo, scope, websites o indirizzi storefront, e una scadenza (mai, 30, 90 o 365 giorni). La chiave completa viene mostrata una volta; dopo si vedono solo il suo inizio e le ultime quattro cifre
ModificaCambia gli scope e gli elenchi di indirizzi; si applica subito
SospendiL'interruttore d'emergenza. Ogni richiesta risponde 403 key_suspended finché qualcuno non clicca Riattiva. Niente viene eliminato
RuotaPOST /v1/keys/rotate con una chiave che ha write:keys restituisce la sua sostituta: stesso nome, tipo, scope e limiti. La vecchia chiave continua a funzionare per grace_hours (da 1 a 168, predefinito 24), poi scade. Una chiave ruota una volta, al massimo una volta all'ora, e il proprietario della crew riceve un'email ogni volta
RevocaDefinitiva. Le richieste rispondono 401 invalid_key. Quando la chiave era stata ruotata, Revoca anche le chiavi che l'hanno sostituita revoca tutta la catena: usala quando la chiave è trapelata, dato che chiunque possieda una chiave trapelata con write:keys potrebbe averla ruotata
ScadeDopo expires_at, le richieste rispondono 401 invalid_key

Una chiave revocata, scaduta o sconosciuta risponde tutte allo stesso modo con 401 invalid_key: l'API non dice mai a chi chiama se una chiave che non conosce sia mai esistita. Il pannello Attività su ogni chiave mostra quanto ha usato dei suoi limiti e le sue ultime chiamate.

Se l'abbonamento della crew scade, ogni chiave risponde 403 subscription_required finché non torna attivo.


Scope

Gli scope si scelgono alla creazione della chiave e si possono modificare in seguito. Una richiesta senza lo scope richiesto dal suo endpoint risponde 403 missing_scope.

ScopeAprePredefinito
read:profileIl profilo della crew stessa: /v1/crew, /v1/crew/pagesSì
read:artistsIl roster, ogni artista, i suoi eventi, calendari e press kitSì
read:promotersI promoter che gestisci, i loro eventi, locali e artistiSì
read:eventsEventi, line-up, giorni, orari, tipi di biglietto, disponibilità, commissioni, galleria, prevenditeSì
read:venuesLocali, i loro eventi e la capienzaSì
read:contentNotizie, partner, moduli e press kitSì
read:ambassadorsLa pagina pubblica del programma ambassador, la classifica, i premi e le missioni, e la pagina personale di un membro connessoNo
read:ambassador-membersI membri, i loro estratti conto e le statistiche del programma. Dati personali: solo il proprietario della crewNo
read:exportL'intero catalogo come download NDJSONNo
read:shopIl merch delle pagine che gestisci: catalogo, prodotto, ciò che vende un evento; il calcolo e il controllo di un carrelloNo
write:bookingsInvia richieste di booking (POST /v1/booking-requests)No
write:newsletterAggiunge iscrizioni alla newsletter nel CRM di un promoter, e le toglieNo
write:formsInvia risposte ai tuoi moduli pubbliciNo
write:checkoutApre checkout di biglietti a pagamento, e verifica coupon e codici di accessoNo
write:checkout:freeEmette anche ordini di biglietti gratuiti e con sconto del 100%. Solo il proprietario della crewNo
write:shop:checkoutAprire checkout di merch per una pagina che gestisciNo
write:ambassadorsIscriversi, accedere e uscire, riscattare un premio, modificare i propri datiNo
write:keysLascia che la chiave ruoti se stessa (POST /v1/keys/rotate)No
mcp:accessUsa la chiave con il server MCPNo

GET /v1/me, /v1/usage, /v1/audit-log e /v1/genres non richiedono alcuno scope: qualsiasi chiave valida può chiamarli.

Gli scope "solo il proprietario della crew" possono essere concessi solo dal proprietario della crew; un membro con il permesso Gestire API può toglierli ma non aggiungerli. GET /v1/scopes restituisce questo elenco come JSON.

Siti web e indirizzi storefront

Due elenchi di indirizzi vivono su una chiave, entrambi impostati in Admin della crew → API:

  • Siti web (chiavi pubblicabili): le uniche origini browser a cui la chiave risponde. Una richiesta da un altro sito risponde 403 origin_not_allowed.
  • Indirizzi storefront (chiavi con uno scope ambassador): dove Sway può rimandare un membro dopo un link di accesso via email. Il tuo sito passa l'indirizzo di ritorno con la richiesta; Sway lo accetta solo se la sua origine è nell'elenco, così il link non può mai essere indirizzato altrove. Una chiave con uno scope ambassador e un elenco vuoto continua a servire le pagine, ma non potrà mai far rientrare un membro.

Sono accettate solo origini https, più http su localhost per lo sviluppo. I percorsi vengono scartati: si salva e si confronta solo l'origine.


Limiti di richieste

L'API è costruita perché un sito partner molto frequentato continui a mostrare i suoi dati. Due idee lo permettono:

  • Una risposta in cache è gratis. Le letture sono in cache per crew. Ripetere una richiesta che la cache ha già costa zero, e un 304 Not Modified costa zero.
  • Si conta il lavoro, non le richieste. Solo una richiesta che deve leggere il database spende il budget di lavoro della chiave, e lo spende in proporzione a quanto legge.
PianoLavoro al minutoLavoro al giorno
Studio12020.000
Roster300100.000

Quanto costa una richiesta non in cache:

RichiestaUnità di lavoro
Un dettaglio (/v1/events/{id})1
Una pagina di elenco1 per ogni blocco iniziato di 25 elementi (limit=100 costa 4)
Ogni valore di expand+1
Una ricerca in blocco (ids=)1 per ogni blocco iniziato di 10 id

Quando il budget finisce, una lettura che la cache possiede riceve comunque risposta, dall'ultima copia, con Cache-Status: sway; hit; ...; detail=rate-limited. Solo una lettura senza alcuna copia risponde 429 rate_limited, con Retry-After.

Altre quote, per chiave salvo indicazione diversa:

QuotaValore
Richieste, in cache o no2.400 al minuto su Studio, 6.000 su Roster (o 20 volte il lavoro al minuto della chiave, il valore più alto)
Dati di risposta5 GB al giorno su Studio, 20 GB su Roster
Tutte le chiavi della crew insieme600 di lavoro al minuto e 120.000 al giorno su Studio; 1.500 e 500.000 su Roster. Altrettante scritture
Scritture (POST), checkout a parteQuante il lavoro al minuto della chiave, mai meno di 60
Sessioni di checkout600 al minuto, per la chiave e per tutte le chiavi della crew insieme, a parte le altre scritture
Verifiche di coupon e codici di accesso300 al minuto, a parte le scritture
Messaggi MCP120 al minuto
Chiave pubblicabile, per indirizzo di visitatore60 di lavoro e 600 richieste al minuto

Ogni risposta autenticata riporta il budget di lavoro della chiave:

RateLimit-Policy: "burst";q=120;w=60, "daily";q=20000;w=86400
RateLimit: "burst";r=118;t=42, "daily";r=19873;t=51230
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 118
X-RateLimit-Reset: 1759525489

RateLimit-Policy e RateLimit seguono la bozza IETF; gli header X-RateLimit-* mostrano la più stretta delle due finestre e restano per i client esistenti. Su un 429, aspetta Retry-After secondi; una chiave che continua a superare il limite riceve attese più lunghe. Una chiave che scorre gli id con un pattern insolito viene limitata alle sole risposte in cache per un po'.

Con If-None-Match e la cache, un sito partner che serve migliaia di visitatori al giorno resta ben sotto queste cifre. La guida all'integrazione mostra il pattern. Se ti serve di più, contattaci: i limiti si impostano per chiave.

Cache

Ogni lettura in cache porta la sua durata e un ETag:

Cache-Control: private, max-age=60, stale-while-revalidate=120, stale-if-error=86400
ETag: "4f9a1c7e2b8d5f3a"
Cache-Status: sway; hit; ttl=42

Rimanda indietro l'ETag come If-None-Match: quando niente è cambiato, la risposta è 304 Not Modified, senza corpo e senza costo. Cache-Status dice da dove viene una risposta: hit (la cache), fwd=miss (lettura fresca), fwd=stale (aggiornata dopo che la sua durata è scaduta). Le durate vanno da 30 secondi (tipi di biglietto, disponibilità) a un'ora (generi); il riferimento degli endpoint le indica una per una. Le risposte che portano dati a livello di chiave o di membro non sono mai in cache (Cache-Control: private, no-store).

Per saltare la copia di Sway, invia Cache-Control: no-cache con una chiave segreta: la richiesta viene letta di nuovo alla fonte (Cache-Status: sway; fwd=request; stored) e consuma lavoro come qualsiasi lettura fuori cache. È la richiesta da fare quando un webhook annuncia un cambiamento, perché la copia può essere precedente. Quando il tuo budget di lavoro è esaurito, ricevi comunque la copia, con detail=rate-limited. Il Cache-Control di una chiave pubblicabile viene ignorato.


Errori

Ogni errore è un corpo RFC 9457 application/problem+json con un code stabile su cui basare la logica, e l'id della richiesta:

{
  "type": "https://www.sway.events/en/docs/api/errors#missing_scope",
  "title": "Missing Scope",
  "status": 403,
  "detail": "This API key does not have the required scope.",
  "instance": "/api/v1/events/4521",
  "code": "missing_scope",
  "request_id": "0b7c2e4a-9f1d-4a3b-8c6e-5d2f1a9b7e3c"
}
codeStatoSignificato
invalid_key401La chiave è malformata, sconosciuta, revocata o scaduta
key_in_query400Una chiave segreta è stata inviata come ?key=. Ruotala
key_suspended403La chiave è sospesa; un admin della crew può riattivarla
subscription_required403Il piano della crew non include più l'API
missing_scope403Alla chiave manca lo scope dell'endpoint, oppure una chiave pubblicabile ha chiamato un endpoint che richiede una chiave segreta
origin_not_allowed403Una chiave pubblicabile è stata usata da un sito non nel suo elenco, oppure un browser ha chiamato il server MCP
not_found404La risorsa non esiste o è fuori dalle pagine della tua crew
conflict409Lo stesso Idempotency-Key è ancora in elaborazione, oppure l'azione è già stata fatta (una chiave ruota una volta)
validation_error400 / 422Un parametro di query (400) o il corpo della richiesta (422) non è valido
invalid_cursor400Il cursore di paginazione è malformato o scaduto
idempotency_mismatch422Questo Idempotency-Key è stato usato con un altro corpo
password_required401Un press kit protetto richiede Presskit-Password
rate_limited429È stato raggiunto un limite; vedi Retry-After
internal_error500Qualcosa è andato storto da parte nostra; puoi riprovare senza rischi
upstream_error502Un servizio da cui Sway dipende (pagamenti, email) ha fallito; puoi riprovare senza rischi

validation_error porta anche un array errors di { "param", "message" }. Gli endpoint aggiungono propri codici dove servono (un tipo di biglietto esaurito, un codice che non si applica); il riferimento degli endpoint li elenca.

404 not_found vale sia per "mancante" sia per "non tuo": l'API non conferma mai che esistano dati fuori dalla tua crew.

Ogni risposta, di successo o di errore, porta X-Request-Id. Citalo quando ci contatti: trova la richiesta nei nostri log. Puoi inviare il tuo X-Request-Id (da 8 a 128 tra lettere, cifre, ., _, : o -) per collegare i nostri log ai tuoi; qualsiasi altra cosa viene sostituita con un id nuovo.


Idempotenza

Un tentativo di rete ripetuto non deve mai prenotare, iscrivere o rimborsare due volte. Invia un header Idempotency-Key su un POST, un valore unico per operazione (un UUID è l'ideale, da 1 a 255 caratteri ASCII visibili):

curl -X POST https://www.sway.events/api/v1/booking-requests \
  -H "Authorization: Bearer sway_sk_YOUR_KEY" \
  -H "Idempotency-Key: 5b8f3c1e-2a7d-4e9b-8c6f-1d3a5e7b9c2f" \
  -H "Content-Type: application/json" \
  -d '{ "artist_ids": [501], "event_name": "Warehouse Series #4", "contact_email": "[email protected]" }'
  • La stessa chiave con lo stesso corpo entro 24 ore riceve indietro la prima risposta, senza rifare il lavoro.
  • La stessa chiave con un altro corpo risponde 422 idempotency_mismatch.
  • Un tentativo che arriva mentre la prima richiesta è ancora in corso risponde 409 conflict: aspetta e riprova.

Funziona su ogni POST tranne l'apertura di un checkout, che si protegge da sola dai duplicati, e le verifiche di coupon e codici di accesso, che non cambiano nulla. Una risposta che porta un segreto (una chiave ruotata) non viene mai conservata per un replay.