Vai al contenuto
Voxiora

Per sviluppatori

Collega Voxiora ai sistemi che usi già

Leggi chiamate, trascrizioni, appuntamenti e contatti tramite REST API, ricevi webhook firmati appena succede qualcosa, lascia che l’assistente controlli dati in tempo reale nel tuo sistema durante la chiamata e aggiungi un assistente vocale al tuo sito con una riga di codice.

REST API e webhook sono inclusi in tutti i piani, da Essential; i piani superiori hanno limiti più alti. Durante la prova gratuita hai un accesso di test: 1 chiave API, 20 richieste al minuto.

URL di base dell’API
https://voxiora.com/api/v1
curl · Prova la chiave
curl https://voxiora.com/api/v1/me \
  -H "Authorization: Bearer tf_live_…"
200 OK
{
  "object": "account",
  "id": "1f2e3d4c-5b6a-4789-8a7b-6c5d4e3f2a1b",
  "name": "Smile Dental",
  "timezone": "Europe/Warsaw",
  "locale": "en",
  "country": "PL",
  "currency": "PLN",
  "status": "active",
  "plan": {
    "id": "team",
    "billing_cycle": "month",
    "trial_ends_at": null,
    "period_start": "2026-10-01T00:00:00Z",
    "period_end": "2026-11-01T00:00:00Z",
    "recording_history_days": 365
  },
  "minutes": {
    "included": 3000,
    "extra": 100,
    "total": 3100,
    "used": 412.5,
    "remaining": 2687.5
  },
  "api_key_id": "2a3b4c5d-6e7f-4a8b-9c0d-1e2f3a4b5c6d",
  "scopes": [
    "read:calls",
    "read:appointments",
    "read:contacts",
    "read:tasks"
  ],
  "limits": {
    "plan": "team",
    "requests_per_minute": 180,
    "requests_per_day": 50000,
    "max_api_keys": 10,
    "max_webhook_endpoints": 10
  }
}

Avvio rapido

La tua prima richiesta in due minuti

  1. 1

    Crea una chiave API

    Nella dashboard vai su Sviluppatori → Chiavi API, dai un nome alla chiave e scegli i permessi. La chiave (tf_live_…) viene mostrata una sola volta: salvala subito nel tuo gestore di segreti.

  2. 2

    Prova la connessione

    Chiama GET /me. Funziona con qualsiasi chiave valida e restituisce il tuo account, i permessi della chiave e i limiti del piano.

  3. 3

    Leggi i dati o iscriviti agli eventi

    Elenca chiamate, appuntamenti e contatti, oppure aggiungi un endpoint in Sviluppatori → Webhook per ricevere gli eventi in tempo reale.

curl · Prova la chiave
curl https://voxiora.com/api/v1/me \
  -H "Authorization: Bearer tf_live_…"
JavaScript: tutte le chiamate con prenotazione, pagina per pagina
const API = "https://voxiora.com/api/v1";
const headers = { Authorization: `Bearer ${process.env.VOXIORA_API_KEY}` };

// All calls booked since 1 October, page by page
let cursor = null;
while (true) {
  const url = new URL(`${API}/calls`);
  url.searchParams.set("outcome", "booked");
  url.searchParams.set("since", "2026-10-01T00:00:00Z");
  url.searchParams.set("limit", "100");
  if (cursor) url.searchParams.set("cursor", cursor);

  const res = await fetch(url, { headers });
  if (res.status === 429) {
    // rate limited: wait for the number of seconds in Retry-After, then retry the same page
    await new Promise((r) => setTimeout(r, Number(res.headers.get("Retry-After") ?? 1) * 1000));
    continue;
  }
  if (!res.ok) throw new Error((await res.json()).error.message);

  const page = await res.json();
  for (const call of page.data) console.log(call.started_at, call.from, call.summary);
  if (!page.has_more) break;
  cursor = page.next_cursor;
}

REST API v1

Chiamate, appuntamenti e contatti via HTTPS

JSON in entrata e in uscita. Ogni richiesta è autenticata con una chiave API e ha i limiti del tuo piano. Le richieste compaiono nella dashboard (metodo, percorso, stato e durata, mai il contenuto di richieste o risposte).

Autenticazione

Crea una chiave in Chiavi API e inviala come token Bearer. Le chiavi iniziano con tf_live_ e appartengono a un solo account.

Authorization: Bearer tf_live_…

Paginazione

Gli endpoint di elenco restituiscono fino a limit elementi (1–100, predefinito 25) in un ordine stabile. Se has_more è true, passa next_cursor invariato come cursor (con gli stessi filtri) per ottenere la pagina successiva. Un cursore non valido restituisce 400 invalid_request.

Errori

Gli errori usano i codici di stato HTTP standard e un corpo JSON con un tipo e un messaggio leggibile.

{ "error": { "type": "…", "message": "…" } }

Endpoint

  • GET/mePermesso: qualsiasi chiave

    Recupera l’account corrente

    Restituisce l’account a cui appartiene la chiave API. Utile come chiamata di “test della connessione”.

  • GET/assistantsPermesso: qualsiasi chiave

    Elenca gli assistenti

    I tuoi assistenti con lingua, stato e numeri di telefono assegnati. Usa l’id come assistant_id per filtrare le chiamate.

  • GET/assistants/{id}Permesso: qualsiasi chiave

    Recupera un assistente

    Un assistente con lingua, stato e numeri di telefono. Con lo scope read:assistants (o write:assistants) restituisce anche il prompt: instructions, greeting, company_profile e updated_at.

  • PATCH/assistants/{id}Permesso: write:assistants

    Modifica il prompt dell’assistente

    Modifica istruzioni, saluto o profilo aziendale. I campi omessi restano invariati. Stessi limiti della dashboard: istruzioni fino a 40.000 caratteri, profilo fino a 10.000, saluto di 5–600 caratteri che deve dire che chi chiama parla con un’IA (altrimenti 400 con code ai_disclosure). Ogni modifica finisce nella cronologia versioni dell’assistente (origine API) e vale dalla chiamata successiva. Le regole di onestà della piattaforma (avviso IA e registrazione) valgono sempre.

  • GET/phone-numbersPermesso: qualsiasi chiave

    Elenca i numeri di telefono

    I numeri di telefono dell’account con paese, tipo, stato e l’assistente che risponde.

  • GET/callsPermesso: read:calls

    Elenca le chiamate

    Chiamate dalla più recente, senza trascrizioni. Le chiamate demo non sono mai incluse.

  • GET/calls/{id}Permesso: read:calls

    Recupera una chiamata

    Una singola chiamata con la trascrizione completa e gli appuntamenti prenotati durante la chiamata.

  • GET/calls/{id}/recordingPermesso: read:calls

    Ottieni la registrazione di una chiamata

    Link firmati (validi 1 ora) per ascoltare e scaricare la registrazione MP3 (stereo: chiamante a sinistra, assistente a destra). Una registrazione più vecchia dello storico del tuo piano restituisce 403 plan_required con recording_history_days; una chiamata senza registrazione restituisce 404.

  • GET/appointmentsPermesso: read:appointments

    Elenca gli appuntamenti

    Prossimi appuntamenti in ordine di inizio (per impostazione predefinita da adesso).

  • GET/appointments/{id}Permesso: read:appointments

    Recupera un appuntamento

    Un appuntamento del tuo account.

  • POST/appointments/{id}/cancelPermesso: write:appointments

    Annulla un appuntamento

    Annulla un appuntamento futuro, lo rimuove dal calendario Google collegato e invia il webhook appointment.canceled. calendar_sync indica se il calendario è stato aggiornato.

  • POST/appointments/{id}/reschedulePermesso: write:appointments

    Sposta un appuntamento

    Sposta un appuntamento futuro a un nuovo orario. Risponde 409 se l’orario si sovrappone a un altro appuntamento o a un periodo occupato nel calendario collegato. Senza ends_at la durata resta invariata. Invia il webhook appointment.rescheduled.

  • GET/contactsPermesso: read:contacts

    Elenca i contatti

    Le persone che ti hanno chiamato, dalla più recente. L’assistente le riconosce da una chiamata all’altra.

  • GET/contacts/{id}Permesso: read:contacts

    Recupera un contatto

    Un contatto con nome, email, tag, note e l’origine di nome ed email (call, system o manual).

  • POST/contactsPermesso: write:contacts

    Crea un contatto

    Aggiunge un contatto, ad esempio dal tuo CRM, così l’assistente conosce il nome di chi chiama. Nome ed email salvati tramite API sono marcati manual e l’assistente non li sovrascrive mai. Restituisce 409 con contact_id se il numero esiste già. Invia il webhook contact.created.

  • PATCH/contacts/{id}Permesso: write:contacts

    Aggiorna un contatto

    Modifica nome, email, note o blocco. I campi omessi restano invariati e null svuota un campo. Un nome o un’email modificati sono marcati manual, quindi l’assistente non li sovrascriverà. Il numero di telefono non si può cambiare.

  • GET/tasksPermesso: read:tasks

    Elenca le attività

    Richieste di richiamata, messaggi e altre attività dalle chiamate, dalla più recente.

  • GET/tasks/{id}Permesso: read:tasks

    Recupera un’attività

    Un’attività del tuo account.

  • PATCH/tasks/{id}Permesso: write:tasks

    Aggiorna un’attività

    Imposta lo stato dell’attività su open, done o dismissed, ad esempio dopo che il tuo team ha richiamato il cliente.

  • POST/calls/outboundPermesso: write:calls

    Crea una chiamata in uscita

    Il tuo assistente chiama una persona dal numero della tua azienda con l’obiettivo che indichi, come un passaggio di una sequenza di onboarding. La chiamata parte subito o a call_at, sempre negli orari di chiamata (lun–sab, 9:00–20:00 nel fuso orario del tuo account; fuori orario viene spostata al primo momento consentito e call_at indica quando). Se nessuno risponde, riprova fino a 3 volte, a 2 ore di distanza. Restituisce 202 con la chiamata nello stato scheduled. Errori: 400 consent_missing, invalid_phone, number_not_callable, international o invalid_call_at; 409 opted_out (la persona ha chiesto di non essere chiamata), already_scheduled (con existing_id), assistant_not_live, over_quota o no_outbound_number; 429 daily_outbound_limit con limit (200 chiamate ogni 24 ore per impostazione predefinita). L’esito arriva nei webhook call.completed e outbound_call.finished con il tuo external_id.

  • GET/calls/outbound/{id}Permesso: write:calls

    Recupera una chiamata in uscita

    Stato di una chiamata in uscita pianificata: scheduled, dialing, done (con il call_id della conversazione), failed (last_error, ad es. no_answer dopo tutti i tentativi) o canceled. Funziona anche con il callback_id dei webhook per le chiamate di onboarding e le richiamate.

  • DELETE/calls/outbound/{id}Permesso: write:calls

    Annulla una chiamata in uscita

    Annulla una chiamata in uscita creata tramite API che è ancora pianificata. Si può ripetere senza rischi (se è già annullata restituisce 200). Restituisce 409 not_cancelable se la chiamata è in composizione o conclusa, oppure per le chiamate di onboarding (in quel caso rimuovi la persona con POST /api/outreach/unenroll).

  • GET/messagesPermesso: read:messages

    Elenco SMS

    SMS inviati dal tuo account e risposte dei clienti, dal più recente. phone è il numero del cliente (destinatario o mittente di una risposta). generated_by: template, ai o manual; charged_from: plan o credits.

  • POST/messagesPermesso: write:messages

    Inviare un SMS

    Invia un SMS a uno dei tuoi contatti o chiamanti: un testo tuo o un modello del tuo account con vars (date, time, name, service, link, amount). Conta nel limite SMS del piano e negli SMS aggiuntivi. Errori: 400 invalid_request (consent_missing, invalid_phone, too_long, missing_vars); 409 conflict (opted_out — il cliente ha risposto STOP, over_quota, recipient_not_allowed — crea prima il contatto, blocked_destination); 429 rate_limited.

Permessi

Ogni chiave ha le autorizzazioni scelte alla creazione. Le autorizzazioni non si possono cambiare in seguito: se te ne servono altre, crea una nuova chiave. Se alla chiave manca l’autorizzazione richiesta, gli endpoint rispondono 403 insufficient_scope. GET /me, /assistants e /phone-numbers funzionano con qualsiasi chiave valida.

  • read:callsLeggere chiamate, riepiloghi e trascrizioni
  • read:appointmentsLeggere appuntamenti
  • write:appointmentsAnnullare e spostare appuntamenti
  • read:contactsLeggere contatti
  • write:contactsCreare e modificare contatti
  • read:tasksLeggere attività e richieste di richiamata
  • write:tasksCambiare lo stato delle attività
  • write:callsAvviare chiamate in uscita, verificarle e annullarle
  • read:messagesLeggere gli SMS e le risposte dei clienti
  • write:messagesInviare SMS ai tuoi contatti
  • read:assistantsLeggere il prompt dell’assistente (istruzioni, saluto, profilo aziendale)
  • write:assistantsModificare il prompt dell’assistente

Errori

  • 400invalid_request: un parametro manca o non è valido
  • 401unauthorized: chiave API mancante, non valida o revocata
  • 403insufficient_scope: alla chiave manca l’autorizzazione dell’endpoint; plan_required: l’account non ha un piano attivo oppure la registrazione è più vecchia dello storico registrazioni del piano
  • 404not_found: l’oggetto non esiste nel tuo account
  • 409conflict — l’orario è già occupato, l’appuntamento non può essere modificato, esiste già un contatto con questo numero oppure non è possibile effettuare una chiamata in uscita (code: opted_out, already_scheduled, assistant_not_live, over_quota, no_outbound_number, not_cancelable)
  • 429rate_limited — troppe richieste; riprova dopo il tempo indicato nell’intestazione Retry-After
  • 500server_error: si è verificato un problema da parte nostra
  • 503service_unavailable: problema temporaneo; riprova dopo il tempo indicato nell’header Retry-After

La documentazione completa dell’API, con parametri e risposte di esempio, è nella dashboard in Sviluppatori → Documentazione API.

Webhook

Eventi inviati al tuo server in tempo reale

Aggiungi un endpoint https nella dashboard, scegli gli eventi e invieremo una busta JSON firmata (POST) appena succede qualcosa. “Invia test” consegna un esempio del tuo primo evento, utile per mappare i campi in Zapier o Make.

Eventi

  • call.completedChiamata completata

    Riepilogo, esito, sentiment, trascrizione, contatto e prenotazioni; per le chiamate in uscita anche la sequenza o la richiesta API (callback_id, external_id, outreach).

  • call.startedChiamata iniziata

    È stata risposta una chiamata (in entrata, in uscita o dal web).

  • appointment.bookedAppuntamento prenotato

    L’assistente ha prenotato un appuntamento.

  • appointment.canceledAppuntamento annullato

    Un appuntamento è stato annullato al telefono o tramite API.

  • appointment.rescheduledAppuntamento spostato

    Un appuntamento è stato spostato a un nuovo orario (con l’orario precedente).

  • task.createdRichiamata / attività creata

    Chi ha chiamato ha chiesto una richiamata o ha lasciato un’attività.

  • contact.createdNuovo contatto

    Una persona che chiamava per la prima volta è stata salvata come contatto.

  • outbound_call.finishedChiamata in uscita conclusa

    Una chiamata in uscita pianificata (API, sequenza di onboarding o richiamata) è conclusa o non è riuscita, anche quando nessuno ha risposto dopo tutti i tentativi.

Intestazioni di ogni consegna

  • Voxiora-SignatureFirma: t=<tempo unix>,v1=<HMAC-SHA256 hex>
  • Voxiora-EventTipo di evento, ad es. appointment.booked
  • Voxiora-DeliveryID univoco della consegna
  • User-AgentVoxiora-Webhooks/1.0 · Identifica il nostro mittente dei webhook

Consegna e nuovi tentativi

  • Rispondi con qualsiasi stato 2xx entro 10 secondi. I reindirizzamenti non vengono seguiti.
  • Le consegne non riuscite vengono ripetute dopo 1 min, 5 min, 30 min, 2 h, 6 h e 12 h (7 tentativi, circa 21 ore).
  • Lo stesso evento può arrivare più di una volta. Usa l’id dell’evento per scartare i duplicati.
  • Sono accettati solo indirizzi https pubblici. Dopo 50 errori consecutivi l’endpoint viene messo in pausa e la dashboard ne spiega il motivo.
  • La dashboard conserva un registro delle consegne con codici di stato e risposte e permette di ripetere una consegna a mano.
Consegna di esempio: call.completed
{
  "id": "evt_3f9a1c7e5b2d4f6a8c0e1b3d5f7a9c1e",
  "type": "call.completed",
  "created_at": "2026-10-01T09:17:45Z",
  "account_id": "1f2e3d4c-5b6a-4789-8a7b-6c5d4e3f2a1b",
  "data": {
    "id": "5b1f0c6e-2a8d-4f3e-9d51-7c0a4e2b9f10",
    "object": "call",
    "assistant_id": "0e6f4c8a-3b2d-4a1e-8f7c-5d9b2a1c3e4f",
    "phone_number_id": "4d3c2b1a-0f9e-4d8c-b7a6-958473625140",
    "contact_id": "9a8b7c6d-5e4f-4a3b-2c1d-0e9f8a7b6c5d",
    "direction": "inbound",
    "status": "completed",
    "from": "+48601234567",
    "to": "+48221234567",
    "started_at": "2026-10-01T09:14:03Z",
    "ended_at": "2026-10-01T09:17:41Z",
    "duration_sec": 218,
    "summary": "Anna Kowalska booked a first consultation for Friday 10:00.",
    "outcome": "booked",
    "sentiment": "positive",
    "tags": [
      "new-customer"
    ],
    "has_recording": true,
    "end_reason": "caller_hangup",
    "page_url": null,
    "page_title": null,
    "continued_from": null,
    "callback_id": null,
    "external_id": null,
    "outreach": null,
    "callback_return": false,
    "caller_spoke": true,
    "caller_words": 6,
    "transcript": [
      {
        "role": "caller",
        "text": "I'd like to book a consultation this week.",
        "at": 4.1
      }
    ],
    "contact": {
      "id": "9a8b7c6d-5e4f-4a3b-2c1d-0e9f8a7b6c5d",
      "object": "contact",
      "phone": "+48601234567",
      "name": "Anna Kowalska",
      "name_source": "call",
      "email": "anna.kowalska@example.com",
      "email_source": "call",
      "notes": null,
      "tags": [
        "customer"
      ],
      "blocked": false,
      "calls_count": 3,
      "last_call_at": "2026-10-01T09:14:03Z",
      "created_at": "2026-08-12T15:02:11Z"
    },
    "appointments": [
      {
        "id": "c3d2e1f0-a9b8-4c7d-8e6f-5a4b3c2d1e0f",
        "object": "appointment",
        "call_id": "5b1f0c6e-2a8d-4f3e-9d51-7c0a4e2b9f10",
        "contact_id": "9a8b7c6d-5e4f-4a3b-2c1d-0e9f8a7b6c5d",
        "customer_name": "Anna Kowalska",
        "customer_phone": "+48601234567",
        "customer_email": "anna.kowalska@example.com",
        "service": "First consultation",
        "starts_at": "2026-10-03T08:00:00Z",
        "ends_at": "2026-10-03T08:30:00Z",
        "status": "booked",
        "external_id": "google:7h3k9s2l1m0n",
        "created_at": "2026-10-01T09:16:20Z"
      }
    ]
  }
}

Verifica delle firme. Ogni consegna include un header Voxiora-Signature nel formato t=timestamp,v1=signature. Calcola l’HMAC-SHA256 di “timestamp.raw_body” con il tuo segreto di firma e confrontalo con v1. Rifiuta i timestamp più vecchi di 5 minuti.

Node.js · Verifica la firma
import crypto from "node:crypto";

// Express: app.post("/webhook", express.raw({ type: "application/json" }), handler)
export function verify(rawBody, signatureHeader, secret) {
  // Voxiora-Signature: t=1727774265,v1=5f2c…
  const parts = Object.fromEntries(signatureHeader.split(",").map((p) => p.split("=")));
  const expected = crypto.createHmac("sha256", secret).update(`${parts.t}.${rawBody}`).digest("hex");
  const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
  const given = Buffer.from(parts.v1 ?? "");
  // timingSafeEqual needs buffers of the same length
  return fresh && given.length === expected.length && crypto.timingSafeEqual(Buffer.from(expected), given);
}
Python · Verifica la firma
import hmac, hashlib, time

def verify(raw_body: bytes, signature_header: str, secret: str) -> bool:
    parts = dict(p.split("=", 1) for p in signature_header.split(","))
    expected = hmac.new(secret.encode(), f"{parts['t']}.".encode() + raw_body, hashlib.sha256).hexdigest()
    fresh = abs(time.time() - int(parts["t"])) < 300
    return fresh and hmac.compare_digest(expected, parts.get("v1", ""))
Ricevitore di esempio (Express)
import express from "express";
import { verify } from "./verify.js";

const app = express();
const seen = new Set(); // use your database in production

app.post("/voxiora/webhook", express.raw({ type: "application/json" }), (req, res) => {
  const raw = req.body.toString("utf8");
  if (!verify(raw, req.get("Voxiora-Signature") ?? "", process.env.VOXIORA_WEBHOOK_SECRET)) return res.sendStatus(401);

  const event = JSON.parse(raw);
  if (seen.has(event.id)) return res.sendStatus(200); // retried delivery, already handled
  seen.add(event.id);

  if (event.type === "appointment.booked") {
    const a = event.data;
    console.log("New booking:", a.customer_name, a.service, a.starts_at);
  }
  res.sendStatus(200); // answer with 2xx within 10 seconds
});

app.listen(3000);

Valori stabili

Questi valori nelle chiamate (API e webhook call.*) e nelle chiamate in uscita (outbound_call.finished, /calls/outbound) sono un contratto stabile: potremmo aggiungere nuovi valori, ma non modificheremo né rimuoveremo questi. I tentativi in uscita senza risposta, occupati o rifiutati non creano mai una chiamata, quindi per loro non c’è call.completed: dopo l’ultimo tentativo ricevi outbound_call.finished con last_error no_answer. Una chiamata a cui risponde la segreteria telefonica conta come risposta (di solito una chiamata breve con outcome other).

Stato della chiamata

in_progress
La chiamata è in corso.
completed
La persona ha parlato con l’assistente.
missed
Nessuno ha parlato, oppure l’assistente non ha potuto rispondere (vedi end_reason).
failed
Non è stato possibile gestire la chiamata a causa di un errore.

Esito della chiamata

booked
È stato prenotato un appuntamento.
transferred
La chiamata è stata trasferita a una persona.
message
È stato raccolto un messaggio, una richiesta di richiamata, un ordine o un follow-up.
info
La persona ha ricevuto informazioni; non serviva altro.
spam
Spam, telemarketing o numero bloccato.
other
Qualsiasi altro caso, ad es. una chiamata molto breve o la segreteria.

Motivo di fine chiamata (end_reason)

caller_hangup
L’altra persona ha riattaccato.
caller_hangup_greeting
Chiamata in uscita: la persona ha riattaccato durante il saluto o subito dopo, senza dire una parola (chiamata fino a 45 s). In questo caso caller_spoke è false.
agent_hangup
L’assistente ha chiuso la chiamata dopo i saluti.
transferred
La chiamata è stata trasferita a una persona.
silence
Terminata dopo un lungo silenzio.
max_duration
È stata raggiunta la durata massima della chiamata.
spam
Terminata come spam.
blocked
Il numero è bloccato nei contatti.
busy
Tutte le linee dell’account erano occupate.
over_quota
Minuti di chiamata esauriti.
trial_expired
La prova gratuita è terminata.
inactive
Nessun piano attivo.
assistant_not_live
L’assistente è in pausa.
no_assistant
Nessun assistente risponde a questo numero.
ai_budget
Gestita senza IA a causa di un limite temporaneo; al team è stato chiesto di richiamare.
error
Un errore tecnico ha chiuso la chiamata.

Stato della chiamata in uscita

scheduled
In attesa del suo orario (o del prossimo tentativo).
dialing
Sta chiamando ora.
done
Risposta; call_id è la conversazione.
failed
Nessuna risposta dopo tutti i tentativi, oppure non è stato possibile effettuarla (vedi last_error).
canceled
Annullata (API, persona rimossa dalla sequenza o sequenza disattivata).

last_error della chiamata in uscita

no_answer
Nessuno ha risposto (anche linea occupata o chiamata rifiutata).
failed
L’assistente non era disponibile al momento della chiamata (piano, minuti o pausa).
no_result
Nessun esito della chiamata entro 15 minuti.
expired
La chiamata era in ritardo di oltre 2 ore, quindi non è stata effettuata.
outside_window
Spostata ai prossimi orari di chiamata (lo stato resta scheduled).
dial_error
La rete telefonica ha rifiutato la chiamata (inviato come dial_<reason>).
over_quota
Minuti di chiamata esauriti.
inactive
Nessun piano attivo.
assistant_not_live
L’assistente è in pausa.
no_outbound_number
Nessun numero aziendale da cui chiamare.
canceled_by_api
Annullata con DELETE /calls/outbound/{id}.
unenrolled
La persona è stata rimossa dalla sequenza (POST /api/outreach/unenroll).
opt_out
La persona ha chiesto di non essere chiamata.
replaced
Sostituita da una richiamata più recente allo stesso numero.

Dati in diretta via MCP

L’assistente consulta il tuo sistema durante la chiamata

Collega un server MCP remoto (Model Context Protocol), come il catalogo del tuo negozio, il magazzino o il sistema di prenotazioni o ordini. Scegli tu esattamente quali strumenti e risorse (sola lettura) può usare l’assistente.

Impara in anticipo

Per contenuti che cambiano di rado, come listini, descrizioni dei prodotti o condizioni. Le risorse e gli strumenti di sola lettura scelti vengono importati nelle conoscenze dell’assistente ogni 1–168 ore o su richiesta. Nessun ritardo durante la chiamata.

Controlla in diretta in chiamata

Per ciò che cambia: giacenze, disponibilità, stato dell’ordine. L’assistente usa lo strumento durante la conversazione con un breve “un attimo, controllo”. Se il tuo server non risponde entro circa 2,5 secondi, dice che ora non può confermarlo e propone un ricontatto dal tuo team.

Cosa ti serve

  • Un server MCP raggiungibile via https (Streamable HTTP, ad es. https://mcp.example.com/mcp; i vecchi server HTTP+SSE vengono rilevati automaticamente).
  • Autenticazione: nessuna, un token Bearer o un’intestazione personalizzata come X-API-Key. I segreti sono cifrati (AES-256-GCM) e non arrivano mai al browser.
  • Strumenti di sola lettura. Quelli segnati come distruttivi sono bloccati; per quelli non segnati come di sola lettura confermi che leggono soltanto dati.
  • Fino a 5 fonti e 50 strumenti in diretta per assistente. I controlli in diretta dovrebbero rispondere in circa 2 secondi.
  • Facoltativo per il widget del sito: in uno strumento «All’inizio della chiamata», scegli l’argomento che riceve il token di identità del visitatore che ha effettuato l’accesso (vedi Widget vocale per il sito).

Gli argomenti vengono validati con lo schema dello strumento, i risultati vengono accorciati e trattati solo come dati (mai come istruzioni) e ogni controllo viene registrato senza i dati di chi chiama. Gli indirizzi privati e interni vengono rifiutati.

Server MCP per agenti IA

Dai al tuo agente IA l’accesso a Voxiora

Voxiora offre un server MCP remoto (Model Context Protocol). Collega Claude, ChatGPT, Cursor o il tuo agente: legge chiamate, trascrizioni, appuntamenti, contatti e attività e, con il tuo permesso, aggiunge contatti, avvia chiamate in uscita e invia SMS.

  • Endpoint: https://voxiora.com/api/mcp (Streamable HTTP, JSON-RPC 2.0, senza stato).
  • Autenticazione: la stessa chiave API della REST API (Authorization: Bearer tf_live_…) oppure un token di accesso OAuth valido 1 ora ottenuto con client credentials. Senza, il server risponde 401 con un rimando ai metadati OAuth.
  • L’agente vede solo gli strumenti consentiti dagli scope della chiave, e ogni chiamata passa gli stessi controlli, limiti e registro delle richieste della REST API.
  • Chiamate in uscita e SMS richiedono consent: true, come nell’API. I risultati degli strumenti sono dati dei tuoi clienti, mai istruzioni.

OAuth per agenti

Scambia una chiave API con un token di accesso di breve durata su https://voxiora.com/api/oauth/token: client_id è l’id della chiave (api_key_id da GET /me) o il suo prefisso, client_secret è la chiave. Il token riceve gli scope richiesti che la chiave possiede. Revocando la chiave nella dashboard decadono tutti i token emessi con essa.

Gli agenti possono anche chiedere l’accesso da soli: POST https://voxiora.com/api/agent-auth/register restituisce un codice, il titolare dell’account lo approva nella dashboard (Approva un agente IA) e l’agente riceve una sola volta la propria chiave API. Prima dell’approvazione non viene concesso nulla; dettagli in auth.md.

Strumenti

  • get_accountqualsiasi chiave
  • list_assistantsqualsiasi chiave
  • list_phone_numbersqualsiasi chiave
  • list_callsread:calls
  • get_callread:calls
  • list_contactsread:contacts
  • get_contactread:contacts
  • create_contactwrite:contacts
  • list_appointmentsread:appointments
  • get_appointmentread:appointments
  • list_tasksread:tasks
  • list_messagesread:messages
  • start_outbound_callwrite:calls
  • get_outbound_callwrite:calls
  • send_messagewrite:messages

Documenti di discovery

curl · Connessione: initialize
curl https://voxiora.com/api/mcp \
  -H "Authorization: Bearer tf_live_…" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"my-agent","version":"1.0"}}}'
curl · Token di accesso: client credentials
curl https://voxiora.com/api/oauth/token \
  -u "<api_key_id>:tf_live_…" \
  -d grant_type=client_credentials \
  -d scope="read:calls read:contacts"

Widget vocale per il sito

Il tuo assistente sul tuo sito, con una riga di codice

I visitatori cliccano un pulsante e parlano, dal browser, con lo stesso assistente che risponde al tuo telefono. Le chiamate web usano i minuti del piano, senza costi di telefonia.

Da Business

  • Circa 21 KB, senza dipendenze, isolato in Shadow DOM: gli stili del tuo sito non possono romperlo.
  • Parla la lingua della tua pagina (<html lang>) o quella impostata con data-lang="de".
  • Sottotitoli in diretta; se il microfono è bloccato, il visitatore può scrivere e l’assistente risponde a voce.
  • Limita il widget ai tuoi domini: si avvierà solo su quei siti.
Codice da incorporare (la chiave è nella dashboard)
<script src="https://voxiora.com/widget.js" data-key="tfw_…" async></script>
API JavaScript
// optional: open the widget from your own button
document.querySelector("#talk-to-us").addEventListener("click", () => window.VoxioraWidget?.open());
// open and start right away: { mode: "voice" } or { mode: "chat" } (text only, no microphone)
window.VoxioraWidget?.open({ mode: "chat" });

window.VoxioraWidget?.close();   // close the panel
window.VoxioraWidget?.destroy(); // remove the widget from the page

Hai una Content Security Policy restrittiva? Consenti script-src https://voxiora.com https://cdn.jsdelivr.net, connect-src https://voxiora.com wss://*.livekit.cloud https://*.livekit.cloud e media-src blob:.

Utenti che hanno effettuato l’accesso (token di identità)

Se i visitatori hanno effettuato l’accesso al tuo sito, l’assistente può sapere chi sta parlando senza chiedere l’e-mail.

  • Il tuo server emette un token firmato e di breve durata per l’utente che ha effettuato l’accesso (es. HMAC, valido 15 minuti). Non mettere mai nel browser un indirizzo e-mail o un ID utente: chiunque potrebbe scrivere quello di un altro nella console.
  • Chiama VoxioraWidget.identify({ token }) in qualsiasi momento; una chiamata successiva sostituisce il token e identify(null) lo cancella. Prima che widget.js sia caricato, imposta window.VoxioraIdentity = { token, expires_at }: viene letto all’inizio della conversazione e un token scaduto viene ignorato.
  • Nella dashboard scegli l’argomento del token di identità in qualsiasi strumento MCP dal vivo: di consultazione, di azione o il tuo strumento «All’inizio della chiamata». Voxiora passa il token invariato solo a quell’argomento, non lo salva né lo mostra mai e lo tiene fuori da trascrizioni, riepiloghi, webhook e log; l’assistente non lo vede mai.
  • Da 8 a 400 caratteri: lettere, cifre e . _ ~ + / = - (es. base64url). Rinnovalo prima della scadenza, per esempio ogni 10 minuti. Lo verifica il tuo server MCP; un token non valido o scaduto dovrebbe dare lo stesso risultato dell’assenza di token. Le chiamate telefoniche e le conversazioni senza token funzionano come prima.
Token di identità
// signed-in user: a short-lived, signed token issued by YOUR server (never an e-mail or user ID)
const { token, expires_at } = await fetch("/api/voxiora-identity").then((r) => r.json());

window.VoxioraIdentity = { token, expires_at };       // read when the conversation starts, also before widget.js loads
window.VoxioraWidget?.identify({ token, expires_at }); // replaces the token at any time

// refresh before it expires (e.g. every 10 minutes); on sign-out:
window.VoxioraIdentity = null;
window.VoxioraWidget?.identify(null);

Modalità chat e pulsante personalizzato

Apri l’assistente dalla tua interfaccia — un pulsante, un menu o una reception in una scena 3D — a voce o come chat testuale.

  • data-launcher="none" nasconde il pulsante flottante: il pannello si apre solo dal tuo codice con VoxioraWidget.open() e si chiude con close(). Il focus torna dov’era (ad esempio al canvas del tuo gioco). close() nasconde solo il pannello e la conversazione continua; VoxioraWidget.endCall() o close({ end: true }) la termina (il widget resta).
  • open({ mode: "voice" }) o open({ mode: "chat" }) apre il pannello e avvia subito la conversazione. data-mode="chat" rende la chat la modalità predefinita del pulsante di avvio.
  • La modalità chat non chiede mai il microfono: il visitatore scrive e l’assistente risponde per iscritto. È lo stesso assistente, con le stesse conoscenze e gli stessi strumenti.
  • Ogni metodo funziona prima che il widget sia caricato: le chiamate vengono messe in coda ed eseguite appena è pronto. Anche prima del caricamento di widget.js puoi accodarle in window.VoxioraWidget = { q: [["open", { mode: "chat" }]] }.
Pulsante personalizzato e modalità chat
<!-- no floating button: the panel opens only from your code -->
<script src="https://voxiora.com/widget.js" data-key="tfw_…" data-launcher="none" data-mode="chat" async></script>

<script>
  // calls made before widget.js has loaded are queued and run when it starts
  window.VoxioraWidget = window.VoxioraWidget || { q: [] };
  const voxiora = (method, ...args) =>
    window.VoxioraWidget.q ? window.VoxioraWidget.q.push([method, ...args]) : window.VoxioraWidget[method](...args);

  // e.g. the reception desk in your 3D gallery
  document.querySelector("#talk").addEventListener("click", () => voxiora("open", { mode: "voice" })); // asks for the microphone
  document.querySelector("#chat").addEventListener("click", () => voxiora("open", { mode: "chat" }));  // text only, no microphone

  // later:
  // VoxioraWidget.close();   hide the panel (the conversation continues)
  // VoxioraWidget.isOpen();  true | false
  // VoxioraWidget.state();   { ready, open, mode, call: "idle" | "connecting" | "active", agent_state }
  // VoxioraWidget.init({ launcher: false, mode: "chat", lang: "de" }); // the same options in code
</script>

Eventi

Reagisci alla conversazione nella tua pagina: anima un avatar mentre l’assistente parla, mostra fumetti di chat personalizzati o registra le chiamate.

  • Ogni evento è un CustomEvent di window chiamato voxiora:<nome> e puoi ascoltarlo anche con VoxioraWidget.on(nome, fn) / off(nome, fn).
  • ready { key, assistant, available } · open · close · call_started { mode, roomName } · call_ended { reason } · agent_state { state: listening | thinking | speaking } · error { code }
  • speaking { speaking, level }: livello della voce dell’assistente da 0 a 1, fino a 20 volte al secondo mentre parla e a ogni cambio, anche se il visitatore preferisce ridurre le animazioni.
  • message { role: agent | user, text, final, id }: il testo man mano che arriva (final: false) e completo (final: true), per la tua interfaccia di chat. tool: vedi gli strumenti lato pagina qui sotto. Un intervento = un id: arrivano più eventi con lo stesso id e il testo definitivo ha final: true — uniscili per id.
Animare un avatar con voxiora:speaking
const avatar = document.querySelector("#receptionist");

// { speaking: boolean, level: 0..1 } — up to 20 times per second while the assistant speaks
window.addEventListener("voxiora:speaking", (e) => {
  avatar.classList.toggle("talking", e.detail.speaking);
  avatar.style.setProperty("--mouth", e.detail.level); // e.g. scale the mouth or a glow
  // Unity WebGL: unityInstance.SendMessage("Receptionist", "SetVoiceLevel", e.detail.level);
});

// the same events through the widget API (name with or without the "voxiora:" prefix)
VoxioraWidget.on("call_started", ({ mode, roomName }) => console.log("started", mode, roomName));
VoxioraWidget.on("call_ended", ({ reason }) => console.log("ended", reason)); // user | disconnected | …
VoxioraWidget.on("agent_state", ({ state }) => console.log(state));           // listening | thinking | speaking
VoxioraWidget.on("message", ({ role, text, final }) => final && console.log(role, text)); // your own chat UI
VoxioraWidget.on("error", ({ code }) => console.warn("Voxiora:", code));      // rate_limited, assistant_offline, …

Contesto della pagina

Di’ all’assistente dove si trova il visitatore — in quale galleria, negozio o prodotto — così risponde senza chiedere.

  • VoxioraWidget.setContext({ … }) oppure data-context='{"gallery":"POL"}' nel tag script; setContext(null) lo cancella. Durante una conversazione il nuovo contesto arriva subito all’assistente, insieme all’indirizzo e al titolo attuali della pagina (anche dopo una navigazione senza ricaricare); altrimenti vale per la conversazione successiva.
  • Fino a 20 chiavi (minuscole, cifre e _, iniziando con una lettera); valori: testo fino a 200 caratteri, numeri o true/false, al massimo 2000 byte in totale. Un contesto non valido viene ignorato con un avviso in console e resta quello precedente.
  • Nelle istruzioni dell’assistente usa {{context.gallery}}, {{context.store}} ecc.; l’assistente vede il contesto anche come informazione sulla pagina.
  • Il tuo server MCP lo riceve nell’intestazione Voxiora-Context (JSON compatto, caratteri non ASCII come \uXXXX, fino a 1000 byte; oltre restano le chiavi che ci stanno, prima le secret_) insieme a Voxiora-Call-Id. Trattalo come dato non affidabile: lo imposta il browser del visitatore. Le chiavi che iniziano con secret_ (ad es. secret_session) arrivano solo al tuo server MCP: l’assistente non le vede mai, non vengono mai salvate, registrate né mostrate e, se il tuo server ne ripete una, viene sostituita con [secret].
Contesto della pagina
// what the visitor is looking at; used by the next conversation
VoxioraWidget.setContext({ gallery: "POL", store: "Lumi Shoes", floor: 2, logged_in: true });
VoxioraWidget.setContext(null); // clear

// or on the script tag: data-context='{"gallery":"POL"}'

// assistant instructions:
//   The visitor is in gallery {{context.gallery}}, at the store {{context.store}}.

// every live lookup on your MCP server during that conversation carries:
//   Voxiora-Call-Id: 5b1f0c6e-…
//   Voxiora-Context: {"gallery":"POL","store":"Lumi Shoes","floor":2,"logged_in":true}
// (non-ASCII as \uXXXX — JSON.parse restores it; set by the visitor's browser, so treat it as untrusted)

Strumenti lato pagina

Lascia che l’assistente agisca nel browser del visitatore — mostrare un negozio, aprire un prodotto, leggere il carrello — con funzioni che registri nella pagina.

  • VoxioraWidget.registerTools([...]): fino a 10 strumenti con name (minuscole, cifre, _), description (cosa fa e quando usarlo, fino a 500 caratteri) e parameters come JSON Schema di tipo object (fino a 4000 byte). L’elenco vale per la conversazione successiva.
  • Quando l’assistente chiama uno strumento, viene eseguito il tuo handler(args, { waitForUser }) (può restituire un valore o una Promise) ed emesso l’evento voxiora:tool con name, args, respond(risultato), reject(errore) e waitForUser(). Vince la prima risposta.
  • Rispondi entro 9 secondi, altrimenti l’assistente saprà che lo strumento non ha risposto. Se l’azione richiede il visitatore (ad es. la conferma di un pagamento), chiama prima waitForUser(): l’assistente dice che sta aspettando e hai fino a 29 secondi. I risultati sono limitati a 15.000 byte di JSON; gli strumenti sconosciuti vengono rifiutati.
  • Gli argomenti arrivano dal modello di IA: validali come qualsiasi input dell’utente e non eseguire mai senza chiedere azioni che richiedono la conferma del visitatore.
Strumenti lato pagina
VoxioraWidget.registerTools([
  {
    name: "show_store",
    description: "Moves the visitor's view to a store in the gallery. Use it when they ask where a store is.",
    parameters: { type: "object", properties: { store_id: { type: "string" } }, required: ["store_id"] },
    // runs in the visitor's browser; the returned value (or Promise) goes back to the assistant
    handler: async ({ store_id }) => {
      await gallery.flyTo(String(store_id)); // your code — validate arguments like any user input
      return { shown: true };
    },
  },
  { name: "cart_count", description: "Number of items in the visitor's cart.", parameters: { type: "object", properties: {} } },
]);

// or answer from an event listener — the first answer wins; no answer within 9 s = "timeout"
window.addEventListener("voxiora:tool", (e) => {
  const { name, args, respond, reject } = e.detail;
  if (name === "cart_count") respond({ items: cart.length });
});

Più assistenti in una pagina

Ogni assistente ha la propria chiave widget. Passa dall’uno all’altro senza ricaricare la pagina, ad esempio tra la reception e l’assistente di un negozio.

  • VoxioraWidget.switchTo({ key }) termina la conversazione in corso, rimuove il widget e lo riavvia con l’altra chiave; un open() chiamato subito dopo attende che il nuovo assistente sia pronto. Contesto (setContext), strumenti (registerTools) e listener (on) restano: non serve registrarli di nuovo; cambia solo il contesto se l'altro assistente ne richiede uno diverso (ad es. place).
  • Gli ascoltatori di eventi, il contesto della pagina e gli strumenti restano; ogni chiave ha il proprio stato di ripresa della conversazione. Aggiungi il tuo dominio ai domini consentiti di ogni chiave.
  • VoxioraWidget.destroy() rimuove completamente il widget: interfaccia, ascoltatori, conversazione e stato di ripresa salvato.
Passare da un assistente all’altro
const RECEPTION = "tfw_…"; // widget key of the gallery reception
const STORE = "tfw_…";     // widget key of a store's own assistant

document.querySelector("#store-desk").addEventListener("click", () => {
  VoxioraWidget.switchTo({ key: STORE }); // ends the conversation, reloads the widget with the other assistant
  VoxioraWidget.open({ mode: "voice" });  // queued until the new assistant is ready
});

VoxioraWidget.on("ready", ({ key, assistant }) => console.log("now:", assistant, key === RECEPTION ? "reception" : "store"));

VoxioraWidget.destroy(); // remove the widget completely (interface, listeners, conversation, resume state)

Onboarding telefonico

I tuoi nuovi clienti ricevono in automatico una chiamata di benvenuto

Quando qualcuno si registra nel tuo sistema, inseriscilo in una sequenza di chiamate. L’assistente chiama dal numero della tua azienda, aiuta a iniziare e ricontatta qualche giorno dopo. Le chiamate partono negli orari di chiamata della sequenza: per impostazione predefinita dal lunedì al sabato, dalle 9:00 alle 20:00 nel tuo fuso orario, oppure nei giorni e negli orari che imposti.

POST/api/outreach/enroll

Autenticati con la chiave della sequenza (tlo_…) dalla dashboard, come token Bearer o nell’intestazione X-Voxiora-Key. Ogni sequenza ha la sua chiave.

Corpo della richiesta

  • phonestringobbligatorio
    Numero di telefono, meglio in formato E.164. I numeri nazionali vengono letti con il prefisso del paese del tuo account.
  • consenttrueobbligatorio
    Deve essere true: la persona ha accettato di essere contattata per telefono, ad es. nel tuo modulo di registrazione.
  • namestringfacoltativo
    Nome, fino a 120 caratteri.
  • emailstringfacoltativo
    Indirizzo email.
  • contextstringfacoltativo
    Cosa deve sapere l’assistente su questa persona, fino a 1.000 caratteri (ad es. il piano scelto).
  • external_idstring | numberfacoltativo
    L’ID della persona nel tuo sistema (testo o numero). Lo stesso ID viene iscritto una sola volta.
curl
curl -X POST "https://voxiora.com/api/outreach/enroll" \
  -H "Authorization: Bearer tlo_…" \
  -H "Content-Type: application/json" \
  -d '{"phone":"+48601234567","name":"Anna Kowalska","email":"anna@example.com","context":"Signed up today, Start plan","external_id":"user_123","consent":true}'
201 Created
{
  "ok": true,
  "enrollment_id": "8f7e6d5c-4b3a-4c2d-9e1f-0a9b8c7d6e5f",
  "external_id": "user_123",
  "status": "active",
  "next_call_at": "2026-10-09T10:20:00Z",
  "limits": {
    "per_day": 500,
    "remaining_today": 499
  }
}

Risposte

  • 201: iscritta. enrollment_id, external_id, status, next_call_at (orario della prima chiamata), intro_sms_at (quando parte l’SMS prima della prima chiamata, oppure null) e limits { per_day, remaining_today }
  • 200: già iscritta (stesso external_id, oppure questo numero è già in corso nella sequenza), con duplicate: true
  • 400: invalid_request, invalid_phone o consent_required
  • 401: invalid_key
  • 409: sequence_disabled, consent_missing o opted_out (la persona non vuole essere chiamata)
  • 429: limit, raggiunto il limite giornaliero di iscrizioni della sequenza (500 nuove persone ogni 24 ore; il corpo contiene limit). rate_limited: più di 120 richieste al minuto con questa chiave (intestazione Retry-After)

Orari di chiamata: nella dashboard imposta per ogni sequenza i giorni e una fascia oraria tra le 7:00 e le 21:00. Se nessuno risponde, l’assistente riprova fino a 3 volte, a 2 ore di distanza, entro quegli orari.

SMS prima della chiamata: se è attivo nella sequenza (dashboard), la persona riceve un breve messaggio dal numero da cui chiamerà l’assistente, per impostazione predefinita 20 minuti prima della prima chiamata (5–120 min). Se la prima chiamata cadesse prima, viene spostata in modo che l’SMS parta sempre per primo, entro gli orari di chiamata. Un SMS non riuscito (la persona ha risposto STOP, gli SMS a disposizione sono esauriti) non blocca mai la chiamata. Ogni SMS viene scalato dagli SMS a tua disposizione e compare nella dashboard, nella sezione SMS.

Saluto: nella dashboard imposti la prima frase della chiamata per ogni passaggio (o come predefinita per l’intera sequenza) — un testo fisso con le variabili {first_name}, {first_name_vocative} (vocativo polacco, ad es. Krystianie), {assistant_name}, {brand} (nome dell’azienda nelle chiamate, dalle impostazioni dell’assistente) e {name}, con una variante a parte per le iscrizioni senza nome — oppure una modalità in cui l’assistente scrive la prima frase da name, context, obiettivo del passaggio e risultato degli strumenti «All’inizio della chiamata». Che chiama un assistente IA e che la chiamata è registrata viene sempre aggiunto alla prima frase se il testo non lo dice.

Rimuovere una persona dalla sequenza

POST/api/outreach/unenroll

Quando qualcuno non ha più bisogno delle chiamate (ad es. dopo la prima vendita o se ha disdetto), interrompi la sua sequenza con la stessa chiave. Le chiamate pianificate vengono annullate subito. Una chiamata già in corso non viene interrotta, ma non ne seguono altre.

  • external_id | enrollment_id | phoneobbligatorio
    Esattamente uno tra: external_id (come inviato all’iscrizione), enrollment_id (dalla risposta dell’iscrizione) o phone.
  • reasonfacoltativo
    Motivo facoltativo, fino a 200 caratteri, conservato nel registro di audit del tuo account.
  • 200: interrotta (changed: true) o già conclusa: status stopped, completed o failed (changed: false). Si può ripetere senza rischi.
  • 400: invalid_request (nessun identificativo o più di uno) o invalid_phone
  • 401: invalid_key
  • 404: not_found, nessuna iscrizione corrispondente in questa sequenza
  • 429: rate_limited
curl
curl -X POST "https://voxiora.com/api/outreach/unenroll" \
  -H "Authorization: Bearer tlo_…" \
  -H "Content-Type: application/json" \
  -d '{"external_id":"user_123","reason":"first_sale"}'
200 OK
{
  "ok": true,
  "enrollment_id": "8f7e6d5c-4b3a-4c2d-9e1f-0a9b8c7d6e5f",
  "external_id": "user_123",
  "status": "stopped",
  "changed": true
}

Esiti delle chiamate nei webhook

call.started e call.completed per le chiamate di una sequenza contengono callback_id, external_id e outreach { sequence_id, enrollment_id, external_id, step } (tutti null per le chiamate in entrata). I tentativi senza risposta non creano una chiamata, quindi per loro non c’è call.completed: dopo l’ultimo tentativo ricevi outbound_call.finished con status failed e last_error no_answer. I nuovi tentativi di uno stesso passaggio contano come un unico esito. call.completed include anche caller_spoke e caller_words (quante parole ha detto la persona). Se qualcuno ha risposto e ha riattaccato durante il saluto o subito dopo senza dire nulla, end_reason è caller_hangup_greeting. Se la persona richiama il numero dell’azienda entro 3 giorni dal nostro tentativo, la chiamata in entrata ha callback_return: true e gli stessi callback_id, external_id e outreach della chiamata a cui risponde. Le chiamate dal widget del sito hanno direction widget, page_url e page_title (pagina della chiamata) e continued_from (parte precedente dopo il ricaricamento della pagina); un test nel browser dal pannello è direction web. Ogni richiesta al tuo server MCP durante una chiamata ha l’header Voxiora-Call-Id (uguale all’id della chiamata), Voxiora-Caller nelle chiamate telefoniche e Voxiora-Outreach-External-Id nelle chiamate di sequenza o API.

call.completed per una chiamata di sequenza
{
  "id": "5b1f0c6e-2a8d-4f3e-9d51-7c0a4e2b9f10",
  "object": "call",
  "assistant_id": "0e6f4c8a-3b2d-4a1e-8f7c-5d9b2a1c3e4f",
  "phone_number_id": "4d3c2b1a-0f9e-4d8c-b7a6-958473625140",
  "contact_id": "9a8b7c6d-5e4f-4a3b-2c1d-0e9f8a7b6c5d",
  "direction": "outbound",
  "status": "completed",
  "from": "+48732127980",
  "to": "+48601234567",
  "started_at": "2026-10-01T09:14:03Z",
  "ended_at": "2026-10-01T09:17:41Z",
  "duration_sec": 218,
  "summary": "Jan added the company name and tax ID; asked for a call tomorrow at 10:00 about payments.",
  "outcome": "message",
  "sentiment": "positive",
  "tags": [],
  "has_recording": true,
  "end_reason": "agent_hangup",
  "page_url": null,
  "page_title": null,
  "continued_from": null,
  "callback_id": "0f1e2d3c-4b5a-4968-8776-655443322110",
  "external_id": "gapli-evt-1842",
  "outreach": {
    "sequence_id": "11111111-1111-4111-8111-111111111111",
    "enrollment_id": "8f7e6d5c-4b3a-4c2d-9e1f-0a9b8c7d6e5f",
    "external_id": "gapli-evt-1842",
    "step": 1
  },
  "callback_return": false,
  "caller_spoke": true,
  "caller_words": 112
}

Calendario, CRM e automazioni

Integrazioni pronte, senza codice

  • Google Calendar

    L’assistente verifica la disponibilità reale e prenota, sposta e annulla gli appuntamenti nel tuo calendario. Legge solo gli orari occupati, mai i titoli degli eventi.

  • HubSpot

    Ogni chiamata viene registrata sul contatto, le prenotazioni diventano riunioni e le richiamate attività. Facoltativamente crea contatti e trattative.

  • Pipedrive

    Chiamate come attività o note, prenotazioni come riunioni, richiamate come attività. Facoltativamente crea persone, trattative o lead.

  • Zapier, Make e n8n

    Guide passo passo nella dashboard. Si basano su webhook e API, inclusi in tutti i piani.

Piani e limiti

Di quale piano hai bisogno

Limiti per account: richieste al minuto (anche per chiave, finestra mobile) e al giorno (UTC). Oltre il limite ricevi 429 rate_limited con l’intestazione Retry-After.

PianoRichieste / minRichieste / giornoChiavi APIWebhook
Prova gratuita (accesso di test)20100011
Essential30200022
Pro6010.00055
Business18050.0001010
Enterprise600200.0002520

Widget vocale per il sito: dal piano Business.

Ti servono limiti più alti? Scrivici e possiamo aumentarli per il tuo account.

Inizia oggi

Pronto a collegarti?

Inizia la prova gratuita, crea una chiave API di test e invia la tua prima richiesta in pochi minuti. L’API è inclusa in tutti i piani, da Essential.