DonutworkHelp Center
Applicazioni
Documentazione Donutwork

Come Costruire un'App DonutWork

Una guida agnostica dal punto di vista della programmazione per progettare, registrare e distribuire integrazioni di terze parti nell'ecosistema DonutWork.

Costruire per DonutWork

Questa guida fornisce un blueprint concettuale e tecnico per lo sviluppo di applicazioni di terze parti. Il framework DonutWork è esplicitamente agnostico rispetto al linguaggio di programmazione, basandosi su protocolli standard HTTP/REST e sull'incorporamento sicuro della UI per facilitare l'orchestrazione tra sistemi.


1. Registrazione dell'Applicazione

Prima di iniziare lo sviluppo, è necessario registrare l'applicazione per ottenere le credenziali necessarie per l'identità e l'accesso alla piattaforma.

  • Client ID: L'identificativo pubblico della tua applicazione.
  • Client Secret: Una credenziale sicura utilizzata per autorizzare la tua app ad agire per conto di un'azienda.
  • Redirect URI: L'URL dove DonutWork invierà i codici di autorizzazione.

Consulta la Guida Dettagliata OAuth per l'handshake tecnico di autenticazione.


2. Scoperta delle Funzionalità (Il Manifesto)

DonutWork utilizza un Discovery Pattern per comprendere le capacità della tua applicazione. Invece di chiamare la tua app alla cieca, la tua app deve "dichiarare" le sue funzionalità (Azioni).

Dichiarazione delle Funzionalità

La tua applicazione deve inviare una richiesta PUT all'endpoint /integrations.json. Il manifesto dichiara le capability proxate per workflow e API, oltre alle azioni UI opzionali che aprono l'app dal backoffice.

PUT
/2026-02-01/integrations.json
Required permissionintegrations:write

Path, query, and header parameters

No path, query, or additional header parameters.

Request Body

JSON
{
  "app_token": "YOUR_APP_SHARED_TOKEN",
  "capabilities": [
    {
      "name": "ping",
      "description": "Test della salute della connessione",
      "endpoint": "https://your-app.com/api/ping.json",
      "method": "GET",
      "scopes": [
        "workflow",
        "api"
      ],
      "inputs": []
    },
    {
      "name": "provision_resource",
      "description": "Creazione di una nuova risorsa",
      "endpoint": "https://your-app.com/api/provision.json",
      "method": "POST",
      "inputs": [
        {
          "name": "resource_name",
          "type": "string",
          "required": true
        }
      ]
    }
  ],
  "ui_actions": [
    {
      "key": "print_customer",
      "label": "Stampa dati cliente",
      "placements": [
        "customer:details"
      ]
    }
  ]
}

Request examples

curl --request PUT 'https://api.hub.donutwork.com/2026-02-01/integrations.json' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{
  "app_token": "YOUR_APP_SHARED_TOKEN",
  "capabilities": [
    {
      "name": "ping",
      "description": "Test della salute della connessione",
      "endpoint": "https://your-app.com/api/ping.json",
      "method": "GET",
      "scopes": [
        "workflow",
        "api"
      ],
      "inputs": []
    },
    {
      "name": "provision_resource",
      "description": "Creazione di una nuova risorsa",
      "endpoint": "https://your-app.com/api/provision.json",
      "method": "POST",
      "inputs": [
        {
          "name": "resource_name",
          "type": "string",
          "required": true
        }
      ]
    }
  ],
  "ui_actions": [
    {
      "key": "print_customer",
      "label": "Stampa dati cliente",
      "placements": [
        "customer:details"
      ]
    }
  ]
}'
const response = await fetch('https://api.hub.donutwork.com/2026-02-01/integrations.json', {
  method: 'PUT',
  headers: {
    Authorization: 'Bearer ' + process.env.DONUTWORK_API_KEY,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "app_token": "YOUR_APP_SHARED_TOKEN",
    "capabilities": [
      {
        "name": "ping",
        "description": "Test della salute della connessione",
        "endpoint": "https://your-app.com/api/ping.json",
        "method": "GET",
        "scopes": [
          "workflow",
          "api"
        ],
        "inputs": []
      },
      {
        "name": "provision_resource",
        "description": "Creazione di una nuova risorsa",
        "endpoint": "https://your-app.com/api/provision.json",
        "method": "POST",
        "inputs": [
          {
            "name": "resource_name",
            "type": "string",
            "required": true
          }
        ]
      }
    ],
    "ui_actions": [
      {
        "key": "print_customer",
        "label": "Stampa dati cliente",
        "placements": [
          "customer:details"
        ]
      }
    ]
  }),
});
const requestId = response.headers.get('x-request-id');
if (!response.ok) {
  const error = await response.text();
  throw new Error(`${response.status} ${error} [${requestId}]`);
}
const contentType = response.headers.get('content-type') ?? '';
const data = response.status === 204 ? null
  : contentType.includes('application/json') ? await response.json()
  : await response.text();
$request = curl_init('https://api.hub.donutwork.com/2026-02-01/integrations.json');
curl_setopt_array($request, [
    CURLOPT_CUSTOMREQUEST => 'PUT',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('DONUTWORK_API_KEY'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => '{
  "app_token": "YOUR_APP_SHARED_TOKEN",
  "capabilities": [
    {
      "name": "ping",
      "description": "Test della salute della connessione",
      "endpoint": "https://your-app.com/api/ping.json",
      "method": "GET",
      "scopes": [
        "workflow",
        "api"
      ],
      "inputs": []
    },
    {
      "name": "provision_resource",
      "description": "Creazione di una nuova risorsa",
      "endpoint": "https://your-app.com/api/provision.json",
      "method": "POST",
      "inputs": [
        {
          "name": "resource_name",
          "type": "string",
          "required": true
        }
      ]
    }
  ],
  "ui_actions": [
    {
      "key": "print_customer",
      "label": "Stampa dati cliente",
      "placements": [
        "customer:details"
      ]
    }
  ]
}',
]);
$response = curl_exec($request);
if ($response === false) {
    $error = curl_error($request);
    curl_close($request);
    throw new RuntimeException($error);
}
$status = curl_getinfo($request, CURLINFO_RESPONSE_CODE);
$contentType = (string) curl_getinfo($request, CURLINFO_CONTENT_TYPE);
curl_close($request);
if ($status >= 400) { throw new RuntimeException($response); }
$data = $status === 204 ? null
    : (str_contains($contentType, 'application/json')
        ? json_decode($response, true, flags: JSON_THROW_ON_ERROR) : $response);

Replace uppercase placeholders and sample values with your own data and company resource IDs. Keep the API key on a trusted server. Reuse an idempotency key only for the same logical request and payload.

  • app_token: DonutWork includerà questo Bearer token in ogni header Authorization quando chiamerà la tua applicazione. Ciò consente alla tua app di verificare che la richiesta provenga da DonutWork.
  • capabilities: Un array di azioni consentite, ciascuna con un endpoint, un method e gli inputs attesi.
  • capabilities[].scopes: "workflow", "api" oppure un array con entrambi, facoltativo. Se omesso resta il comportamento senza restrizioni; se presente, la capability è accessibile solo dai canali indicati.
  • ui_actions: Array facoltativo di azioni nel backoffice. Ogni azione ha una key univoca, una label visibile e un array placements. L'unica posizione supportata è customer:details. Ometti il campo per mantenere le azioni UI esistenti oppure invia [] per rimuoverle. La guida API riporta limiti e regole di validazione.

Ogni PUT sostituisce capabilities, events e app_token: includi i valori correnti quando aggiorni soltanto ui_actions.

Azioni nei Dettagli Cliente

Per un'app installata con canvas_uri HTTPS, ogni azione customer:details compare nel menu Altre azioni della pagina cliente. Selezionandola, l'app si apre nel suo iframe con action_type uguale alla key dichiarata e action_id uguale all'ID del cliente. DonutWork firma entrambi insieme agli altri parametri di apertura. La tua app deve verificare la firma dell'iframe prima di usarli. L'ID cliente fornisce il contesto, non il permesso di accedere ai dati: per richiederli usa le credenziali API autorizzate.


3. Modello di Comunicazione (La Logica dell'Azione)

Quando un utente o un workflow generico attiva un'azione, DonutWork agisce come un Proxy Sicuro.

  1. Trigger: DonutWork identifica l'azione di destinazione (es. provision_resource).
  2. Identificazione: DonutWork recupera l' endpoint e l' app_token dal manifesto salvato.
  3. Esecuzione: DonutWork invia una richiesta HTTP al tuo endpoint, includendo l' app_token nell'intestazione e i dati richiesti nel corpo.
  4. Risposta: La tua applicazione elabora la richiesta e restituisce una risposta JSON standard.

4. Framework UI (L'Iframe)

DonutWork incorpora il frontend della tua applicazione all'interno della propria dashboard tramite un Iframe. Per garantire un'esperienza utente fluida e mantenere la sicurezza, è necessario rispettare le seguenti policy:

Poiché la tua applicazione è in esecuzione in un contesto Iframe (cross-site), i cookie di sessione tradizionali potrebbero essere bloccati dai browser moderni. È necessario configurare i cookie di sessione con:

  • SameSite=None
  • Secure=true

Header di Sicurezza

DonutWork consente l'incorporamento di Iframe da applicazioni registrate. Da parte tua, dovresti assicurarti che i tuoi header consentano alla piattaforma di visualizzare i tuoi contenuti. Per approfondire la verifica della firma, vedi Contesto Iframe e Sicurezza.

Content-Security-Policy: frame-ancestors 'self' https://hub.donutwork.com;
X-Frame-Options: ALLOW-FROM https://hub.donutwork.com

5. Sincronizzazione degli Eventi (Webhook)

Per reagire agli eventi della piattaforma in tempo reale (ad esempio, quando un utente paga una fattura), dovresti registrare un Webhook.

  1. Endpoint: La tua applicazione dovrebbe fornire un URL HTTPS dedicato per ricevere i payload POST.
  2. Rotazione delle Chiavi: Durante la registrazione, riceverai un token. Usa questa chiave per verificare l'intestazione X-SIGNATURE su ogni richiesta in arrivo.

6. Callback di Disinstallazione (Pulizia Dati Company)

Quando una company disinstalla la tua app da DonutWork, la piattaforma invia una callback server-to-server verso l'uninstall_url configurata.

  • Metodo: DELETE
  • Content-Type: application/x-www-form-urlencoded
  • Body:
    • company_id: identificativo della company da eliminare.
    • verify: firma HMAC calcolata come HMAC_SHA256(company_id, client_secret).

Esempio payload:

DELETE /uninstall HTTP/1.1
Content-Type: application/x-www-form-urlencoded

company_id=699ecb44790ac4eea20e9cd3&verify=7c72b2...

Regola di verifica lato app:

$expected = hash_hmac('sha256', $companyId, $clientSecret);
$isValid = hash_equals($expected, $verify);

Requisiti implementativi:

  • Valida company_id e verify prima di qualsiasi cancellazione.
  • Elimina solo i dati tenant-scoped della company_id ricevuta.
  • Rendi l'endpoint idempotente (chiamate ripetute non devono fallire).
  • Restituisci 2xx solo quando la pulizia e' completata (o gia' completata).

Se il tuo endpoint restituisce status non-2xx, DonutWork considera fallita la callback di uninstall e blocca la disinstallazione locale.


Elenco di Controllo Riassuntivo

  • Registra l'App e ottieni Client ID/Secret.
  • Implementa OAuth 2.0 per ottenere i Platform Access Tokens.
  • Usa PUT /integrations.json per dichiarare gli endpoint della tua app.
  • Implementa i tuoi endpoint API per gestire le richieste dal proxy DonutWork.
  • Configura i cookie SameSite=None e Secure per la tua UI in Iframe.
  • Registra i Webhook per l'elaborazione degli eventi in tempo reale.
  • Implementa DELETE uninstall_url con company_id + HMAC verify e cleanup tenant-safe.

On this page