Punto di arrivo dei biglietti
Ticket
Creare, aggiornare, esportare ed eliminare ticket di assistenza. Include cronologia degli stati, assegnazioni amministrative, appuntamenti CRM collegati, allegati e richieste di modifica.
GET /Export/JSON/tickets
GET /Export/JSON/ticket/{id}
POST /Import/JSON/tickets
PUT /Import/JSON/tickets
PUT /Import/JSON/ticket_claims
DELETE /Delete/JSON/ticket/{id}Endpoint
| Metodo | Endpoint | Ambito | Descrizione |
|---|---|---|---|
| GET | /Export/JSON/tickets | tickets:read | Recupera tutti i ticket (paginati) |
| GET | /Export/JSON/ticket/{id} | tickets:read | Singolo ticket con cronologia dello stato e appuntamenti associati |
| GET | /Export/JSON/ticket_attachments/{id} | tickets:read | Elenca gli allegati di un ticket |
| GET | /Export/JSON/ticket_attachment/{id}?file=name | tickets:read | Scaricare un singolo file in formato binario |
| POST | /Import/JSON/tickets | tickets:write | Crea un nuovo ticket (INSERT) — facoltativamente con commento e allegati |
| PUT | /Import/JSON/tickets | tickets:write | Aggiorna ticket esistente (UPDATE) — facoltativamente con commento, risposta pubblica e allegati |
| PUT | /Import/JSON/ticket_claims | tickets:write | Impostare, prorogare, acquisire da un altro amministratore o terminare una richiesta di modifica NOVITÀ v2.118.0 |
| DELETE | /Delete/JSON/ticket/{id} | tickets:delete | Eliminare un ticket con Cascade-Delete |
Esportazione: parametri di query
| Parametro | Descrizione |
|---|---|
?status={id} | Filtra in base all'ID dello stato |
?priority={id} | Filtra per ID priorità |
?department={id} | Filtra per ID reparto |
?customer_id={id} | Ticket di un determinato cliente |
?admin_id={id} | Ticket assegnati a un amministratore |
?type={type} | Filtra per tipo di ticket |
?date_from=&date_to= | Filtro per periodo |
?last_modified=AAAA-MM-GG | Modificato a partire dal data |
?language=de | Filtro per lingua |
?limit=50&offset=0 | Impaginazione (Predefinito: 50, Massimo: 250) |
Esportazione: campi della risposta
| Campo | Tipo | Descrizione |
|---|---|---|
ticket_id | intero | ID ticket |
ticket_link_id | stringa | ID link alfanumerico di 14 caratteri |
ticket_subject | stringa | Oggetto del ticket |
ticket_status | stringa | Stato attuale |
priorità_ticket | stringa | Priorità |
department_id | numero intero | ID reparto |
department_name | stringa | Nome del reparto |
cronologia_stato[] | array | Conversazione completa con commenti |
amministratori_assegnati[] | array | Amministratori assegnati |
followers[] | array | Seguaci del ticket |
linked_appointments[] | array | NOVITÀ v2.11.0: Appuntamenti CRM collegati (ID, inizio, fine, titolo, luogo, stato) |
claim | oggetto|null | NOVITÀ v2.118.0: Richiesta di elaborazione attiva con admin_id, admin_name, label, claimed_at, expires_at e expires_in_seconds; null se nessuno ha rivendicato il ticket |
edit_lock | object|null | NOVITÀ v2.118.0: il ticket è attualmente aperto nel backend: admin_id, admin_name, locked_at (valido per 300 secondi dall'ultima attività nella maschera); altrimenti null |
Importazione POST: creazione di un ticket
Campi obbligatori e facoltativi durante la creazione di un nuovo ticket.
Campi obbligatori
ticket_subject— Oggettoticket_customers_email— E-mail del clienteticket_customers_name— Nome del cliente
Campi facoltativi
ticket_type— Tipo di ticket (predefinito: cliente)ticket_status_id— ID stato (viene convalidato)ticket_priority_id— ID priorità (viene convalidato)ticket_department_id— ID reparto (viene convalidato)ticket_customers_id— ID clienteticket_language_id— ID della linguaticket_comments— Primo commento come testo (sempre interno)admin_id— ID amministratore per il commento (0 = sistema)attachments[]— NOVITÀ v2.60.0: allegati (nome+contenuto_base64); richiede la presenza diticket_commentsassigned_admins[]— Array con gli ID amministratore
Esempio: Creazione di un ticket
POST /Import/JSON/tickets
Authorization: Bearer {token}
Content-Type: application/json
{
"type": "tickets",
"data": [{
"ticket_subject": "API-Test-Ticket",
"ticket_customers_email": "kunde@example.com",
"ticket_customers_name": "Max Mustermann",
"ticket_priority_id": 2,
"ticket_department_id": 1,
"ticket_comments": "Erstellt via xoPort API",
"admin_id": 0,
"attachments": [
{ "name": "bestellung.pdf", "content_base64": "JVBERi0xLjQKJcfs…" }
],
"assigned_admins": [1, 3]
}]
}Importazione PUT: aggiornare un ticket
Campi modificabili e sistema di commenti.
ticket_internal_comment=1). A partire dalla versione 2.25.0, una risposta visibile al cliente richiede l'esplicito flag is_public_reply: true e un admin_id valido > 0; l'invio dell'e-mail richiede inoltre notify_customer: true. Entrambi i flag vengono verificati rigorosamente come valori booleani — "true" o 1 non sono validi.| Campo | Tipo | Descrizione |
|---|---|---|
ticket_id | intero | Obbligatorio. ID del ticket da aggiornare |
ticket_subject | stringa | Modifica oggetto |
tipo_ticket | stringa | Modifica tipo |
id_stato_ticket | numero intero | Modifica stato (soggetto a convalida) |
ticket_priority_id | intero | Modifica priorità (soggetto a convalida) |
id_reparto_ticket | intero | Modifica reparto (soggetto a convalida) |
data_nascondimento_ticket | datetime | Nascondi ticket fino al |
ticket_login_required | 0/1 | Accesso richiesto |
ticket_allow_learning | 0/1 | Consenti l'apprendimento tramite IA |
ticket_comments | string | Testo del commento — crea una nuova voce nella cronologia |
admin_id | intero | Autore del commento (0 = sistema; in caso di risposta pubblica deve essere > 0) |
is_public_reply | booleano | v2.25.0: true = risposta visibile al cliente anziché nota interna |
notify_customer | booleano | v2.25.0: true = e-mail al cliente (solo in combinazione con is_public_reply) |
append_signature | booleano | v2.27.0: allega la firma dell'amministratore a una risposta pubblica (impostazione predefinita: attiva; disattivazione solo tramite false) |
attachments[] | array | NOVITÀ v2.60.0: allegati (name + content_base64); richiede ticket_comments |
assigned_admins[] | array | Sostituisci le assegnazioni degli amministratori |
Formato dei commenti
"ticket_comments": "Nota interna tramite API",
"admin_id": 1ticket_comments è il testo del commento, admin_id è il suo autore (0 = sistema, >0 = ID amministratore, viene convalidato).
Esempio: aggiornamento di un ticket con commento
PUT /Import/JSON/tickets
Authorization: Bearer {token}
Content-Type: application/json
{
"type": "tickets",
"data": [{
"ticket_id": 42,
"ticket_status_id": 3,
"ticket_priority_id": 1,
"ticket_comments": "Status auf Erledigt gesetzt",
"admin_id": 1,
"assigned_admins": [1, 5]
}]
}Eliminazione: eliminazione a cascata
Quando si elimina un ticket, vengono automaticamente rimossi tutti i dati ad esso collegati:
Ordine di cascata
ticket_status_history— Commenti/Cronologiaticket_to_admins— Assegnazioni agli amministratoriticket_to_followers— Followerxocrm_appointments_relationships— Collegamenti agli appuntamenti (gli appuntamenti vengono mantenuti)xocrm_followers— Follower CRM- File su disco —
upload/ticket/{link_id}/ ticket_ticket— Il ticket stesso
Esempio
DELETE /Delete/JSON/ticket/42
Authorization: Bearer {token}Risposta
{
"success": true,
"deleted_id": 42,
"details": {
"ticket_id": 42,
"ticket_subject": "Gelöschtes Ticket",
"ticket_link_id": "8i1c5O8g7w8b5c"
}
}Appuntamenti CRM collegati v2.11.0
Nell'esportazione, ogni ticket contiene un array linked_appointments[] con gli appuntamenti CRM collegati:
"linked_appointments": [
{
"appointment_id": 15,
"startdate": "2026-04-14 10:00:00",
"enddate": "2026-04-14 11:00:00",
"allday": 0,
"done": 0,
"status_id": 1,
"title": "Rückruf Kunde",
"description": "Bezüglich offener Rückfrage",
"location": ""
}
]Gli appuntamenti vengono collegati tramite xocrm_appointments_relationships (target_type = ticket_id). L'eliminazione di un ticket rimuove solo il collegamento, non l'appuntamento stesso.
Corretto nella v2.118.1: linked_appointments su POST/PUT /Import/JSON/tickets generava anche l’avviso UNKNOWN_FIELD, sebbene gli appuntamenti fossero stati creati. L’avviso non viene più visualizzato; l’elaborazione rimane invariata.
Endpoint degli allegati
Gli allegati vengono identificati tramite l’ID del ticket:
Listing: GET /ticket_attachments/{id}
{
"success": true,
"ticket_id": 42,
"ticket_link_id": "8i1c5O8g7w8b5c",
"files": [
{
"name": "screenshot.png",
"size": 245760,
"mime_type": "image/png",
"last_modified": "2026-03-15 14:30:22"
}
]
}Download: GET /ticket_attachment/{id}?file=screenshot.png
Download di file binari con Content-Type e Content-Disposition corretti : header "attachment". Protezione dal path traversal tramite validazione basename() + realpath().
Caricamento: attachments[] su POST/PUT NOVITÀ v2.60.0
Fino alla v2.59.0 gli allegati erano solo in lettura — venivano caricati esclusivamente tramite il modulo del backend. Un ticket creato tramite API rimaneva quindi privo di allegati (PDF, .eml, screenshot). A partire dalla v2.60.0,POST/PUT /Import/JSON/tickets accetta il campo opzionale attachments[]:
"ticket_comments": "Allegato relativo al reclamo",
"admin_id": 1,
"attachments": [
{
"name": "rechnung.pdf",
"content_base64": "JVBERi0xLjQKJcfs…"
}
]ticket_comments nella stessa richiesta, non viene creata alcuna voce a cui il file possa essere associato — in tal caso, il file viene rifiutato con un messaggio di errore anziché essere semplicemente scartato.| Campo | Tipo | Descrizione |
|---|---|---|
name | string | Obbligatorio. Nome del file, estensione inclusa; l'estensione deve essere presente nell'elenco consentito (regola identica a quella per il caricamento dal backend) |
content_base64 | stringa | Obbligatorio. Contenuto del file codificato in Base64; il codice Base64 non valido viene rifiutato e non salvato |
L'archiviazione avviene tramite lo stesso meccanismo del caricamento sul backend: stessa lista di estensioni consentite, convenzione di denominazione e deduplicazione tramite hash per evitare il doppio salvataggio. Il nome del file effettivamente assegnato è riportato nella risposta alla voce attachments per ogni record. Il limite massimo per ogni file corrisponde all’endpoint /media(XOPORT_MEDIA_MAX_FILESIZE, valore predefinito 10 MB).
Le voci errate (estensione non consentita, Base64 non valido, superamento della dimensione massima) generano un errore per ogni file; l’aggiornamento del ticket stesso va comunque a buon fine. Se is_public_reply: true è impostato insieme a notify_customer: true, i file vengono inviati tramite l’e-mail del cliente — vengono scritti prima dell’invio.
Claim di elaborazione NUOVO v2.118.0
Un claim indica che qualcuno sta attualmente lavorando su un ticket, ad esempio un agente IA per conto di un collaboratore. Gli altri responsabili lo vedono nell’esportazione e nel backend (elenco ticket e vista ticket) e non iniziano a lavorarci in parallelo. La richiesta di elaborazione è un’indicazione, non un blocco: non è protetta da accesso in scrittura, il ticket rimane modificabile da tutti.
PUT /Import/JSON/ticket_claims
{
"type": "ticket_claims",
"data": [
{ "ticket_id": 33999, "admin_id": 12, "state": "claimed", "ttl_minutes": 60, "label": "KI-Agent" }
]
}| Campo | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
ticket_id | intero | sì | ID di un ticket esistente |
admin_id | numero intero | sì | Amministratore esistente per conto del quale si opera. Un claim indica sempre una persona. |
state | stringa | sì | claimed imposta o rinnova il claim, mentre released lo termina. Non esiste un valore predefinito: l'endpoint imposta uno stato e non cambia mai. |
ttl_minutes | intero | no | Durata in minuti, valore predefinito 60, compresa tra 5 e 240. Una proroga non riduce mai la durata di un claim in corso. |
label | stringa | no | Identificativo della fonte, ad es. KI-Agent; massimo 32 caratteri, l'HTML viene rimosso. Il backend mostra «Nome (Etichetta)». |
force | booleano | no | Solo JSON:true/false. true acquisisce o rilascia il claim di un altro amministratore. |
Risultato per ogni record
records[].status indica il risultato, records[].claim lo stato dopo la chiamata:
| status | Significato |
|---|---|
claimed | Nuova richiesta creata |
refreshed | Claim proprio prorogato; claimed_at rimane invariato |
taken_over | Claim di un altro amministratore acquisito con force: true; previous_claim contiene il valore precedente |
released | revocato; previous_claim contiene quello precedente |
unchanged | state: released inviato, ma nessun claim attivo |
conflict | Un altro amministratore detiene il claim. Non viene scritto nulla, claim indica il detentore e errors[] la dichiarazione. Il record viene conteggiato come failed. |
Risposta (HTTP 200) se un altro amministratore effettua una richiesta mentre Max Mustermann detiene il claim:
{
"success": false,
"outcome": "failed",
"api_version": "2.118.1",
"type": "ticket_claims",
"stats": { "total": 1, "claimed": 0, "refreshed": 0, "taken_over": 0, "released": 0, "unchanged": 0, "failed": 1 },
"errors": [
{ "record": 1, "error": "Ticket 33999 is claimed by Max Mustermann (admin_id 12) until 2026-09-17 14:05:10. Send force=true to take it over." }
],
"timestamp": "2026-09-17 13:20:02",
"records": [
{
"index": 1,
"ticket_id": 33999,
"status": "conflict",
"claim": {
"admin_id": 12,
"admin_name": "Max Mustermann",
"label": "KI-Agent",
"claimed_at": "2026-09-17 13:05:10",
"expires_at": "2026-09-17 14:05:10",
"expires_in_seconds": 2708
}
}
]
}Rilascio automatico
- Risposta pubblica al cliente, tramite API (
PUT /Import/JSON/tickets) o nel backend. Non importa chi risponde. - Passaggio a uno stato di chiusura. Chi salva nuovamente un ticket già chiuso non rilascia alcun claim.
- Scadenza del periodo di validità.
Una nota interna non sblocca il claim. Le operazioni di impostazione e sblocco non modificano né ticket_date_last_modified né la cronologia e non attivano eventi; l’ordinamento e l’esportazione delta (?last_modified=) rimangono inalterati. Per ogni ticket esiste al massimo un claim: su due richieste simultanee, solo una riceve conflict.
claim di un altro amministratore o un edit_lock, significa che qualcuno sta già lavorando sul ticket. Coordinatevi prima di inviare force: true.A partire dalla versione 4.9.73 dello Shop. Se la tabella ticket_claims non è ancora presente, l’endpoint risponde con un codice HTTP 503 TICKET_CLAIMS_UNAVAILABLE; lo Shop la creerà al prossimo allineamento dello schema (all’apertura di una pagina xoCRM o dell’HealthCheck).
Ambiti
| Ambito | Descrizione |
|---|---|
tickets:read | Esportazione di ticket e allegati |
tickets:write | Creare e aggiornare ticket, scrivere commenti e allegare file, impostare e chiudere richieste di modifica |
tickets:delete | Eliminare ticket (a cascata) |
Gestire i ticket tramite API
Creare un client OAuth2 con gli ambiti tickets:read, tickets:write e tickets:delete.