Endpoints
Webhooks
Subscribe a URL to workspace events. This is what the Make instant triggers call when a scenario is turned on and off.
Cómo funcionan los webhooks
Los webhooks envían un evento a tu URL cuando ocurre algo en el workspace: un contacto nuevo, una reserva, un pedido pagado, una llamada terminada. Cada entrega va firmada para que puedas comprobar que viene de Entagl.
Configura un endpoint
Crea un endpoint con POST /webhooks. Elige tipos de evento de la lista de abajo (o de GET /events), o usa un comodín como contact.* o *. La respuesta incluye el secret de firma una sola vez: guárdalo. La app de Entagl para Make lo hace por ti cuando se activa un scenario con un trigger de Entagl, y elimina el endpoint cuando se desactiva.
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"
}'El webhook que puedes configurar en la página Canales → Webhooks de la app usa el formato original: el cuerpo es { event, workspace_id, occurred_at, data } y X-Entagl-Signature es sha256= seguido de un HMAC-SHA256 del cuerpo sin procesar. Todo lo que sigue describe los endpoints creados mediante la API.
Formato del evento
Cada entrega es un POST con un evento JSON. type indica qué ocurrió y data contiene los detalles de ese tipo (consulta la lista de eventos). actor indica quién lo provocó (ai, human, customer, system, api, import o provider) y source.connection_id qué credencial, para que una integración pueda ignorar sus propios cambios. Los eventos de actualización también llevan changes con los nombres de los campos modificados.
{
"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-SignatureFirma, ver más abajo.
- X-Entagl-Event-IdEl ID del evento. Úsalo para omitir duplicados.
- X-Entagl-Event-TypeEl tipo de evento, p. ej. contact.created.
- X-Entagl-Delivery-Attempt1 en el primer intento, luego 2, 3 …
Verifica la firma
El encabezado X-Entagl-Signature tiene este aspecto: t=1767225600,v1=5257a869…. t es la hora de envío en segundos Unix. Cada v1 es un HMAC-SHA256, en hexadecimal, del texto {t}.{raw body} generado con el secreto de tu endpoint. Calcúlalo tú y compara en tiempo constante. Rechaza la solicitud cuando t esté a más de 5 minutos de tu reloj. Tras rotar el secreto hay dos valores v1 durante 24 horas: acepta la solicitud si coincide cualquiera de los dos.
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);Entrega y reintentos
- Responde con cualquier estado
2xxen menos de 10 segundos. No se siguen las redirecciones. - Cualquier otra cosa es un intento fallido. Entagl reintenta tras 30 segundos, 2 minutos, 10 minutos, 1 hora, 6 horas y 24 horas, y luego marca la entrega como fallida (7 intentos en total).
410 Gonedetiene los reintentos de inmediato y desactiva el endpoint.- Un endpoint que sigue fallando durante 72 horas se desactiva y se avisa al propietario del workspace. Vuelve a activarlo con
PATCH /webhooks/:idy"status": "active". - Una entrega puede llegar más de una vez, y los eventos pueden llegar desordenados. Usa el ID del evento para omitir duplicados.
GET /webhooks/:id/deliveriesmuestra los intentos de los últimos 30 días;POST …/redeliverenvía uno de nuevo.
Privacidad
De forma predeterminada, los eventos omiten lo que escribieron los clientes (texto de mensajes, transcripciones, resúmenes, notas) y sus datos de contacto (nombres, teléfonos, emails, nombres de usuario). La lista de eventos muestra estos campos en cada evento. Para recibirlos, activa include_content y/o include_contact_details en el endpoint. Solo el propietario o un admin del workspace puede activarlos. En los workspaces HIPAA nunca se envían, sea cual sea la configuración. Cuando se omite un campo privado, changes sigue indicando su nombre, pero nunca su valor anterior.
Lista de eventos
Todos los tipos de evento que Entagl envía hoy. message.received y message.sent tienen mucho volumen: un comodín nunca los incluye, así que indícalos por nombre. GET /events devuelve el mismo catálogo 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.
Campos de datos de contacto: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.
Campos de datos de contacto: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.
Campos de contenido:summary
Campos de datos de contacto: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.
Campos de contenido: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.
Campos de contenido: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.
Campos de contenido:body
Messages (3)
message.receivedIncoming messageFires for every message a customer sends, on any channel. High volume — subscribe only if you need every message.
Campos de contenido:text
message.sentOutgoing messageFires for every message sent to a customer: AI replies, team replies, campaigns and automations.
Campos de contenido: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).
Campos de contenido:summary
Campos de datos de contacto: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.
Campos de contenido:comment
Bookings (6)
booking.createdBooking createdFires when an appointment is created for a customer.
Campos de contenido:conversation_summarydocuments
Campos de datos de contacto: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).
Campos de contenido:documents
Campos de datos de contacto:customer_namecustomer_phone
booking.updatedBooking updatedFires when any booking detail changes (time, service, duration, location, notes). The envelope "changes" lists what changed.
Campos de datos de contacto:customer_namecustomer_phone
booking.rescheduledBooking rescheduledFires when a booking moves to a new date or time.
Campos de datos de contacto:customer_namecustomer_phone
booking.cancelledBooking cancelledFires when a booking is cancelled by the customer (through the AI), a team member, or the API.
Campos de datos de contacto:customer_namecustomer_phone
booking.status_changedBooking status changedFires when a booking status changes, e.g. confirmed, completed or no-show.
Campos de datos de contacto:customer_namecustomer_phone
Reminders (4)
reminder.createdReminder createdFires when a reminder is created.
Campos de contenido:titlenote
reminder.firedReminder dueFires when a reminder becomes due and the assignees are notified.
Campos de contenido:titlenote
reminder.closedReminder closedFires when a reminder is marked done.
Campos de contenido: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.
Campos de datos de contacto:customer_namecustomer_phone
order.updatedOrder updatedFires when any order detail or item changes. The envelope "changes" lists what changed.
Campos de datos de contacto:customer_namecustomer_phone
order.status_changedOrder status changedFires when an order status changes (for example confirmed, shipped, delivered).
Campos de datos de contacto:customer_namecustomer_phone
order.paidOrder paidFires when an order is marked as paid.
Campos de datos de contacto:customer_namecustomer_phone
order.cancelledOrder cancelledFires when an order is cancelled.
Campos de datos de contacto: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.
Campos de contenido:summarytranscript
Campos de datos de contacto:from_numberto_number
call.missedMissed callFires when an incoming call was not answered or an outgoing call got no answer.
Campos de contenido:summarytranscript
Campos de datos de contacto:from_numberto_number
call.voicemail_receivedVoicemail receivedFires when a caller leaves a voicemail.
Campos de contenido:transcript_text
Campos de datos de contacto: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).
Campos de contenido: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).
Las rutas son relativas a la URL base. Cada tarjeta indica el permiso que necesita la llamada y cada parámetro, con su tipo y sus límites.
/webhooksList webhook endpoints.
/webhooksCreate a webhook endpoint. The signing secret is returned once, in this response.
Cuerpo JSON
- urlobligatoriostring (URL)
https:// URL that receives events, up to 2,048 characters. Private and internal addresses are refused (422 unsafe_url).
- eventsobligatorioarray 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.
- descriptionopcionalstring
Up to 200 characters.
- filtersopcionalobject
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_contentopcionalboolean
Include message text, transcripts and notes. Owner or admin only; refused on HIPAA workspaces.
- include_contact_detailsopcionalboolean
Include names, phones, emails and handles. Owner or admin only; refused on HIPAA workspaces.
- created_viaopcionalenum
Which tool created the endpoint.
Valores permitidos:
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.
Parámetros de ruta
- idobligatoriostring
Endpoint ID (starts with "we_").
/webhooks/:idUpdate an endpoint, or pause and re-activate it.
Parámetros de ruta
- idobligatoriostring
Endpoint ID.
Cuerpo JSON
- urlopcionalstring (URL)
New receiving URL.
- eventsopcionalarray of strings
New event list.
- descriptionopcionalstring | null
Up to 200 characters.
- filtersopcionalobject
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_contentopcionalboolean
Owner or admin only to turn on.
- include_contact_detailsopcionalboolean
Owner or admin only to turn on.
- statusopcionalenum
Re-activating resets the failure count.
Valores permitidos:
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).
Parámetros de ruta
- idobligatoriostring
Endpoint ID.
/webhooks/:id/rotate-secretIssue a new signing secret. The previous secret keeps signing deliveries for 24 hours.
Parámetros de ruta
- idobligatoriostring
Endpoint ID.
/webhooks/:id/testSend a signed sample event to the endpoint now and return the result (delivered, status, error, duration_ms).
Parámetros de ruta
- idobligatoriostring
Endpoint ID.
Cuerpo JSON
- eventopcionalstring
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.
Parámetros de ruta
- idobligatoriostring
Endpoint ID.
/webhooks/:id/deliveries/:deliveryId/redeliverQueue a delivery to be sent again. Answers 202.
Parámetros de ruta
- idobligatoriostring
Endpoint ID.
- deliveryIdobligatoriointeger
Delivery ID from the deliveries list.