API Reference / Trattative

Trattative

Le trattative (deal) sono le opportunità di vendita: un valore, uno stage nella pipeline, i collegamenti ad account e contatto, e un elenco di righe prodotti/servizi gestito a parte.

Prima di iniziare

Envelope, errori, paginazione e advanced_filters sono comuni: vedi Introduzione alle API. Ogni richiesta richiede Authorization: Bearer <accessToken>.

01L'oggetto Trattativa

Ogni trattativa restituita dall'API ha questa forma. Le date sono stringhe ISO 8601 in UTC; amount è un numero decimale (2 cifre) e viene omesso se il tuo ruolo non può leggere il campo. righe è un array (l'offerta) oppure null.

AttributoTipoDescrizione
idintegerIdentificativo univoco della trattativa.
namestringTitolo della trattativa.
stagestringFase nella pipeline (default new).
statusstringStato (default open; won/lost se lo stage lo è).
amountnumberImporto (decimale, ≥ 0). Default 0.
close_datestring | nullData di chiusura prevista (ISO 8601, solo data).
account_idinteger | nullAzienda collegata (lookup verso Account).
contact_idinteger | nullReferente collegato (lookup verso Contatti).
lead_idinteger | nullLead di origine (lookup verso Lead).
owner_user_idinteger | nullUtente proprietario del record.
assigned_user_idinteger | nullUtente assegnatario.
created_byinteger | nullUtente che ha creato il record.
updated_byinteger | nullUltimo utente che l'ha modificato.
righearray | nullRighe prodotti/servizi dell'offerta: vedi Righe.
is_deletedbooleantrue se in cestino (soft delete).
created_atstringData/ora di creazione (ISO 8601).
updated_atstringData/ora ultima modifica (ISO 8601).
json · l'oggetto Trattativa
{
  "id": 88,
  "name": "Fornitura 2026",
  "stage": "negotiation",
  "status": "open",
  "amount": 24000,
  "close_date": "2026-09-30",
  "account_id": 128,
  "contact_id": 341,
  "lead_id": null,
  "owner_user_id": 7,
  "assigned_user_id": 7,
  "created_by": 7,
  "updated_by": 7,
  "righe": [
    { "descrizione": "Licenza annuale", "quantita": 3, "prezzo_unitario": 500, "aliquota_iva": 22, "natura": null }
  ],
  "is_deleted": false,
  "created_at": "2026-07-07T15:24:00.000Z",
  "updated_at": "2026-07-07T15:24:00.000Z"
}

02Endpoint

MetodoPathAzioneCosa fa
GET/api/dealsreadElenco paginato e filtrabile.
GET/api/deals/:idreadUna singola trattativa.
GET/api/deals/:id/historyreadStorico modifiche del record.
GET/api/deals/:id/righereadLe righe prodotti/servizi.
PUT/api/deals/:id/righeupdateSostituisce l'intero elenco righe.
POST/api/dealscreateCrea una trattativa.
PATCH/api/deals/:idupdateModifica parziale.
PATCH/api/deals/bulkupdateAggiornamento massivo.
DELETE/api/deals/:iddeleteSoft delete.
POST/api/deals/:id/restoreupdateRipristina un record eliminato.
GET/api/deals/export.csv · .xlsxexportEsporta l'elenco filtrato.
POST/api/deals/import/preview · /executecreateImport CSV.

03Elencare le trattative

GET /api/deals bearer richiesto

Restituisce le trattative visibili all'utente (secondo lo scope del ruolo), paginate. Se il ruolo non legge amount, il campo è omesso da ogni elemento.

Parametri query
ParametroTipoDescrizione
limitintegeropzionaleRighe per pagina. Default 50, max 200.
offsetintegeropzionaleRighe da saltare (default 0).
sort_bystringopzionaleColonna di ordinamento (es. amount, close_date, created_at).
sort_dirstringopzionaleasc o desc.
advanced_filtersstring (JSON)opzionaleArray di condizioni: vedi Filtri avanzati.
Richiesta
curl "https://crm.tuodominio.it/api/deals?limit=2&sort_by=amount&sort_dir=desc" \
  -H "Authorization: Bearer <accessToken>"
const res = await fetch("https://crm.tuodominio.it/api/deals?limit=2&sort_by=amount&sort_dir=desc", {
  headers: { Authorization: `Bearer ${accessToken}` },
});
const { data } = await res.json();
Risposta · 200
json
{
  "success": true,
  "data": {
    "items": [ { "id": 88, "name": "Fornitura 2026", "amount": 24000,  }, { "id": 87,  } ],
    "pagination": { "limit": 2, "offset": 0, "count": 2, "sortBy": "amount", "sortDir": "desc" }
  }
}

04Recuperare una trattativa

GET /api/deals/:id bearer richiesto
Parametri path
ParametroTipoDescrizione
idintegerId della trattativa.
Esiti
StatoCorpo
200{ success: true, data: { …deal } }
404NOT_FOUND: inesistente o fuori dal tuo scope.

05Creare una trattativa

POST /api/deals bearer richiesto
Parametri body
CampoTipoNote
namestringrichiestoTitolo della trattativa. 1–255 caratteri.
stagestringopzionaleFase nella pipeline (es. negotiation). Default new. Max 40.
statusstringopzionaleStato. Default derivato dallo stage (open/won/lost). Max 40.
amountnumberopzionaleImporto. Non negativo.
close_datedateopzionaleData di chiusura prevista.
account_idintegeropzionaleAzienda collegata.
contact_idintegeropzionaleReferente collegato.
lead_idintegeropzionaleLead di origine.
owner_user_idintegeropzionaleProprietario. Assegnabile solo a sé o a un sottoposto nella gerarchia dei ruoli (admin: chiunque). Vedi Utenti e team.
assigned_user_idintegeropzionaleUtente assegnatario.
Richiesta
curl -X POST https://crm.tuodominio.it/api/deals \
  -H "Authorization: Bearer <accessToken>" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Fornitura 2026", "account_id": 128, "amount": 24000, "stage": "negotiation" }'
const res = await fetch("https://crm.tuodominio.it/api/deals", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${accessToken}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ name: "Fornitura 2026", account_id: 128, amount: 24000, stage: "negotiation" }),
});
const { data } = await res.json();
Risposta · 201
json
{ "success": true, "data": { "id": 88, "name": "Fornitura 2026", "amount": 24000, "stage": "negotiation", "status": "open",  } }
Esiti
StatoCodiceQuando
201·Trattativa creata; nel corpo l'oggetto completo.
422VALIDATION_ERRORname mancante, amount negativo o campo fuori misura.
403FORBIDDENCampo non scrivibile dal ruolo (nei details l'elenco dei campi negati).

06Righe prodotti / servizi

Ogni trattativa può avere un elenco di righe (voci di offerta) memorizzato nella colonna righe. Si leggono e si scrivono su un endpoint dedicato: la scrittura è una sostituzione completa dell'elenco, non un aggiornamento parziale. I totali di riga e di documento sono derivati (mai memorizzati), così non possono divergere.

GET/api/deals/:id/righebearer richiesto

Restituisce l'elenco delle righe della trattativa e l'eventuale riferimento alla fattura emessa (invoice, null se assente).

Richiesta
curl "https://crm.tuodominio.it/api/deals/88/righe" \
  -H "Authorization: Bearer <accessToken>"
const res = await fetch("https://crm.tuodominio.it/api/deals/88/righe", {
  headers: { Authorization: `Bearer ${accessToken}` },
});
const { data } = await res.json();
Risposta · 200
json
{
  "success": true,
  "data": {
    "righe": [
      { "descrizione": "Licenza annuale", "quantita": 3, "prezzo_unitario": 500, "aliquota_iva": 22, "natura": null }
    ],
    "invoice": null
  }
}
PUT/api/deals/:id/righebearer richiesto

Rimpiazza tutte le righe con quelle inviate (max 200). Le righe genuinamente vuote (senza descrizione e con prezzo 0) vengono scartate; i numeri sono normalizzati entro limiti ragionevoli e i negativi azzerati.

Campi di una riga
CampoTipoNote
descrizionestringVoce dell'offerta. Fino a 500 caratteri.
quantitanumberDefault 1. Range 0–1.000.000.
prezzo_unitarionumberDefault 0. Non negativo (max 1.000.000.000).
aliquota_ivanumberPercentuale IVA. Default 22, range 0–100.
naturastring | nullCodice natura IVA per le righe esenti (es. N2.2). Max 4 caratteri, maiuscolo.
Richiesta
curl -X PUT https://crm.tuodominio.it/api/deals/88/righe \
  -H "Authorization: Bearer <accessToken>" \
  -H "Content-Type: application/json" \
  -d '{ "righe": [ { "descrizione": "Licenza annuale", "quantita": 3, "prezzo_unitario": 500, "aliquota_iva": 22 } ] }'
const res = await fetch("https://crm.tuodominio.it/api/deals/88/righe", {
  method: "PUT",
  headers: {
    Authorization: `Bearer ${accessToken}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ righe: [
    { descrizione: "Licenza annuale", quantita: 3, prezzo_unitario: 500, aliquota_iva: 22 },
    { descrizione: "Setup iniziale", quantita: 1, prezzo_unitario: 1500, aliquota_iva: 22 },
  ] }),
});
Risposta · 200
json
{ "success": true, "data": { "righe": [ { "descrizione": "Licenza annuale", "quantita": 3, "prezzo_unitario": 500, "aliquota_iva": 22, "natura": null } ] } }
Esiti
StatoCorpo
200Elenco righe salvato (già normalizzato) nel corpo.
404NOT_FOUND: trattativa inesistente o fuori dal tuo scope.

07Aggiornare una trattativa

PATCH/api/deals/:idbearer richiesto

Modifica parziale: invia solo i campi da cambiare (gli stessi del body di creazione, tutti opzionali). Risponde 200 con l'oggetto aggiornato, 404 se fuori scope, 422 in validazione.

curl
curl -X PATCH https://crm.tuodominio.it/api/deals/88 \
  -H "Authorization: Bearer <accessToken>" \
  -H "Content-Type: application/json" \
  -d '{ "stage": "won", "status": "won" }'

08Eliminare e ripristinare

DELETE/api/deals/:idbearer richiesto

Soft delete: il record viene marcato eliminato (is_deleted: true), non cancellato. Risponde 200 { deleted: true, id }.

POST/api/deals/:id/restorebearer richiesto

Ripristina una trattativa eliminata (richiede il permesso update). Risponde 200 con l'oggetto ripristinato.

09Aggiornamento massivo

PATCH/api/deals/bulkbearer richiesto

Applica gli stessi valori a una lista di record. Gli id fuori dal tuo scope vengono saltati; la risposta riporta quanti record sono stati effettivamente aggiornati.

Body
json
{ "ids": [88, 87, 85], "changes": { "stage": "negotiation" } }
Risposta · 200
json
{ "success": true, "data": { "requested": 3, "updated": 2 } }

10Import ed export