Download OpenAPI specification:Download
Integration of the Live Chat with your own server: every time a message occurs in a conversation we send a POST request (JSON) to the URL you configure, and whatever your server replies is applied to the conversation: answering the visitor, offering buttons or closing the chat.
This way your own business logic can respond in real time: look up an order status in your ERP and answer it, identify the visitor against your CRM, launch a survey with buttons and store the answer, or log the whole conversation in your ticketing system.
You do not have to reply at all. If your server does not answer, answers too late or returns something that is not JSON, the conversation carries on as usual and your agent handles it as always. The integration never blocks the chat.
Please note: event names and JSON field names are in Spanish because they are the literal wire format. Use them exactly as shown (mensaje-recibido, not message-received).
In your panel: Chat → Websites, edit the site and fill in the «Reports to your server» section:
- URL (https): must be https and a public host. For security, private or reserved IPs are rejected (127.0.0.1, 10.x, 192.168.x, ::1…) and redirects are not followed.
- Authentication: the value you type here is sent verbatim in the Authorization header of every request (for example Bearer my_secret_token). Check it on your server and reject anything that does not match.
The setting belongs to the account, not to the site: the first active site that has a URL is used, and it applies to every conversation of the account, whether it comes from the web widget or from WhatsApp, SMS or email.
Always a POST with Content-Type: application/json, a JSON body and a 5 second timeout. The Evento field tells you what happened; ignore any event you do not recognise (new ones will be added keeping this format).
| Event | When it is sent | Is your reply applied? |
mensaje-recibido | INBOUND message: written by the visitor (Emisor = 0). | Yes. |
mensaje-enviado | OUTBOUND message: written by your agent, the AI assistant or the system. Check Emisor to tell which. | No. You are notified so you can log it, but nothing is applied: otherwise your own replies would trigger another reply and it would loop. |
pulsacion-chip | The visitor presses one of the message chips, the [label|value] ones. They do not navigate anywhere: they send a value back into the conversation. | Yes, always. |
pulsacion-boton | The visitor opens a link button (an <a class="boton-chat">) among those marked for tracking. | Yes. |
enlace-pulsado | The visitor opens a link inside the text among those marked for tracking. | Yes. |
encuesta-cierre | The visitor rates the service with a smiley of the closing survey. | Yes. |
conversacion-cerrada | The conversation has been closed, by an agent or by your own reply. | No. Replying to a closure would reopen the conversation in practice, and if your script always answers the same, it would loop. |
A note on the three click events, which is the part that usually needs explaining: a chip carries no URL and its value comes back into the conversation as a visitor reply; a button and a link do carry a URL and you are told when somebody opens them. Keeping them apart lets you measure what works without mixing two different things in one counter.
Reply 2xx with a JSON object. Every field is optional; if you return an empty body nothing happens:
| Field | What it does |
responder | Text posted in the conversation as a reply to the visitor. Supports basic HTML and buttons (see below). Maximum 4,000 characters. |
ref | Your own reference (max. 64 characters). It is stored with the message and comes back in every click event when the visitor presses a chip, a button or a link of that message. This is what lets you match the click with the request that produced it. |
html | Forces how the text is handled: true treats it as HTML even if no tag is recognised, false leaves it as plain text even if it has tags. If you omit it, it is decided automatically (see HTML). |
cerrar | true closes the conversation. |
seguir | true to be told when the visitor opens the links of this message. See Link tracking. |
estadistica | Name under which the clicks of this message are grouped so they can be counted (max. 40 characters). Implies seguir. |
If you want to know when somebody opens one of your links, add "seguir": true. The links in that message start pointing at a redirector of ours that records the click and forwards to the destination, and you get a pulsacion-boton or enlace-pulsado event.
It is never switched on by itself, and there is a reason for you to weigh: with tracking on, the visitor sees our domain when hovering over the link, not the destination. For a campaign link that rarely matters; for a link to your own site it can cost you some trust.
Link by link. What you put on the <a> itself overrides the message setting, so you can turn tracking on for everything and leave one link alone:
<a href="https://yoursite.com/offer" data-seguir="1" data-estadistica="summer-offer">See the offer</a>
<a href="https://yoursite.com/legal" data-seguir="0">Legal notice</a>
It works the same in the quick replies your agents have saved in the panel: just write the link with those attributes.
Clicks are not stored unless you name them, with "estadistica": "name" or with data-estadistica on the link. Without a name you are told about the click and nothing is kept: piling up your visitors' browsing when nobody asked for it adds nothing and involves personal data.
Use the same name across several messages to group a whole campaign.
Buttons are written inside the text of responder, using the format [label|value]:
- The label is what the visitor sees (usually an emoji, text works too).
- The value is what we send back to you when it is pressed.
- Up to 10 buttons per message; the markers are removed from the text that is displayed.
- The visitor can only press once per message; afterwards the buttons are disabled.
Appearance: if no label contains letters or digits (that is, they are emojis or symbols), the buttons are drawn round and large, like the survey smileys. As soon as one label contains text they are all drawn as normal buttons. It is automatic and does not change the value that is returned.
Emojis -> round: "[😍|5][🙂|4][😐|3][🙁|2][😞|1]"
Text -> normal: "[Yes|yes][No|no]"
Mixed -> normal: "[😍|5][I would rather not answer|x]"
There are no buttons outside the web chat. On WhatsApp, SMS or email only the text is sent, already without the markers.
The text of responder supports basic HTML and is always sanitised against a whitelist before being published:
- Allowed tags: b strong i em u s br p ul ol li a code small blockquote. Any other tag is dropped but its text is kept.
- Removed entirely, contents included: script, style, iframe, img, form and the like, plus every attribute (style=, onclick=…).
- Links only accept http, https, mailto and tel, and come out with target="_blank" and rel="noopener noreferrer nofollow".
- If you leave a tag unclosed, it is closed for you: malformed HTML does not break the message.
You do not need to ask for HTML: if the text contains one of the allowed tags it is sanitised; if not, it is treated as plain text as always. It works this way so that an ordinary message such as "5 < 10" is not parsed as a tag and does not lose that fragment. With html you can force either behaviour.
On WhatsApp, SMS or email the plain text version is sent (<br> becomes line breaks and links become text: url).
The [label|value] buttons send the value back to your server, but sometimes all you need is to take the visitor to a page. For that, add the boton-chat class to an ordinary link:
{
"responder": "<b>Choose your subscription</b><br>Make a choice."
+ "<a href='https://yoursite.com/basic' class='boton-chat'>Basic plan</a>"
+ "<a href='https://yoursite.com/premium' class='boton-chat'>Premium plan</a>"
}
Each link is drawn as a full-width button, on its own line and centred, using the colour the site has configured for the chat (and its contrast, so the text stays readable with a light brand colour). You can mix them with the rest of the HTML: bold text, lists or plain paragraphs.
Differences from the [label|value] buttons:
- They open a URL in a new tab; they do not send anything to your server and do not raise the pulsacion-chip event.
- There is no limit of 10 and they are not disabled after being pressed: the visitor can use them again.
- boton-chat is the only class that survives sanitisation, and only on <a>. On any other tag it is dropped, and a link whose target is not http, https, mailto or tel loses both the target and the button appearance.
- Outside the web chat they are sent like any other link: text: url.
1) The visitor writes their last message («thanks, it is sorted now») and your server receives mensaje-recibido. It replies with the survey and its reference:
Careful: the survey has to be launched from a mensaje-recibido. On mensaje-enviado your reply is not applied (see the events table), so it cannot be used to react to the agent closing the chat; for that there is the close with survey button in the panel, which sends it on its own.
{
"responder": "Please rate how we did<br/>[😍|5][🙂|4][😐|3][🙁|2][😞|1]",
"ref": "12345AB"
}
2) The visitor presses the first smiley and your server receives the encuesta-cierre event with your reference, the value pressed and the visitor's details:
{
"Evento": "encuesta", "idConversacion": 88, "idMensaje": 1420,
"Canal": "web", "Valor": "5", "Nota": 5,
"Ref": "12345AB", "idAgente": 7, "Fecha": "2026-08-01 12:40:11",
"Visitante": {
"idVisitante": 12, "Nombre": "Ana", "Email": "ana@customer.com", "Telefono": "",
"IP": "203.0.113.10", "UserAgent": "Mozilla/5.0...",
"Url": "https://yourshop.com/cart", "Idioma": "es", "Zona": "Europe/Madrid",
"Pais": "Spain", "Ciudad": "Madrid", "Operadora": "Movistar",
"Vars": { "order": "8812" },
"idSitio": 3, "Canal": "web", "RefCanal": ""
}
}
3) Your server can still answer something to that click and close:
{ "responder": "Thanks for your feedback!", "cerrar": true }
$expected = 'Bearer my_secret_token';
if (($_SERVER['HTTP_AUTHORIZATION'] ?? '') !== $expected) { http_response_code(401); exit; }
$d = json_decode(file_get_contents('php://input'), true);
header('Content-Type: application/json');
// Button press: matched against the reference we sent earlier
if (($d['Evento'] ?? '') === 'encuesta') {
storeRating($d['Ref'], $d['Valor'], $d['Visitante']['Email'] ?? '');
echo json_encode(['responder' => 'Thanks, we will take it into account.', 'cerrar' => true]);
exit;
}
// Message from the VISITOR. Outbound ones are not answered.
if (($d['Evento'] ?? '') === 'mensaje-recibido') {
if (stripos($d['Cuerpo'], 'order') !== false) {
echo json_encode([
'responder' => '<b>Order 881</b> has shipped. Anything else? [Yes|yes][No|no]',
'ref' => 'ORD-881'
]);
exit;
}
}
echo '{}'; // do nothing: the conversation carries on with the agent
HTTP Basic authentication. Use your API User as the username and your API Token as the password (you will find them in your panel, under Your Data → Configure Account). The resulting header is Authorization: Basic base64(APIUser:APIToken). Most HTTP libraries build it automatically (curl -u, requests auth=, Ruby's basic_auth, etc.) without needing to encode the base64 by hand.
basicPOST REQUEST (JSON) THAT WE SEND TO YOUR URL ON EVERY CHAT MESSAGE.
It carries the Authorization header with the value you configured in the panel: check it and return 401 if it does not match. Reply 2xx with JSON (see «Your server's reply» in the Introduction) within 5 seconds. If you do not reply, the conversation carries on as usual and your agent handles it.
Fields marked as belonging to the click events (Valor, Nota, Ref, Visitante…) do not arrive in the message events, and the other way round.
Parameters received by your script in a POST request, with the settings specified in your user panel under API configuration.
| Evento required | string What happened:
|
| idConversacion required | integer Conversation identifier. It is stable for the whole conversation: use it to group the messages in your system. |
| Canal required | string Channel the conversation started from: |
| idMensaje required | integer Message identifier. In the click events it is the id of the message that contained the buttons, not of the click. |
| Inicio | integer Message events only. |
| Motivo | string
|
| ConEncuesta | integer
|
| Mensajes | integer
|
| Duracion | integer
|
| Destino | string
|
| Estadistica | string
|
| Emisor | integer Only in the message events. Who wrote it: |
| Cuerpo | string Only in the message events. Text of the message. |
| Valor | string Only in the click events. The value of the button pressed, that is the right-hand side of your |
| Nota | integer Only in the click events. The same |
| Texto | string Only in the click events. The label of the button pressed, exactly as the visitor saw it (the emoji or the text). |
| Ref | string Only in the click events. The reference you sent in the |
| Visitante | object Only in the click events. Details of the person chatting:
|
| idAgente | integer Only in the click events. Agent the conversation was assigned to ( |
| Fecha required | string Date and time of the event, in |
{- "Evento": "mensaje-recibido",
- "idConversacion": "88",
- "Canal": "web",
- "idMensaje": "1420",
- "Inicio": "1",
- "Motivo": "agente",
- "ConEncuesta": "0",
- "Mensajes": "14",
- "Duracion": "842",
- "Estadistica": "summer-offer",
- "Emisor": "0",
- "Cuerpo": "Hello, I would like to know where my order is",
- "Valor": "5",
- "Nota": "5",
- "Texto": "😍",
- "Ref": "12345AB",
- "Visitante": "{ \"idVisitante\": 12, \"Nombre\": \"Ana\", \"Email\": \"ana@customer.com\", \"IP\": \"203.0.113.10\", \"Pais\": \"Spain\", \"Vars\": { \"order\": \"8812\" } }",
- "idAgente": "7",
- "Fecha": "2026-08-01 12:40:11"
}