Power Automate
Ricevere webhook Blu Pantheon in Power Automate, validare la firma HMAC e chiamare le API con l azione HTTP e Azure Key Vault.
Guida per costruire flussi Power Automate (cloud flow) che ricevono eventi da Blu Pantheon e scrivono nei sistemi Microsoft 365, Dataverse o in gestionali on-premise tramite gateway.
Prima di iniziare
- Licenza Power Automate Premium (le azioni HTTP e Key Vault sono connettori premium)
- Un ambiente Power Platform con permessi di creazione flussi
- Un Azure Key Vault con i segreti bp-api-key e bp-webhook-secret
- La chiave API bp_live_xxxxxxxxxxxxxxxx e il segreto whsec_xxxxxxxxxxxxxxxx
Licenze premium
Le azioni HTTP, HTTP con Azure AD e il connettore Azure Key Vault richiedono un piano Power Automate Premium (o Per-flow). Il trigger "Quando viene ricevuta una richiesta HTTP" richiede a sua volta un piano premium per l'uso in produzione. Verifica le licenze prima di progettare il flusso.
Parte 1 — Ricevere il webhook
Crea il flusso
In make.powerautomate.com scegli Crea → Flusso cloud automatizzato istantaneo. Nome:
BluPantheon - Ricezione eventi. Come trigger seleziona Quando viene ricevuta una richiesta
HTTP (When an HTTP request is received).
Imposta il metodo
Espandi Mostra opzioni avanzate e imposta Metodo su POST.
Incolla lo schema JSON del corpo
Nel riquadro Schema JSON del corpo della richiesta incolla lo schema seguente. Definisce l'envelope comune e i campi più usati.
{
"type": "object",
"properties": {
"id": { "type": "string" },
"event": { "type": "string" },
"apiVersion": { "type": "string" },
"createdAt": { "type": "string" },
"tenantId": { "type": "string" },
"projectId": { "type": "string" },
"data": {
"type": "object",
"properties": {
"sessionId": { "type": "string" },
"clientId": { "type": "string" },
"bookingId": { "type": "string" },
"documentId": { "type": "string" },
"reason": { "type": "string" },
"priority": { "type": "string" },
"queue": { "type": "string" },
"summary": { "type": "string" },
"status": { "type": "string" },
"reference": { "type": "string" },
"startAt": { "type": "string" },
"endAt": { "type": "string" },
"timezone": { "type": "string" },
"traceId": { "type": "string" },
"client": {
"type": "object",
"properties": {
"id": { "type": "string" },
"displayName": { "type": "string" },
"email": { "type": "string" },
"phone": { "type": "string" }
}
},
"service": {
"type": "object",
"properties": {
"id": { "type": "string" },
"name": { "type": "string" },
"durationMinutes": { "type": "integer" }
}
},
"classification": {
"type": "object",
"properties": {
"category": { "type": "string" },
"confidence": { "type": "number" }
}
}
}
}
},
"required": ["id", "event", "apiVersion", "createdAt", "tenantId", "projectId", "data"]
}Salva e copia l'URL
Salva il flusso. Torna sul trigger: l'URL POST HTTP è ora popolato. Ha questa forma:
https://prod-42.westeurope.logic.azure.com:443/workflows/<id>/triggers/manual/paths/invoke?api-version=2016-06-01&sp=%2Ftriggers%2Fmanual%2Frun&sv=1.0&sig=<firma>Copialo integralmente, inclusi i parametri di query.
Registra l'endpoint in Console
Incolla l'URL completo nel campo URL e seleziona gli eventi da sottoscrivere.
La firma SAS dell URL non basta
Il parametro sig nell'URL rende l'endpoint difficile da indovinare, ma chiunque lo intercetti
può invocare il flusso. La validazione HMAC descritta sotto è obbligatoria.
Parte 2 — Leggere i segreti da Key Vault
Prepara Key Vault
In Azure crea (o riusa) un Key Vault e aggiungi due segreti:
| Nome segreto | Valore |
|---|---|
bp-api-key | bp_live_xxxxxxxxxxxxxxxx |
bp-webhook-secret | whsec_xxxxxxxxxxxxxxxx |
Aggiungi l'azione Key Vault
Nel flusso aggiungi Azure Key Vault → Ottieni segreto (Get secret).
| Campo | Valore |
|---|---|
| Nome del segreto | bp-webhook-secret |
Rinomina l'azione in Ottieni segreto webhook.
Nascondi il valore nei log
Apri Impostazioni dell'azione e attiva Impostazioni sicure → Proteggi output. Senza questa impostazione il segreto compare in chiaro nella cronologia di esecuzione del flusso.
Ripeti per la chiave API
Duplica l'azione impostando Nome del segreto su bp-api-key e rinominala
Ottieni chiave API. Attiva anche qui Proteggi output.
Parte 3 — Validare la firma HMAC
Power Automate non dispone di una funzione HMAC nativa nelle espressioni. Usa l'azione
Inline Code → Esegui codice JavaScript (Execute JavaScript Code), disponibile negli
ambienti con Power Platform e Node abilitati.
Recupera il corpo grezzo e gli header
Aggiungi un'azione Componi (Compose) rinominata Corpo grezzo con espressione:
string(triggerOutputs()?['body'])Aggiungi un'altra Componi rinominata Header firma:
triggerOutputs()?['headers']?['X-BP-Signature']E una terza rinominata Header consegna:
triggerOutputs()?['headers']?['X-BP-Delivery']Aggiungi l'azione JavaScript
Inserisci Esegui codice JavaScript rinominata Verifica firma con questo codice:
const crypto = require('crypto');
const rawBody = workflowContext.actions['Corpo_grezzo'].outputs;
const signatureHeader = workflowContext.actions['Header_firma'].outputs || '';
const secret = workflowContext.actions['Ottieni_segreto_webhook'].outputs.body.value;
const TOLERANCE_SECONDS = 300;
let timestamp = null;
const signatures = [];
for (const segment of signatureHeader.split(',')) {
const index = segment.indexOf('=');
if (index === -1) continue;
const key = segment.slice(0, index).trim();
const value = segment.slice(index + 1).trim();
if (key === 't') timestamp = Number(value);
if (key === 'v1') signatures.push(value);
}
if (!Number.isFinite(timestamp)) {
return { valid: false, reason: 'timestamp-non-valido' };
}
const now = Math.floor(Date.now() / 1000);
if (Math.abs(now - timestamp) > TOLERANCE_SECONDS) {
return { valid: false, reason: 'timestamp-fuori-tolleranza' };
}
if (signatures.length === 0) {
return { valid: false, reason: 'firma-assente' };
}
const expected = crypto
.createHmac('sha256', secret)
.update(timestamp + '.' + rawBody, 'utf8')
.digest('hex');
const expectedBuffer = Buffer.from(expected, 'utf8');
let valid = false;
for (const candidate of signatures) {
const candidateBuffer = Buffer.from(candidate, 'utf8');
if (
candidateBuffer.length === expectedBuffer.length &&
crypto.timingSafeEqual(candidateBuffer, expectedBuffer)
) {
valid = true;
break;
}
}
return { valid, reason: valid ? 'ok' : 'firma-non-corrispondente' };Nomi delle azioni negli indirizzamenti
In workflowContext.actions i nomi delle azioni usano l'underscore al posto degli spazi.
Corpo grezzo diventa Corpo_grezzo. Se rinomini un'azione, aggiorna il codice.
Interrompi se la firma non è valida
Aggiungi una Condizione (Condition) rinominata Firma valida?:
| Lato sinistro | Operatore | Lato destro |
|---|---|---|
body('Verifica_firma')?['valid'] | è uguale a | true |
Nel ramo Se no inserisci:
- Risposta (
Response) con Codice di stato401e Corpo{"error": "firma non valida"}. - Termina (
Terminate) con StatoFailed, Codice erroreSIGNATURE_INVALID, Messaggio erroreFirma HMAC non verificata.
Rispondi subito nel ramo positivo
Nel ramo Se sì, come prima azione inserisci Risposta con Codice di stato 202 e
corpo vuoto. Poi prosegui con la logica di business.
Rispondi prima di elaborare
Il trigger HTTP attende la prima azione Risposta. Se la inserisci dopo dieci azioni lente, Blu Pantheon andrà in timeout e ritenterà.
Parte 4 — Chiamare le API di Blu Pantheon
Aggiungi l'azione HTTP (connettore premium) e compila:
| Campo | Valore |
|---|---|
| Metodo | POST |
| URI | https://api.blupantheon.com/identity/v1/sessions/@{triggerBody()?['data']?['sessionId']}/notes |
| Intestazioni | vedi tabella sotto |
| Corpo | vedi JSON sotto |
Intestazioni da inserire nella vista chiave/valore:
| Chiave | Valore |
|---|---|
Authorization | Bearer @{body('Ottieni_chiave_API')?['value']} |
X-BP-Project | bp_proj_7f3a9c21 |
Content-Type | application/json |
Idempotency-Key | @{outputs('Header_consegna')} |
Corpo della richiesta:
{
"body": "Ticket aperto in ServiceNow: @{body('Crea_ticket')?['number']}",
"visibility": "internal",
"author": "power-automate"
}Proteggi gli input dell azione HTTP
Apri Impostazioni dell'azione HTTP e attiva Proteggi input. Senza questa opzione
l'header Authorization con la chiave API è visibile nella cronologia esecuzioni a chiunque
possa aprire il flusso.
Gestione dei limiti di frequenza
Configura in Impostazioni dell'azione HTTP:
| Impostazione | Valore |
|---|---|
| Criterio dei tentativi | Intervallo esponenziale |
| Numero di tentativi | 4 |
| Intervallo | PT10S |
| Intervallo minimo | PT5S |
| Intervallo massimo | PT2M |
| Timeout | PT30S |
Parte 5 — Gestione degli errori
Configura Ambito e Configura esecuzione dopo
Raggruppa la logica di business in un Ambito (Scope) rinominato Elaborazione. Aggiungi
un secondo ambito Gestione errori e configura Configura esecuzione dopo selezionando
è non riuscita, è stata ignorata e è scaduta.
Notifica il fallimento
Dentro Gestione errori inserisci Invia messaggio di posta elettronica (V2) verso
automazioni@acme.it con oggetto:
[Blu Pantheon] Flusso fallito - evento @{triggerBody()?['event']}E corpo che includa @{triggerBody()?['id']}, @{outputs('Header_consegna')} e
@{result('Elaborazione')}.
Termina con stato coerente
Chiudi con Termina e Stato Failed. Questo rende il fallimento visibile nella
dashboard delle analisi del flusso.
| Situazione | Comportamento consigliato |
|---|---|
| Firma non valida | 401 + Terminate Failed. Nessun ritentativo utile. |
| Sistema a valle non risponde | Ritentativo automatico dell'azione, poi Terminate Failed. |
| Dato mancante nel payload | Terminate Failed con messaggio esplicito. Non ritentare. |
429 dalle API Blu Pantheon | Ritentativo esponenziale già configurato sull'azione HTTP. |
| Evento duplicato | Controlla X-BP-Delivery in una tabella Dataverse prima di elaborare. |
Test del flusso
Invia un evento di prova
Seleziona webhook.test e invia. Il flusso deve completarsi con esito positivo.
Controlla la cronologia
Apri il flusso, sezione Cronologia esecuzioni (Run history). Verifica che
Verifica firma restituisca valid: true e che la Risposta sia 202.
Verifica il rifiuto
Modifica temporaneamente il segreto in Key Vault con un valore errato ed esegui di nuovo il
test. Il flusso deve terminare con SIGNATURE_INVALID. Ripristina subito il valore corretto.
Verifica la chiamata inbound
Controlla in Console che la nota sia stata aggiunta alla sessione di prova.