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.
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.
| Attributo | Tipo | Descrizione |
|---|---|---|
| id | integer | Identificativo univoco della trattativa. |
| name | string | Titolo della trattativa. |
| stage | string | Fase nella pipeline (default new). |
| status | string | Stato (default open; won/lost se lo stage lo è). |
| amount | number | Importo (decimale, ≥ 0). Default 0. |
| close_date | string | null | Data di chiusura prevista (ISO 8601, solo data). |
| account_id | integer | null | Azienda collegata (lookup verso Account). |
| contact_id | integer | null | Referente collegato (lookup verso Contatti). |
| lead_id | integer | null | Lead di origine (lookup verso Lead). |
| owner_user_id | integer | null | Utente proprietario del record. |
| assigned_user_id | integer | null | Utente assegnatario. |
| created_by | integer | null | Utente che ha creato il record. |
| updated_by | integer | null | Ultimo utente che l'ha modificato. |
| righe | array | null | Righe prodotti/servizi dell'offerta: vedi Righe. |
| is_deleted | boolean | true se in cestino (soft delete). |
| created_at | string | Data/ora di creazione (ISO 8601). |
| updated_at | string | Data/ora ultima modifica (ISO 8601). |
{ "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
| Metodo | Path | Azione | Cosa fa |
|---|---|---|---|
| GET | /api/deals | read | Elenco paginato e filtrabile. |
| GET | /api/deals/:id | read | Una singola trattativa. |
| GET | /api/deals/:id/history | read | Storico modifiche del record. |
| GET | /api/deals/:id/righe | read | Le righe prodotti/servizi. |
| PUT | /api/deals/:id/righe | update | Sostituisce l'intero elenco righe. |
| POST | /api/deals | create | Crea una trattativa. |
| PATCH | /api/deals/:id | update | Modifica parziale. |
| PATCH | /api/deals/bulk | update | Aggiornamento massivo. |
| DELETE | /api/deals/:id | delete | Soft delete. |
| POST | /api/deals/:id/restore | update | Ripristina un record eliminato. |
| GET | /api/deals/export.csv · .xlsx | export | Esporta l'elenco filtrato. |
| POST | /api/deals/import/preview · /execute | create | Import CSV. |
03Elencare le trattative
Restituisce le trattative visibili all'utente (secondo lo scope del ruolo), paginate.
Se il ruolo non legge amount, il campo è omesso da ogni elemento.
| Parametro | Tipo | Descrizione | |
|---|---|---|---|
| limit | integer | opzionale | Righe per pagina. Default 50, max 200. |
| offset | integer | opzionale | Righe da saltare (default 0). |
| sort_by | string | opzionale | Colonna di ordinamento (es. amount, close_date, created_at). |
| sort_dir | string | opzionale | asc o desc. |
| advanced_filters | string (JSON) | opzionale | Array di condizioni: vedi Filtri avanzati. |
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();
{ "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
| Parametro | Tipo | Descrizione |
|---|---|---|
| id | integer | Id della trattativa. |
| Stato | Corpo |
|---|---|
| 200 | { success: true, data: { …deal } } |
| 404 | NOT_FOUND: inesistente o fuori dal tuo scope. |
05Creare una trattativa
| Campo | Tipo | Note | |
|---|---|---|---|
| name | string | richiesto | Titolo della trattativa. 1–255 caratteri. |
| stage | string | opzionale | Fase nella pipeline (es. negotiation). Default new. Max 40. |
| status | string | opzionale | Stato. Default derivato dallo stage (open/won/lost). Max 40. |
| amount | number | opzionale | Importo. Non negativo. |
| close_date | date | opzionale | Data di chiusura prevista. |
| account_id | integer | opzionale | Azienda collegata. |
| contact_id | integer | opzionale | Referente collegato. |
| lead_id | integer | opzionale | Lead di origine. |
| owner_user_id | integer | opzionale | Proprietario. Assegnabile solo a sé o a un sottoposto nella gerarchia dei ruoli (admin: chiunque). Vedi Utenti e team. |
| assigned_user_id | integer | opzionale | Utente assegnatario. |
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();
{ "success": true, "data": { "id": 88, "name": "Fornitura 2026", "amount": 24000, "stage": "negotiation", "status": "open", … } }
| Stato | Codice | Quando |
|---|---|---|
| 201 | · | Trattativa creata; nel corpo l'oggetto completo. |
| 422 | VALIDATION_ERROR | name mancante, amount negativo o campo fuori misura. |
| 403 | FORBIDDEN | Campo 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.
Restituisce l'elenco delle righe della trattativa e l'eventuale riferimento alla fattura
emessa (invoice, null se assente).
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();
{ "success": true, "data": { "righe": [ { "descrizione": "Licenza annuale", "quantita": 3, "prezzo_unitario": 500, "aliquota_iva": 22, "natura": null } ], "invoice": null } }
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| Campo | Tipo | Note |
|---|---|---|
| descrizione | string | Voce dell'offerta. Fino a 500 caratteri. |
| quantita | number | Default 1. Range 0–1.000.000. |
| prezzo_unitario | number | Default 0. Non negativo (max 1.000.000.000). |
| aliquota_iva | number | Percentuale IVA. Default 22, range 0–100. |
| natura | string | null | Codice natura IVA per le righe esenti (es. N2.2). Max 4 caratteri, maiuscolo. |
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 }, ] }), });
{ "success": true, "data": { "righe": [ { "descrizione": "Licenza annuale", "quantita": 3, "prezzo_unitario": 500, "aliquota_iva": 22, "natura": null } ] } }
| Stato | Corpo |
|---|---|
| 200 | Elenco righe salvato (già normalizzato) nel corpo. |
| 404 | NOT_FOUND: trattativa inesistente o fuori dal tuo scope. |
07Aggiornare una trattativa
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 -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
Soft delete: il record viene marcato eliminato (is_deleted: true), non cancellato. Risponde 200 { deleted: true, id }.
Ripristina una trattativa eliminata (richiede il permesso update). Risponde 200 con l'oggetto ripristinato.
09Aggiornamento massivo
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{ "ids": [88, 87, 85], "changes": { "stage": "negotiation" } }
{ "success": true, "data": { "requested": 3, "updated": 2 } }
10Import ed export
- Export:
GET /export.csvo/export.xlsxrispettano gli stessi filtri della lista (advanced_filters, ordinamento). Richiedono il permessoexport; se il ruolo non leggeamountla colonna è esclusa. - Import:
POST /import/previewrestituisce un'anteprima con la mappatura colonne e gli errori riga per riga;POST /import/executeapplica. L'import richiede scopealle rispetta i permessi di scrittura per campo.