Endpoints
Webhooks
Subscribe a URL to workspace events. This is what the Make instant triggers call when a scenario is turned on and off.
Fonctionnement des webhooks
Les webhooks envoient un événement à votre URL quand il se passe quelque chose dans le workspace : un nouveau contact, une réservation, une commande payée, un appel terminé. Chaque envoi est signé pour que vous puissiez vérifier qu’il vient d’Entagl.
Configurer un endpoint
Créez un endpoint avec POST /webhooks. Choisissez des types d’événement dans la liste ci-dessous (ou GET /events), ou utilisez un joker comme contact.* ou *. La réponse contient le secret de signature une seule fois : conservez-le. L’application Entagl pour Make le fait pour vous quand un scenario avec un trigger Entagl est activé, et supprime l’endpoint quand il est désactivé.
curl -X POST "https://api.entagl.com/api/v1/webhooks" \
-H "Authorization: Bearer ai_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/entagl/webhooks",
"events": ["contact.created", "booking.*"],
"description": "CRM sync"
}'Le webhook que vous pouvez configurer sur la page Canaux → Webhooks de l’application utilise le format d’origine : le corps est { event, workspace_id, occurred_at, data } et X-Entagl-Signature vaut sha256= suivi d’un HMAC-SHA256 du corps brut. Tout ce qui suit décrit les endpoints créés via l’API.
Format de l’événement
Chaque envoi est un POST avec un événement JSON. type dit ce qui s’est passé et data contient les détails pour ce type (voir la liste des événements). actor indique qui en est à l’origine (ai, human, customer, system, api, import ou provider) et source.connection_id quel identifiant d’accès, pour qu’une intégration puisse ignorer ses propres changements. Les événements de mise à jour portent aussi changes avec les noms des champs modifiés.
{
"id": "evt_5c1f0a9e7b2d4e3f8a6c0b1d2e3f4a5b",
"object": "event",
"type": "contact.created",
"api_version": "2026-10-01",
"occurred_at": "2026-10-01T14:32:11.482Z",
"created_at": "2026-10-01T14:32:11.482Z",
"workspace_id": "00000000-0000-0000-0000-000000000000",
"actor": {
"type": "api",
"id": "user_9f2c1a"
},
"resource": {
"type": "contact",
"id": "6789"
},
"source": {
"connection_id": "apikey:42",
"via": "api"
},
"data": {
"contact_id": 6789,
"name": "Jane Doe",
"first_name": "Jane",
"last_name": "Doe",
"phone": "+15551234567",
"email": "jane@example.com",
"channel": "instagram",
"tags": [
"vip",
"botox"
],
"stage": {
"funnel_id": 3,
"stage_id": 14,
"stage_name": "Qualified"
},
"assigned_to_user_id": "user_9f2c1a",
"language": "en",
"custom_fields": {
"budget": "500-1000",
"preferred_branch": "Downtown"
},
"created_at": "2026-09-28T10:02:44.000Z",
"updated_at": "2026-10-01T14:32:11.482Z"
},
"changes": null
}- Content-Typeapplication/json
- User-AgentEntagl-Webhooks/2.0
- X-Entagl-SignatureSignature, voir plus bas.
- X-Entagl-Event-IdL’ID de l’événement. Utilisez-le pour ignorer les doublons.
- X-Entagl-Event-TypeLe type d’événement, par ex. contact.created.
- X-Entagl-Delivery-Attempt1 au premier essai, puis 2, 3 …
Vérifier la signature
L’en-tête X-Entagl-Signature ressemble à t=1767225600,v1=5257a869…. t est l’heure d’envoi en secondes Unix. Chaque v1 est un HMAC-SHA256, en hexadécimal, du texte {t}.{raw body} calculé avec le secret de votre endpoint. Calculez-le vous-même et comparez en temps constant. Rejetez la requête quand t est à plus de 5 minutes de votre horloge. Après une rotation du secret, il y a deux valeurs v1 pendant 24 heures : acceptez la requête si l’une des deux correspond.
import crypto from 'node:crypto';
import express from 'express';
const app = express();
// The secret returned once by POST /api/v1/webhooks (or by rotate-secret).
const SECRET = process.env.ENTAGL_WEBHOOK_SECRET;
// Read the RAW body: the signature covers the exact bytes Entagl sent.
app.post('/entagl/webhooks', express.raw({ type: 'application/json' }), (req, res) => {
const rawBody = req.body.toString('utf8');
if (!verifyEntaglSignature(req.get('X-Entagl-Signature') || '', rawBody, SECRET)) {
return res.status(400).send('invalid signature');
}
const event = JSON.parse(rawBody);
// Deliveries can repeat: skip an event.id you have already handled.
console.log(event.type, event.id);
res.sendStatus(200); // any 2xx within 10 seconds counts as delivered
});
function verifyEntaglSignature(header, rawBody, secret, toleranceSeconds = 300) {
const parts = header.split(',').map((p) => p.trim());
const t = Number(parts.find((p) => p.startsWith('t='))?.slice(2));
if (!Number.isFinite(t) || Math.abs(Date.now() / 1000 - t) > toleranceSeconds) return false;
const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
return parts
.filter((p) => p.startsWith('v1='))
.some((p) => {
const got = p.slice(3);
return got.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(got), Buffer.from(expected));
});
}
app.listen(3000);Envoi et nouvelles tentatives
- Répondez avec n’importe quel statut
2xxen moins de 10 secondes. Les redirections ne sont pas suivies. - Tout le reste est un échec. Entagl réessaie après 30 secondes, 2 minutes, 10 minutes, 1 heure, 6 heures et 24 heures, puis marque l’envoi comme échoué (7 tentatives au total).
410 Gonearrête immédiatement les nouvelles tentatives et désactive l’endpoint.- Un endpoint qui échoue sans arrêt pendant 72 heures est désactivé et le propriétaire du workspace est prévenu. Réactivez-le avec
PATCH /webhooks/:idet"status": "active". - Un envoi peut arriver plusieurs fois, et les événements peuvent arriver dans le désordre. Utilisez l’ID de l’événement pour ignorer les doublons.
GET /webhooks/:id/deliveriesmontre les tentatives des 30 derniers jours ;POST …/redeliveren renvoie une.
Confidentialité
Par défaut, les événements omettent ce que les clients ont écrit (texte des messages, transcriptions, résumés, notes) et leurs coordonnées (noms, téléphones, emails, noms d’utilisateur). La liste des événements indique ces champs pour chaque événement. Pour les recevoir, définissez include_content et/ou include_contact_details sur l’endpoint. Seul le propriétaire ou un admin du workspace peut les activer. Sur les workspaces HIPAA, ils ne sont jamais envoyés, quels que soient les réglages. Quand un champ privé est omis, changes en liste quand même le nom, mais jamais l’ancienne valeur.
Liste des événements
Tous les types d’événement qu’Entagl envoie aujourd’hui. message.received et message.sent sont à fort volume : un joker ne les inclut jamais, listez-les donc par nom. GET /events renvoie le même catalogue en JSON.
Contacts (11)
contact.createdContact createdFires when a new contact is added: a first message from someone new, a manual add, an import or the API.
Champs des coordonnées du contact :namefirst_namelast_namephoneemailcustom_fields
contact.updatedContact updatedFires when any contact field or custom field changes. The envelope "changes" lists the changed fields and their previous values.
Champs des coordonnées du contact :namefirst_namelast_namephoneemailcustom_fields
contact.deletedContact deletedFires when a contact is deleted. Carries ids only.
contact.tag_addedContact tag addedFires when one or more tags are added to a contact.
contact.tag_removedContact tag removedFires when one or more tags are removed from a contact.
contact.stage_changedLifecycle stage changedFires when a contact moves to another funnel stage, or the stage is removed.
contact.assignedContact owner changedFires when a contact is assigned to a team member or unassigned.
contact.lead_capturedLead capturedFires when the AI (or a form) marks a contact as a lead, with a short summary of what they want.
Champs de contenu :summary
Champs des coordonnées du contact :namephoneemail
contact.mergedContacts mergedFires when two contacts are merged into one. The secondary contact id now redirects to the primary.
contact.opted_outContact opted outFires when a contact is marked do-not-contact or opts out of marketing messages.
note.createdContact note addedFires when a team member adds a note to a contact.
Champs de contenu :body
Conversations (7)
conversation.createdConversation openedFires when a new conversation starts on any channel.
conversation.reopenedConversation reopenedFires when a closed conversation is opened again, by a new customer message or a team member.
conversation.closedConversation closedFires when a conversation is closed by a team member, the API or auto-close, with the closing note if any.
Champs de contenu :closing_note_text
conversation.assignedConversation assignee changedFires when a conversation is assigned to a team member or unassigned.
conversation.ai_pausedAI paused on a conversationFires when AI replies are paused on a conversation (team member takeover, handover, spam, schedule or API).
conversation.ai_resumedAI resumed on a conversationFires when AI replies are switched back on for a conversation.
conversation.comment_createdConversation comment addedFires when a team member (or the API) adds an internal comment to a conversation. Customers never see comments.
Champs de contenu :body
Messages (3)
message.receivedIncoming messageFires for every message a customer sends, on any channel. High volume — subscribe only if you need every message.
Champs de contenu :text
message.sentOutgoing messageFires for every message sent to a customer: AI replies, team replies, campaigns and automations.
Champs de contenu :text
message.send_failedMessage failed to sendFires when a message to a customer could not be delivered by the channel.
Handovers (2)
handover.requestedHandover requestedFires when the AI hands a conversation to a human (customer asked for a person, complaint, or a question it could not answer).
Champs de contenu :summary
Champs des coordonnées du contact :customer_namecustomer_phone
handover.resolvedHandover resolvedFires when a team member (or auto-resolve) marks a handover as handled.
AI (3)
conversation.followup_sentFollow-up sentFires when the AI sends an automatic follow-up to a customer who went quiet.
ai_turn.failedAI reply failedFires when the AI could not produce a reply for a customer message (a fallback message may have been sent instead).
message_feedback.submittedAI reply ratedFires when a team member gives thumbs up or down to an AI reply.
Champs de contenu :comment
Bookings (6)
booking.createdBooking createdFires when an appointment is created for a customer.
Champs de contenu :conversation_summarydocuments
Champs des coordonnées du contact :customer_namecustomer_phone
booking.documents.addedBooking documents addedFires when a customer shares a document or photo in a conversation after their booking was created. Delivers only the new file(s).
Champs de contenu :documents
Champs des coordonnées du contact :customer_namecustomer_phone
booking.updatedBooking updatedFires when any booking detail changes (time, service, duration, location, notes). The envelope "changes" lists what changed.
Champs des coordonnées du contact :customer_namecustomer_phone
booking.rescheduledBooking rescheduledFires when a booking moves to a new date or time.
Champs des coordonnées du contact :customer_namecustomer_phone
booking.cancelledBooking cancelledFires when a booking is cancelled by the customer (through the AI), a team member, or the API.
Champs des coordonnées du contact :customer_namecustomer_phone
booking.status_changedBooking status changedFires when a booking status changes, e.g. confirmed, completed or no-show.
Champs des coordonnées du contact :customer_namecustomer_phone
Reminders (4)
reminder.createdReminder createdFires when a reminder is created.
Champs de contenu :titlenote
reminder.firedReminder dueFires when a reminder becomes due and the assignees are notified.
Champs de contenu :titlenote
reminder.closedReminder closedFires when a reminder is marked done.
Champs de contenu :titlenote
appointment_reminder.sentAppointment reminder sentFires when an automatic appointment reminder (WhatsApp template or SMS) is sent to a customer.
Orders (6)
order.createdOrder createdFires when an order is created by the AI, a team member, a store sync or the API.
Champs des coordonnées du contact :customer_namecustomer_phone
order.updatedOrder updatedFires when any order detail or item changes. The envelope "changes" lists what changed.
Champs des coordonnées du contact :customer_namecustomer_phone
order.status_changedOrder status changedFires when an order status changes (for example confirmed, shipped, delivered).
Champs des coordonnées du contact :customer_namecustomer_phone
order.paidOrder paidFires when an order is marked as paid.
Champs des coordonnées du contact :customer_namecustomer_phone
order.cancelledOrder cancelledFires when an order is cancelled.
Champs des coordonnées du contact :customer_namecustomer_phone
order.payment_proof_uploadedPayment proof uploadedFires when a customer (through the AI) or a team member attaches a payment receipt to an order.
Products (3)
product.createdProduct createdFires when a product is added to the catalog.
product.updatedProduct updatedFires when a product changes (price, stock, details).
product.deletedProduct deletedFires when a product is removed from the catalog.
Calls (3)
call.endedCall endedFires after every AI phone or WhatsApp call, with outcome and (opt-in) summary and transcript.
Champs de contenu :summarytranscript
Champs des coordonnées du contact :from_numberto_number
call.missedMissed callFires when an incoming call was not answered or an outgoing call got no answer.
Champs de contenu :summarytranscript
Champs des coordonnées du contact :from_numberto_number
call.voicemail_receivedVoicemail receivedFires when a caller leaves a voicemail.
Champs de contenu :transcript_text
Champs des coordonnées du contact :from_number
Campaigns (3)
campaign.completedCampaign finished sendingFires when a broadcast campaign finished sending to all recipients.
campaign_recipient.repliedCampaign replyFires when a recipient replies to a campaign message.
campaign_recipient.failedCampaign message failedFires when a campaign message could not be delivered to a recipient.
Forms (1)
form.submittedForm submittedFires when a customer submits a form (website widget, hosted link or in-chat intake).
Champs de contenu :fields
Account (3)
channel.disconnectedChannel disconnectedFires when a channel stops working (token expired, access removed) or is disconnected.
channel.reconnectedChannel healthy againFires when a previously failing channel works again.
credits.threshold_reachedCredits running lowFires when credit usage crosses 80%, 100% or 120% of the plan allowance.
Workspace (3)
tag.createdTag createdFires when a tag is added to the workspace tag list.
tag.updatedTag updatedFires when a workspace tag is renamed or recolored.
tag.deletedTag deletedFires when a workspace tag is deleted (it is removed from every contact).
Les chemins sont relatifs à l’URL de base. Chaque carte indique la permission requise par l’appel et chaque paramètre, avec son type et ses limites.
/webhooksList webhook endpoints.
/webhooksCreate a webhook endpoint. The signing secret is returned once, in this response.
Corps JSON
- urlobligatoirestring (URL)
https:// URL that receives events, up to 2,048 characters. Private and internal addresses are refused (422 unsafe_url).
- eventsobligatoirearray of strings
1–100 event types from GET /events, "<prefix>.*" (e.g. "contact.*") or "*". Wildcards never include message.received and message.sent; list those explicitly.
- descriptionfacultatifstring
Up to 200 characters.
- filtersfacultatifobject
Optional narrowing: channels (array of channel names, up to 20), actor_types (array, up to 7), sources (object of event type → source keys), ignore_own_changes (boolean — skip events caused by this same connection; on by default for connector-created endpoints).
- include_contentfacultatifboolean
Include message text, transcripts and notes. Owner or admin only; refused on HIPAA workspaces.
- include_contact_detailsfacultatifboolean
Include names, phones, emails and handles. Owner or admin only; refused on HIPAA workspaces.
- created_viafacultatifenum
Which tool created the endpoint.
Valeurs autorisées :
apimakezapiern8n
- A workspace can have up to 100 endpoints (409 endpoint_limit_reached).
/webhooks/:idRetrieve a webhook endpoint with its status and last delivery result.
Paramètres de chemin
- idobligatoirestring
Endpoint ID (starts with "we_").
/webhooks/:idUpdate an endpoint, or pause and re-activate it.
Paramètres de chemin
- idobligatoirestring
Endpoint ID.
Corps JSON
- urlfacultatifstring (URL)
New receiving URL.
- eventsfacultatifarray of strings
New event list.
- descriptionfacultatifstring | null
Up to 200 characters.
- filtersfacultatifobject
Optional narrowing: channels (array of channel names, up to 20), actor_types (array, up to 7), sources (object of event type → source keys), ignore_own_changes (boolean — skip events caused by this same connection; on by default for connector-created endpoints).
- include_contentfacultatifboolean
Owner or admin only to turn on.
- include_contact_detailsfacultatifboolean
Owner or admin only to turn on.
- statusfacultatifenum
Re-activating resets the failure count.
Valeurs autorisées :
activepaused
- On an endpoint that includes content or contact details, only an owner or admin can change url, events or filters.
/webhooks/:idDelete an endpoint (a Make trigger calls this when its scenario is turned off).
Paramètres de chemin
- idobligatoirestring
Endpoint ID.
/webhooks/:id/rotate-secretIssue a new signing secret. The previous secret keeps signing deliveries for 24 hours.
Paramètres de chemin
- idobligatoirestring
Endpoint ID.
/webhooks/:id/testSend a signed sample event to the endpoint now and return the result (delivered, status, error, duration_ms).
Paramètres de chemin
- idobligatoirestring
Endpoint ID.
Corps JSON
- eventfacultatifstring
Event type to sample. Defaults to the endpoint's first non-wildcard event, else a generic "webhook.test" event.
/webhooks/:id/deliveriesRecent delivery attempts, newest first. Deliveries are kept for 30 days.
Paramètres de chemin
- idobligatoirestring
Endpoint ID.
/webhooks/:id/deliveries/:deliveryId/redeliverQueue a delivery to be sent again. Answers 202.
Paramètres de chemin
- idobligatoirestring
Endpoint ID.
- deliveryIdobligatoireinteger
Delivery ID from the deliveries list.