Crea su Conecto
Un’API REST chiara per tutto ciò che fa la piattaforma: crea bot che non si limitano a rispondere, ma possono agire , sul database, sulla fatturazione e sul prodotto, invia immagini, GIF, video e schede nella chat e creare le tue integrazioni che l’agente IA chiama durante la conversazione, qualunque sia il software sottostante. Se il widget può farlo, l’API può controllarlo.
In produzione · Webhook firmati · 300 richieste/min
Concetti di base
Base URL https://conecto.chat/api/v1. Create a credential in Impostazioni → Sviluppatori , ottieni un ID client e un segreto (shown once, stored hashed). Authenticate with HTTP Basic — client ID as username, secret as password — or Authorization: Bearer <client_id>:<secret>. A credential can cover the whole workspace or be scoped to a single widget.
curl https://conecto.chat/api/v1/me/ -u "ck_your_client_id:cs_your_secret"Usi Python? Il SDK ufficiale (pip install conecto) wraps all of this, and does the parts that are easy to get subtly wrong for you: webhook signature verification, delivery deduplication, idempotent writes, retries, and block validation that fires before a request leaves your process. Everything below still applies — it is the same API underneath.
Limite di frequenza 300 richieste/min per credential (429 + Retry-After beyond it). Errors are always {"error": {"code", "message"}}. List endpoints paginate with limit e before_id, returning next_before_id. Write endpoints accept an Idempotency-Key header (any UUID): retry the same key and you get the message that was already created, with a 200 instead of a 201, rather than a second copy in front of the visitor.
Un endpoint descrive tutti gli altri
GET /schema/ restituisce l’intera superficie come JSON: ogni endpoint, evento, tipo di blocco, azione riservata, codice di errore e limite numerico. È servito dallo stesso codice che applica questi limiti, quindi non può divergere dal server in esecuzione come una pagina di documentazione. Genera il client da qui o verifica all’avvio che la funzione necessaria sia distribuita.
curl https://conecto.chat/api/v1/schema/ -u "ck_...:cs_..." | jq '.limits, .events[].name'Every response carries X-Conecto-Api-Version. Pin it in your client and log a warning when it changes — that header is how you find out a field moved before your users do.
Contenuti avanzati: immagini, GIF, video e schede
Any message you send can carry blocks — an ordered list of typed content rendered under the bubble in the widget, and in the agent's inbox exactly as the visitor saw it. Up to 10 per message, and every field is validated and re-projected server-side, so you never hand us markup and we never hand the browser a string.
curl -X POST https://conecto.chat/api/v1/conversations/42/messages/ \
-u "ck_...:cs_..." -H "Content-Type: application/json" \
-d '{
"body": "Here is how to reset it — takes about 20 seconds:",
"blocks": [
{"type": "image", "url": "https://cdn.you.com/reset.gif", "alt": "Reset flow"},
{"type": "buttons", "items": [
{"label": "That worked"},
{"label": "Full guide", "url": "https://docs.you.com/reset"}
]}
]
}'Tipi di blocchi
imageurl, alt, caption, link — animated GIFs are just imagesvideourl to an .mp4/.webm/.mov, plus poster, autoplay, loop, mutedembedprovider youtube · vimeo · loom · wistia · spotify e il normale link di condivisione urlaudiourl a un file .mp3/.wav/.m4a, facoltativo titlefileurl, filename, size — rendered as a download rowcardsitems[] of title · subtitle · text · image · url · price · badge · buttons; layout carousel or listlistrows[] di etichetta · valore · sottotitolo · immagine · URL, per riepiloghi degli ordini e fasi di spedizionebuttonsitems[]: {label, value} replies as the visitor, {label, url} opens a linktext · dividerparagrafi aggiuntivi e una linea orizzontaleURLs must be https. A block that can't be built is a 400 naming the block and the reason, never a silent drop — an image that vanishes without explanation costs an afternoon to track down.
Inviare una GIF senza un posto in cui ospitarla
POST /media/ takes a multipart file (≤10 MB) and hands back a URL you can drop straight into a block. Use the returned url — it is a path, resolved against the widget's own origin, so the same message works in development and in production.
URL=$(curl -s -X POST https://conecto.chat/api/v1/media/ -u "ck_...:cs_..." \
-F "file=@celebrate.gif" | jq -r .media.url)
curl -X POST https://conecto.chat/api/v1/conversations/42/messages/ \
-u "ck_...:cs_..." -H "Content-Type: application/json" \
-d "{\"body\": \"All set!\", \"blocks\": [{\"type\": \"image\", \"url\": \"$URL\"}]}"Shipping a GIF as a muted, looping video block is usually the better trade: a tenth of the bytes, and the visitor cannot tell the difference.
Un carosello di schede
{"blocks": [{
"type": "cards",
"items": [
{"title": "Trail Runner 2", "subtitle": "Road · Neutral",
"price": "89.00 USD", "badge": "Back in stock",
"image": "https://cdn.you.com/tr2.jpg",
"url": "https://shop.you.com/p/tr2",
"buttons": [{"label": "Add to cart", "url": "https://shop.you.com/cart/add/tr2"}]}
]
}]}Crea la tua integrazione
Shopify, Stripe e BigCommerce sono integrazioni che noi abbiamo scritto. Questa è quella che tu scrivere. Descrivi il servizio con un URL di base e un elenco di azioni, installalo su un widget e l’agente IA lo chiamerà durante la conversazione. I sistemi a valle non possono distinguerlo da un’integrazione nativa. È proprio questo il punto: se il negozio, la fatturazione o il CRM funziona su una soluzione che non conosciamo, non devi più attendere che la supportiamo.
1 · Registrarla
curl -X POST https://conecto.chat/api/v1/integrations/ \
-u "ck_...:cs_..." -H "Content-Type: application/json" \
-d '{
"slug": "acme-store",
"name": "Acme Store",
"base_url": "https://api.acme.example/conecto",
"auth_type": "bearer",
"credential": "sk_live_...",
"actions": [
{"name": "catalog.search_products", "path": "/search",
"description": "Search the Acme catalog by keyword.",
"parameters": [{"name": "query", "type": "string", "required": true},
{"name": "limit", "type": "integer"}]},
{"name": "orders.get_status", "path": "/orders/status", "risk": "verified_read",
"description": "Look up one of the visitor's orders by number.",
"parameters": [{"name": "order_number", "type": "string", "required": true}]},
{"name": "warranty.register", "path": "/warranty", "risk": "write",
"description": "Register a warranty for a product the visitor owns.",
"parameters": [{"name": "serial", "type": "string", "required": true}]}
]
}'The response includes a signing_secret. You need it to verify our calls — it is readable on every later GET too, and {"rotate_signing_secret": true} rolls it.
2 · Installarla su un widget
curl -X POST https://conecto.chat/api/v1/integrations/acme-store/install/ \
-u "ck_...:cs_..." -H "Content-Type: application/json" \
-d '{"widget_ids": [7], "actions": ["catalog.search_products", "orders.get_status"]}'actions is an allowlist — omit it to enable everything you declared, or pass [] to keep the integration installed but idle. Nothing is exposed until you install it, and installing is per widget.
3 · Rispondere alla chiamata
We POST a signed JSON envelope to base_url + path. Verify the signature the same way you verify a webhook — one routine covers both directions:
// POST https://api.acme.example/conecto/search
{
"action": "catalog.search_products",
"integration": "acme-store",
"arguments": { "query": "trail shoes", "limit": 3 },
"source": "ai_agent",
"idempotency_key": "8f14e45fceea167a...",
"workspace": { "id": 12 },
"widget": { "id": 7, "key": "w_..." },
"conversation": { "id": 4821 },
"visitor": { "session": "…", "email": "maya@acme.io",
"verified": true, "verified_email": "maya@acme.io", "name": "Maya" }
}
// Headers: X-Conecto-Signature: sha256=<HMAC-SHA256(signing_secret, raw body)>
// X-Conecto-Timestamp, X-Conecto-Action, X-Conecto-Integration,
// X-Conecto-Idempotency-KeyRispondi con una di cinque strutture:
{"ok": true, "result": {...}} // success — the agent reads it
{"ok": true, "result": {...}, "blocks": [...]} // …and attach rich content to the reply
{"ok": true, "not_found": true} // nothing matched (a normal outcome)
{"verify_required": true} // "I need a verified visitor for this"
{"ok": false, "error": "Already cancelled."} // a failure the agent may relayUn semplice oggetto JSON privo di queste chiavi viene considerato il risultato stesso. Puoi quindi indirizzare un’azione a un endpoint già esistente. Limiti: 8 s timeout, 128 KB di risposta. Tutto ciò che restituisci raggiunge il modello come dati in un envelope JSON, mai come istruzioni. Le chiavi simili a segreti vengono rimosse in ingresso.
4 · Provarlo prima di un visitatore
curl -X POST https://conecto.chat/api/v1/integrations/acme-store/actions/catalog.search_products/run/ \
-u "ck_...:cs_..." -H "Content-Type: application/json" \
-d '{"arguments": {"query": "trail shoes"}}'This runs the real call path — signature, credential, timeout, parsing — and shows you the envelope the agent will get. Pass conversation_id to run it in the context of a live chat, which is the only way a verified action can succeed.
Livelli di rischio e l’unica regola che non puoi disattivare
public_readcatalogo, disponibilità, documentazione, identità non richiestaverified_readdati di una persona. Rifiutati finché l’e-mail del visitatore non è verificatapublic_writeuna modifica che non richiede identità, come iscrizione alla newsletter o acquisizione di leadwriteuna modifica all’account di una persona. Verificata e mai ritentata senza avvisoPer gli ultimi due, l’indirizzo verificato viene fornito da noi, in visitor.verified_email — never taken from the model's arguments. Verification comes from an emailed code, or from your own site attestazione di un utente connesso. Nessuna impostazione del widget può esentare un’azione da questa regola, perché «di chi è questo ordine?» non è una domanda a cui dovrebbe rispondere un interruttore.
Azioni riservate: il tuo negozio, collegato come un’integrazione nativa
A few action names mean something to the platform itself. Declare catalog.search_products returning {"products": [{title, url, image, price_from, currency, available}]} and its results become product cards under the AI's replies and fill the widget's Home showcase — the same treatment a Shopify connection gets, from whatever your catalog actually runs on. orders.get_status, orders.get_tracking, orders.list_recent e account.lookup are reserved the same way, and are always verified reads. GET /integrations/{slug}/actions/ returns the full catalog with the shape each one expects.
Le integrazioni personalizzate fanno parte dei piani Agente IA e sono limitate a 20 per spazio di lavoro, con 40 azioni ciascuna. Le chiamate in uscita usano solo HTTPS, vengono risolte e vincolate a un IP pubblico prima della connessione e i reindirizzamenti vengono rifiutati. Un URL di integrazione non può quindi puntare a una destinazione privata.
Avvio rapido: un bot personalizzato in tre passaggi
A bot is a webhook and a reply. Subscribe to message.created, answer through the messages endpoint, and the widget renders it live — quick-reply buttons, email capture, the native ticket form, typing indicators. Turn the built-in AI off on the widget and your bot owns the conversation.
1. Iscrivere il server (Impostazioni → Sviluppatori → Webhook, oppure tramite API):
curl -X POST https://conecto.chat/api/v1/webhooks/ \
-u "ck_...:cs_..." -H "Content-Type: application/json" \
-d '{"url": "https://bots.yourapp.com/conecto",
"events": ["message.created", "conversation.created"]}'2. Verificare e leggere l’evento. Every delivery is signed: X-Conecto-Signature: sha256=<HMAC-SHA256(secret, raw body)>.
// Node/Express
app.post('/conecto', express.raw({ type: '*/*' }), (req, res) => {
const sig = 'sha256=' + crypto.createHmac('sha256', WEBHOOK_SECRET)
.update(req.body).digest('hex')
if (sig !== req.get('X-Conecto-Signature')) return res.sendStatus(401)
const { event, data } = JSON.parse(req.body)
if (event === 'message.created' && data.message.sender === 'visitor') {
handle(data) // your bot logic — reply async, respond 200 fast
}
res.sendStatus(200)
})3. Rispondere con funzionalità.
curl -X POST https://conecto.chat/api/v1/conversations/42/messages/ \
-u "ck_...:cs_..." -H "Content-Type: application/json" \
-d '{"body": "I can refund order #1284 right away — confirm?",
"buttons": ["Yes, refund it", "Talk to a human"]}'Button taps come back to your webhook as normal visitor messages containing the button text — your bot's state machine lives entirely on your side. Other reply powers: blocks sends images, GIFs, video and cards (see Contenuti avanzati), {"ask_email": true} renders the email-capture form, {"ticket_form": true} attaches the native open-a-ticket form, products renders tappable product cards, {"internal": true} leaves an agent-only note, /typing/ shows “…is typing”, /handoff/ summons a human, and PATCH closes the conversation when you're done.
Ricettario per bot
Ricette realmente portate in produzione. Ciascuna comprende un gestore di webhook e poche chiamate API.
1 · Il bot di azione, modifica elementi in tuo software
Poiché il webhook raggiunge tuo può fare sul tuo server tutto ciò che può fare il backend: scrivere nel database, chiamare il sistema di fatturazione o aggiornare una voce nel software di contabilità. Insieme a identità attestata sai esattamente chi sta chiedendo. Perciò «archivia questa ricevuta da 250 $ in Marketing» può essere eseguito in sicurezza:
async function handle({ conversation, visitor, message }) {
const convo = conversation.id
// "file 250 under Marketing" — your parsing, your rules
const cmd = parseAccountingCommand(message.body)
if (!cmd) return reply(convo, "Tell me e.g. 'file 250 under Marketing'.")
// Only act for users YOUR backend has vouched for (logged in on your site)
if (!visitor.verified)
return reply(convo, "Please sign in first, then ask me again.", ["Log in"])
await ledger.addEntry({ // <- YOUR accounting system
account: cmd.account,
amount: cmd.amount,
user: visitor.verified_email, // identity Conecto guarantees
})
await reply(convo,
`Done — $${cmd.amount} filed under ${cmd.account}. Anything else?`,
["Show this month's entries", "Undo that"])
}Lo stesso schema vale per «aggiungi una postazione al mio piano», «rinomina il mio progetto» o «prenota un appuntamento giovedì». Il bot è un sottile livello conversazionale sopra la tua API. Preferisci usare l’IA integrata invece di scrivere un bot? Registra gli stessi endpoint come integrazione e l’IA di Conecto le chiama durante la conversazione con la stessa verifica dell’identità, senza una macchina a stati da mantenere. Se usi già MCP, funziona anche un server remoto di strumenti MCP: Dashboard → Agenti IA → Integrazioni.
2 · Bot sullo stato degli ordini
if (/where.*order|track/i.test(message.body)) {
await typing(convo, 'OrderBot', true)
const order = await shop.lastOrder(visitor.email) // your store
await reply(convo, order
? `Order #${order.id} is ${order.state} — arriving ${order.eta}.`
: "I couldn't find an order for this email.",
order ? [`Track #${order.id}`] : undefined)
}3 · Bot per ridurre i ticket
Cerca prima nel centro assistenza; apri un ticket solo se non trovi corrispondenze:
const { articles } = await api('GET', '/articles/?q=' + q + '&published=true')
if (articles.length)
await reply(convo, `This might help: “${articles[0].title}”. Did that solve it?`,
["Solved it", "Open a ticket"])
else
await api('POST', `/conversations/${convo}/messages/`,
{ body: "Let's get this to the team.", ticket_form: true })4 · Bot per la qualificazione dei lead
// no email yet? capture it natively, then enrich + route
if (!visitor.email)
return api('POST', `/conversations/${convo}/messages/`,
{ body: "Happy to help — what's your work email?", ask_email: true })
await api('POST', '/contacts/', { email: visitor.email,
custom_fields: { lead_source: 'chat', intent: classify(message.body) } })
await api('POST', `/conversations/${convo}/messages/`,
{ body: "Routing you to sales…", internal: true })
await api('POST', `/conversations/${convo}/assign/`, { user_id: SALES_USER_ID })
await api('POST', `/conversations/${convo}/handoff/`)5 · Messaggi proattivi sul ciclo di vita
Invia a una sessione del visitatore senza attendere un messaggio, ad esempio aggiornamenti di spedizione, promemoria di prova o recupero del carrello:
curl -X POST https://conecto.chat/api/v1/widgets/7/visitors/$SESSION/message/ \
-u "ck_...:cs_..." -H "Content-Type: application/json" \
-d '{"body": "Your order shipped — want live tracking?",
"buttons": ["Track my order"]}'Cart recovery works the same way, with products putting the item itself back in front of them as a tappable card:
-d '{"body": "Still thinking it over? Your cart is saved:",
"products": [{"title": "Trail Runner 2", "price_from": "89.00",
"currency": "USD", "image": "https://cdn.you.com/tr2.jpg",
"url": "https://shop.you.com/cart"}]}'6 · Un negozio sconosciuto, collegato all’IA
Nessun webhook né macchina a stati: dichiara l’azione di catalogo riservata, indirizzala al tuo endpoint di ricerca e l’IA consiglia articoli del catalogo con vere schede prodotto.
// POST /conecto/search on your server
app.post('/conecto/search', verifyConectoSignature, async (req, res) => {
const { query, limit = 4 } = req.body.arguments
const hits = await catalog.search(query, { limit }) // <- YOUR catalog
res.json({ ok: hits.length > 0, not_found: hits.length === 0,
products: hits.map(p => ({
title: p.name, url: p.permalink, image: p.thumbnail,
price_from: p.price.toFixed(2), currency: p.currency,
available: p.stock > 0,
})) })
})Questa è l’intera integrazione. Le schede, la selezione Home, la regola «richiamare prima di menzionare un prodotto» e il divieto per il modello di incollare URL non elaborate sono inclusi nel nome riservato.
7 · Follow-up CSAT
Subscribe to conversation.rated; thank promoters, rescue detractors:
if (event === 'conversation.rated') {
if (data.rating.score <= 2)
await api('POST', '/tickets/', { email: data.visitor.email,
message: `Low CSAT (${data.rating.score}/5): ${data.rating.comment}`,
priority: 'high' })
else
await push(data.widget.id, data.visitor.session,
"Glad we could help! Here's 10% off your next order: THANKS10")
}Agire in sicurezza sui tuoi sistemi
Tre regole rendono i bot di azione pronti per la produzione:
1. Prima l’identità. Only mutate data for sessions where visitor.verified is true — your backend vouched for them via /identify/. The vouch is time-boxed and session-scoped; revoke it with /unverify/ on logout.
2. Confermare i passaggi distruttivi. Invia l’azione come domanda con pulsanti («Rimborsare l’ordine n. 1284?» · Sì / No), il tocco ritorna come testo del pulsante, il gestore idempotente viene eseguito e la trascrizione documenta il consenso.
3. Passare a una persona in caso di dubbio. POST /conversations/{id}/handoff/ flags the thread human-needed (your routing rules apply), and {"internal": true} notes give the teammate full context your bot gathered.
Guida agli endpoint
Conversazioni e messaggi
/conversations/Newest first. Filters: status (open · pending · closed), widget_id, session; paginate with limit/before_id.
/conversations/{id}/Conversation + visitor-facing transcript (≤500 messages, since_id for increments).
/conversations/{id}/messages/body (≤4000), buttons (≤6 × 60 chars), blocks (≤10 — see Contenuti avanzati), ask_email, ticket_form (native ticket form), internal (agent-only note), products (≤4 cards rendered as a mini carousel under the bubble: {title, url, price_from, currency, image} — title required, everything else optional). Honors Idempotency-Key. 409 when closed.
/conversations/{id}/typing/{"name": "OrderBot", "on": true} , scade automaticamente dopo circa 8 s.
/conversations/{id}/assign/{"user_id": 12} (or null to unassign) — teammates come from /members/.
/conversations/{id}/handoff/Segnala che serve una persona; appare nella posta in arrivo come un passaggio dall’IA, regole di instradamento comprese.
/conversations/{id}/{"status": "closed" | "open"}. Closing fires conversation.closed.
/widgets/{widget_id}/visitors/{session}/message/Proactive push: reuses the session's live conversation or opens one; body + buttons + blocks + products. Honors Idempotency-Key.
Integrazioni, il tuo software come integrazione di prima classe
/integrations/ · POSTList, or register one: slug, name, base_url (https), optional description/icon_url/homepage_url, auth_type (none · bearer · api_key · basic) + credential, and actions inline. Returns the signing_secret you verify our calls with.
/integrations/{slug}/ · PATCH · DELETEPATCH takes the same fields; actions upserts by name and replace_actions: true makes your list authoritative (deploy-from-source). rotate_signing_secret rolls the secret; active: false switches it off everywhere at once.
/integrations/{slug}/actions/ · POST · DELETE /{name}/Each action: name (dotted, e.g. orders.lookup), path, method, risk, description (what the AI reads), parameters ({name, type, required, description, enum}), ai_enabled. GET also returns the reserved-action catalog.
/integrations/{slug}/install/ · /uninstall/widget_ids (all visible widgets when omitted), actions allowlist, enabled.
/integrations/{slug}/actions/{name}/run/Invoke it now through the real call path. arguments, optional conversation_id for visitor context. Returns {status, result, blocks}.
Contenuti multimediali
/media/Multipart file (≤10 MB) → {media: {url, absolute_url, filename, content_type, size, is_image}}. Put url in an image/video/file block.
Ticket
/tickets/Filters: status (open · pending · resolved), email; paginated.
/tickets/email, message, optional name, category_id, priority (low · normal · high · urgent), submission_id (UUID idempotency key). The requester gets the standard acknowledgement email.
/tickets/{id}/ · PATCHDetail with the comment thread; PATCH status, priority, assignee_user_id.
/tickets/{id}/reply/body — emails the requester; internal: true for a private note.
/ticket-categories/Le categorie dello spazio di lavoro per la creazione dei ticket.
Contatti, sincronizza il database degli utenti
/contacts/Upsert by email (201 created / 200 updated): profile fields + custom_fields (≤30 keys, merged).
/contacts/ · GET/PATCH/DELETE /contacts/{id}/Search with email (exact) or q; standard pagination.
Visitatori, identità e personalizzazione
/widgets/{widget_id}/visitors/{session}/identify/email (required), name, data (custom fields), verified + verify_hours (≤72, default 12) — see Identità. Le sessioni possono essere predisposte in anticipo.
/widgets/{widget_id}/visitors/{session}/unverify/Revoca l’attestazione, chiama al logout.
/widgets/{widget_id}/visitors/{session}/Identità corrente: e-mail, nome e stato di verifica.
Base di conoscenza
/articles/q full-text search, published=true filter — perfect for bot answer lookups.
/articles/ · GET/PATCH/DELETE /articles/{id}/Sincronizza la documentazione tramite codice. Il contenuto HTML viene ripulito sul server con una lista consentita.
Configurazione e metadati
/widgets/{widget_id}/ · PATCHLeggi o aggiorna la configurazione del widget, saluto, colori, pulsanti di azione della Home, domande rapide, orari di apertura e gli stessi campi convalidati modificati dallo strumento di personalizzazione della dashboard.
/members/Membri del team (user_id, name, role) per l’assegnazione.
/stats/Conteggi in tempo reale: conversazioni aperte/in attesa, ticket aperti, contatti e articoli pubblicati.
/me/Il tuo spazio di lavoro, l’ambito della credenziale e i widget (ID + chiavi di incorporamento).
/schema/La descrizione leggibile dalle macchine di tutto quanto sopra: endpoint, eventi, tipi di blocchi, azioni riservate, codici di errore e limiti. Genera il client da questa descrizione.
Identità: evitare una nuova verifica per gli utenti connessi
Il widget memorizza il proprio ID di sessione nel browser. Leggilo sul client, invialo al backend con il tuo cookie di sessione, quindi attesta l’identità da server a server:
# your backend, right after your own auth check
curl -X POST https://conecto.chat/api/v1/widgets/7/visitors/$CONECTO_SESSION/identify/ \
-u "ck_...:cs_..." -H "Content-Type: application/json" \
-d '{"email": "maya@acme.io", "name": "Maya",
"verified": true, "verify_hours": 24,
"data": {"plan": "pro", "customer_since": "2024"}}'While the vouch lasts, Conecto treats the email as verified: the widget knows their name, chats attach to the right CRM contact, and flows that normally demand an email OTP — refund lookups, order changes, sensitive account data through the AI's tools — proceed without one. Your attestation is as strong as our code-by-email, because you actually authenticated them. Only vouch sessions your backend has verified, and call /unverify/ on logout.
Webhook
Manage in the dashboard or via GET/POST /webhooks/ e DELETE /webhooks/{id}/ (https only, ≤10 per workspace, optional per-widget scope). Deliveries carry X-Conecto-Event, X-Conecto-Delivery, X-Conecto-Timestamp and the HMAC signature, and time out after 2.5s — respond 200 fast, process async. Events:
conversation.createdprimo messaggio del visitatore in un nuovo thread, inclusi i moduli della scheda Homemessage.createdogni messaggio del visitatore, il trigger del bot personalizzatoconversation.closedchiusa da un operatore o dall’APIconversation.assignedassegnato a un membro del team o non assegnatoconversation.handoffsegnalata come bisognosa di una personaconversation.ratedvalutazione CSAT del visitatore (1–5 + commento)ticket.createdqualsiasi origine: modulo del widget, IA, bot, automazioni, APIticket.updatedstato, priorità o assegnatario modificatocontact.createduna nuova persona è entrata nel CRM, sincronizzala con il tuovisitor.identifieduna sessione è stata identificata tramite l’API, con il relativo stato di attestazioneLa consegna è almeno una volta e senza ordine garantito. Every payload carries an id (also in X-Conecto-Delivery): store it, skip ids you have already handled, and both replays and duplicates stop being your problem. When your bot answers a delivery, pass that same id as the Idempotency-Key and a redelivery can never make it speak twice.
app.post('/conecto', express.raw({ type: '*/*' }), async (req, res) => {
const sig = 'sha256=' + crypto.createHmac('sha256', WEBHOOK_SECRET)
.update(req.body).digest('hex')
if (!crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(req.get('X-Conecto-Signature') || '')))
return res.sendStatus(401)
const { id, event, data } = JSON.parse(req.body)
res.sendStatus(200) // ack first, work after
if (await seen(id)) return // at-least-once: dedupe on the delivery id
if (event === 'message.created' && data.message.sender === 'visitor') {
await fetch(`https://conecto.chat/api/v1/conversations/${data.conversation.id}/messages/`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Idempotency-Key': id, ...auth },
body: JSON.stringify({ body: await answer(data) }),
})
}
})Everything here is designed to sit under an SDK: stable envelopes, slug-addressed integrations, typed errors, cursor pagination, idempotent writes, one signature scheme in both directions, and GET /schema/ to generate from. Building something? Parla con noi , vogliamo che gli sviluppatori creino su Conecto e diamo priorità alle loro richieste.