Documentazione / Collega
API HTTP
Manda errori, log, metriche e visite con semplici richieste HTTP, senza un SDK.
Gli SDK sono una comodità: sotto, ognuno fa le richieste HTTP di questa pagina. Usa l'API direttamente da un linguaggio senza SDK, da uno script o dalla CI.
Prima di cominciare
Ti servono tre cose, tutte dalle impostazioni del progetto in CloseYourIt:
- l'indirizzo della tua installazione, per esempio
https://bugs.example.com; - l'id del progetto;
- un token di ingest (
cyi_…).
Il token è segreto e resta sul server. ingest permette di mandare dati; le letture private richiedono lo scope separato read. I client browser e mobile usano una chiave pubblica per i segnali supportati, senza accesso in lettura né di amministrazione. I segnali disponibili dipendono dall'SDK e dalla versione; vedi SDK JavaScript.
Ogni richiesta porta il token e manda JSON, con i nomi dei campi in snake_case:
Authorization: Bearer cyi_...
Content-Type: application/json
Dove mandare
| Cosa | Indirizzo | Corpo | Risposta |
|---|---|---|---|
| Errore | POST /api/v1/projects/<id>/events | un evento | 202 {"data":{"id":"…"}} |
| Prestazioni | POST /api/v1/projects/<id>/metrics | un campione o una lista | 202 {"data":{"accepted":N}} |
| Log | POST /api/v1/projects/<id>/logs | una riga o una lista | 202 {"data":{"accepted":N}} |
| Visite | POST /api/v1/projects/<id>/pageviews | una visita o una lista | 202 {"data":{"accepted":N}} |
| Registrazione della sessione | POST /api/v1/projects/<id>/replays | un pezzo o una lista | 202 {"data":{"accepted":N}} |
L'<id> nell'indirizzo deve essere il progetto a cui appartiene il token.
202 vuol dire accettato, non salvato: i dati li scrive un attimo dopo un lavoro in background. accepted conta gli elementi che hanno passato i primi controlli.
Mandare un errore
Il corpo è un evento nel formato di Sentry.
curl -X POST https://bugs.example.com/api/v1/projects/$PROJECT_ID/events \
-H "Authorization: Bearer $CYI_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"event_id": "fc6d8c0c43fc4630ad850ee518f1b9d0",
"timestamp": "2026-10-03T10:15:30Z",
"level": "error",
"platform": "ruby",
"environment": "production",
"release": "v1.4.2",
"exception": { "values": [ {
"type": "RuntimeError",
"value": "boom",
"stacktrace": { "frames": [
{ "filename": "app/models/order.rb", "function": "charge", "lineno": 42, "in_app": true }
] }
} ] }
}'
| Campo | Note |
|---|---|
event_id | un tuo id unico; mandare due volte lo stesso non crea un secondo errore |
timestamp | ISO 8601 o secondi dal 1970; di base è adesso |
level | debug, info, warning, error o fatal; di base error |
exception.values[] | ognuno con type, value e stacktrace.frames[]; si legge l'ultimo |
message | usato come titolo quando manca exception |
environment, release, server_name, platform | facoltativi |
user | id, email, ip_address: se ne tiene solo un hash |
request, breadcrumbs, tags, extra, contexts | facoltativi; i valori sotto chiavi sensibili diventano [FILTERED] |
trace_id | collega l'errore ai log della stessa richiesta |
fingerprint | una lista che sostituisce il modo in cui gli errori si raggruppano |
Gli errori si raggruppano per tipo di eccezione e punto del codice, non per il testo del messaggio.
Mandare log
curl -X POST https://bugs.example.com/api/v1/projects/$PROJECT_ID/logs \
-H "Authorization: Bearer $CYI_TOKEN" \
-H "Content-Type: application/json" \
-d '[
{ "timestamp": "2026-10-03T10:15:30Z", "level": "info", "message": "order charged",
"logger": "billing", "attributes": { "order_id": 7 } }
]'
| Campo | Note |
|---|---|
message | obbligatorio, non vuoto. Si salva così come lo mandi: non metterci segreti |
level | debug, info, warning, error o fatal; qualsiasi altro valore diventa info |
timestamp | ISO 8601 o secondi dal 1970 |
logger | il nome della parte della tua app che l'ha scritto |
attributes | un oggetto qualsiasi; i valori sotto chiavi sensibili vengono filtrati |
event_id, trace_id, environment, release | facoltativi |
Mandare campioni di prestazioni
curl -X POST https://bugs.example.com/api/v1/projects/$PROJECT_ID/metrics \
-H "Authorization: Bearer $CYI_TOKEN" \
-H "Content-Type: application/json" \
-d '[
{ "kind": "slow_query", "duration_ms": 812.4,
"sql": "SELECT * FROM orders WHERE id = 7", "environment": "production" },
{ "kind": "slow_method", "duration_ms": 240.0, "label": "Invoice#render" }
]'
| Campo | Note |
|---|---|
kind | obbligatorio: slow_query, slow_method o performance_issue |
duration_ms | quanto è durato |
sql | per slow_query; numeri e id vengono sostituiti, così le query uguali si raggruppano |
label | per slow_method; obbligatorio, è ciò per cui si raggruppano i campioni |
subtype | per performance_issue; obbligatorio: n_plus_one, slow_request, slow_external_http, high_query_count, jank, repeated_http o rebuild_storm |
sample_id | un tuo id unico (un UUID), per ritentare senza doppioni |
occurred_at, environment, trace_id | facoltativi |
Mandare visite
curl -X POST https://bugs.example.com/api/v1/projects/$PROJECT_ID/pageviews \
-H "Authorization: Bearer $CYI_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "hostname": "www.example.com", "path": "/pricing",
"referrer": "https://www.google.com/", "utm_source": "newsletter" }'
| Campo | Note |
|---|---|
hostname, path | obbligatori. path non contiene mai la query string |
name | lascialo fuori per una visita; qualsiasi altro nome è un evento personalizzato |
referrer | se ne tiene solo il nome dell'host |
utm_source, utm_medium, utm_campaign, utm_term, utm_content | facoltativi |
screen_width | in pixel; si salva solo la classe (telefono, tablet, portatile, desktop) |
event_id, occurred_at, environment | facoltativi |
Non si usano cookie. Il visitatore si conta da un hash che cambia ogni giorno e non si può riportare a una persona. Una richiesta che sembra di un bot riceve 202 con accepted: 0.
Le visite richiedono il token segreto, quindi questo indirizzo si chiama dal tuo server, non dal browser. Per un sito web usa l'SDK JavaScript.
Limiti
| Limite | Valore |
|---|---|
| Log in una richiesta | 1000 |
| Campioni di prestazioni in una richiesta | 1000 |
| Visite in una richiesta | 100 |
| Pezzi di registrazione in una richiesta | 50 |
| Richieste al minuto, errori, log, metriche (per progetto, ognuno) | 1200 |
| Richieste al minuto, visite, registrazioni (per progetto, ognuna) | 600 |
| Richieste al minuto da un indirizzo IP, tutta l'API | 300 |
Oltre un limite di dimensione la risposta è 413 e non si salva niente. Oltre un limite di frequenza la risposta è 429 con l'intestazione Retry-After, in secondi.
Con il gateway di ingest acceso, una richiesta può essere al massimo di 5 MB.
Quando qualcosa va storto
Una risposta di errore è così:
{ "error": { "code": "R422-LOG-004", "message": "…" } }
| Risposta | Significa | Rimandare? |
|---|---|---|
202 | accettato | no |
401 | token mancante, sbagliato o revocato | no: correggi il token |
403 | il token non può fare questa cosa | no |
404 | il progetto nell'indirizzo non è quello del token | no: correggi l'indirizzo |
413 | troppi elementi in una richiesta | no: manda gruppi più piccoli |
422 | il corpo non è JSON valido, o nessun elemento dentro è valido | no: correggi il corpo |
429 | troppe richieste | sì, dopo Retry-After secondi |
500, 502, 503, 504, o nessuna risposta | l'app è occupata o sta ripartendo | sì, con pause sempre più lunghe |
Per ritentare senza doppioni tieni lo stesso event_id o sample_id: CloseYourIt ignora la copia se il primo tentativo era arrivato. Fanno eccezione i pezzi di registrazione: un pezzo rimandato può essere salvato due volte.
Rileggere i dati
Lo stesso token legge i dati del progetto. Ogni indirizzo restituisce gli ultimi 25 elementi.
| Indirizzo | Filtri |
|---|---|
GET /api/v1/error_groups | status: unresolved, resolved, ignored |
GET /api/v1/error_groups/<id> | |
GET /api/v1/metric_groups | kind: slow_query, slow_method, performance_issue |
GET /api/v1/log_entries | level, trace_id, environment |
GET /api/v1/projects/<id>/analytics | range: 24h, 7d, 30d, 1y; environment |
curl -H "Authorization: Bearer $CYI_TOKEN" \
"https://bugs.example.com/api/v1/error_groups?status=unresolved"
Per segnare un errore come risolto: PUT /api/v1/error_groups/<id>/resolution. Per riaprirlo: DELETE sullo stesso indirizzo.
Se vieni da Sentry
Le applicazioni che usano già un SDK di Sentry non hanno bisogno di questa pagina: tengono il loro SDK e cambiano un'impostazione. Vedi SDK Sentry.