Skip to content

Webhooks ​

Webhooks on Yarla are HTTP requests we send to your server at a fixed moment in the conversation. You create them as an HTTP request action; this article covers what all three moments have in common: the signature, the retry policy and the timeouts.

Three moments ​

MomentWhat happensArticle
Before the callAs soon as an inbound phone call arrives, before the greeting, we send the caller's number, your own number and your business details to your endpoint. This does not happen on outbound calls, SMS, WhatsApp or webchat. The assistant receives the JSON you return as context; you use the values in the greeting as {{key}} and in after-conversation actions as {{inboundContext.key}}.Inbound webhook (pre-call)
During the conversationBased on your condition, the assistant decides to call your endpoint (a "tool call"), for example to look up an order status. Your response goes back to the assistant, which uses it in the conversation.Webhook during the call
After the conversationOnce the conversation has ended and the summary and custom variables have been extracted, we send the conversation data to your endpoint.Webhook after the call

Want a message for every ended conversation, new request and appointment across your whole account, without an action per assistant? Use Event webhooks (Enterprise). They are signed the same way.

All three are POST requests with Content-Type: application/json by default. In the action you can change the method, headers and body. The signature is always included, except on GET: that request has no body and no signature.

Verify the signature ​

Every request we send, except GET, carries the header:

text
X-Yarla-Signature: t=1757581245,v1=5f1c9b2a7d0e4c6b8a1f3e5d7c9b2a4f6e8d0c2b4a6f8e0d2c4b6a8f0e2d4c6b
  • t is the Unix time in seconds at which we sent the request.
  • v1 is HMAC-SHA256(secret, t + "." + rawBody), hex encoded.

You find the secret under Settings → Connections as Webhook secret. Generate new creates a new secret after you confirm; the old one stops working, so update your server straight after. Only admins of your own business see and renew the secret here. A reseller or support agent viewing your account does not see it.

Verify like this:

  1. Split the header on the comma and read t and v1.
  2. Reject the request if the difference between now and t is more than 300 seconds (replay protection).
  3. Compute the HMAC over exactly the raw body, meaning the bytes as received, not re-serialised JSON.
  4. Compare the result with v1 using a constant-time comparison.

Example in Node.js with Express, for an after-conversation action:

js
const express = require('express');
const crypto = require('crypto');

const app = express();
const SECRET = process.env.YARLA_WEBHOOK_SECRET;
const TOLERANCE_SECONDS = 300;

function verifySignature(header, rawBody) {
  if (!header) return false;
  const parts = Object.fromEntries(header.split(',').map((kv) => kv.split('=')));
  const t = Number(parts.t);
  const v1 = parts.v1;
  if (!t || !v1) return false;
  if (Math.abs(Date.now() / 1000 - t) > TOLERANCE_SECONDS) return false;

  const expected = crypto
    .createHmac('sha256', SECRET)
    .update(`${t}.`)
    .update(rawBody) // Buffer with the raw bytes
    .digest('hex');

  const a = Buffer.from(expected, 'hex');
  const b = Buffer.from(v1, 'hex');
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

// express.raw keeps req.body as a Buffer instead of parsed JSON
app.post('/yarla/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  if (!verifySignature(req.get('X-Yarla-Signature'), req.body)) {
    return res.status(401).send('invalid signature');
  }

  const event = JSON.parse(req.body.toString('utf8'));

  // Answer immediately, then process asynchronously
  res.status(200).json({});
  setImmediate(() => handleEvent(event));
});

function handleEvent(event) {
  // Without a custom body the request contains conversationId, summary and outcome, among others
  if (event.outcome === 'callback_requested') {
    // for example: create a task in your CRM
  }
}

app.listen(3000);

Use the raw body

If your framework parses the JSON before you verify the signature, you compute the HMAC over a different string and it will never match. In Express use express.raw; other frameworks have an equivalent option to access the original bytes.

Retry policy ​

We do not retry a failed request automatically, and you cannot re-run it manually either. A request counts as failed on a status code outside 2xx, on a timeout or when your endpoint cannot be reached. We follow redirects and the last response counts. Use the final address in the action: a redirect with 301, 302 or 303 turns your request into a GET without a body.

  • After the conversation: the result is shown on the conversation in the Inbox. Open Technical details and look under What the actions did: it shows the status code and, on an error status, the start of the response. On a timeout or an unreachable endpoint it shows only the error message. If an action fails, the next actions still run.
  • Before the call: the assistant starts without the details from that action. What your endpoint did return is under Technical details, in Details looked up before the call; a failed request does not show there.
  • During the conversation: the result is not shown on the conversation. On an error status the assistant hears that the lookup failed and deals with it in the conversation.

Design your endpoint for after-conversation actions accordingly: verify the signature, store the request, answer 2xx immediately and process the data asynchronously. If you process inside the request itself, you hit the timeout as soon as your CRM is a little slow. For actions before the call and during the conversation the assistant needs your answer: return the data straight away, within the timeout below.

IP allowlisting ​

A list of fixed IP addresses is not available: our egress addresses can change. Secure your endpoint with HTTPS and the signature. For an extra layer, add a custom header with a long random token in the action and check it on your server. On a GET request such a header is the only check, because no signature is sent.

Timeouts ​

MomentTimeoutWhen exceeded
Before the call3 secondsThe assistant starts without the details from this action.
During the conversation6 secondsThe lookup fails and the assistant gets no data from your endpoint. On a phone call the assistant can go quiet, so answer well within this time.
After the conversation8 secondsThe action is marked as failed on the conversation.

If you regularly get close to these limits, see Troubleshooting: webhooks.

Yarla Help — the guide to your AI phone assistant.