Essere Voice API

n8n

Use n8n's built-in Webhook and HTTP Request nodes — no community node needed.

Receive events

  1. Add a Webhook node: method POST, path e.g. essere-events. Copy the production URL (https://your-n8n.example.com/webhook/essere-events).

  2. Register it:

    curl -X POST https://voice-public-api.essere.ai/v1/webhook-endpoints \
      -H "Authorization: Bearer $ESSERE_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{"url": "https://your-n8n.example.com/webhook/essere-events", "events": ["call.completed", "lead.captured"], "description": "n8n"}'
    

    Save the returned secret (whsec_...) as an n8n credential/variable.

  3. (Recommended) Verify the signature in a Code node right after the webhook. Set the Webhook node's Raw body option ON, then:

    // n8n Code node (Run Once for Each Item)
    const crypto = require('crypto');
    const secret = $env.ESSERE_WEBHOOK_SECRET; // whsec_...
    const header = $json.headers['x-essere-signature'] || '';
    const raw = Buffer.from($json.body, 'base64'); // raw body (base64 when Raw Body is on)
    
    let t = null;
    const v1s = []; // rotation overlap: the header may carry TWO v1 entries
    for (const part of header.split(',')) {
      const i = part.indexOf('=');
      if (part.slice(0, i) === 't') t = Number(part.slice(i + 1));
      else if (part.slice(0, i) === 'v1') v1s.push(part.slice(i + 1));
    }
    if (!t || Math.abs(Date.now() / 1000 - t) > 300) {
      throw new Error('stale or missing signature timestamp');
    }
    const expected = crypto.createHmac('sha256', secret).update(`${t}.`).update(raw).digest('hex');
    const ok = v1s.some(v1 => {
      const b = Buffer.from(v1);
      return b.length === Buffer.from(expected).length &&
        crypto.timingSafeEqual(Buffer.from(expected), b);
    });
    if (!ok) throw new Error('bad signature');
    return { json: JSON.parse(raw.toString()) };
    
  4. Branch on {{$json.type}} (e.g. a Switch node) and route call.completed events into your CRM, Slack, database, etc.

Call the API

Add an HTTP Request node:

For list endpoints, enable the node's Pagination option: type "Response contains next URL"? No — use Update a parameter in each request with parameter cursor set from {{$response.body.next_cursor}}, stopping when {{$response.body.has_more}} is false.