Rutba CRM

Outgoing webhooks

Tell your other systems the moment a lead arrives, an order is confirmed, a quote is accepted or a deal is won.

An outgoing webhook sends a signed message to an address of yours whenever something important happens in Rutba CRM. Use it to start work in another system: notify a channel, add a row to a sheet through an automation tool, or update your own software.

Before you start

  • Who can do it: owners and administrators of the company.
  • Plan: available when your plan includes API access.
  • An HTTPS address that accepts POST requests on the standard port 443. Automation tools such as Zapier, Make or n8n give you one when you create a webhook trigger.

Add a webhook

  1. In the sidebar open Contacts, then click API keys, then Webhooks.
  2. Under Add webhook, enter the HTTPS address.
  3. Under Send when, tick one or more events:
Event Sent when
A new lead arrives A new contact is created from a store order, a website form or a channel message. Contacts added by hand, imported or created through the API do not send it
A store order is confirmed A store order reaches a sale status (paid, processing, completed or delivered) for the first time. Imported past orders do not send it
A quote is accepted A customer or a team member accepts a quote
A deal is won A deal is marked as won
  1. Click Add webhook.
  2. A box shows the signing secret, starting with whsec_. Copy it now: it is shown only once.

A company can have up to 5 webhooks, counting paused ones and those created through the API, which appear on the same page. Removed webhooks do not count. A webhook's address and events cannot be changed after it is added: to change them, add a new webhook and remove the old one.

Pause or remove a webhook

Each webhook on the Webhooks page has two buttons:

  • Pause stops sending. Events that happen while it is paused are not queued for it. Deliveries that were already waiting stay waiting, and Resume sends them.
  • Remove takes the webhook off the list at once. It never sends again, deliveries still waiting for it are marked Skipped, and it no longer counts towards the 5. A removed webhook cannot be turned back on: to use the address again, add it as a new webhook, which gets a new signing secret.

Its past deliveries stay in Recent events until they are 30 days old.

What is sent

Each delivery is a POST with a JSON body:

{
  "id": "DELIVERY_ID",
  "event": "order_confirmed",
  "occurred_at": "2026-11-08T09:30:00Z",
  "data": {
    "contact": {"id": "CONTACT_ID", "name": "Sample Customer", "email": "customer@example.com", "phone": "+966500000001"},
    "subject": "order:ORDER_ID",
    "amount_minor": 125050,
    "currency": "SAR"
  }
}
Field Meaning
event lead_created, order_confirmed, quote_accepted or deal_won
data.contact The contact's current ID, name, email and phone
data.subject What the event is about: contact:, order:, quote: or deal: followed by its ID
data.amount_minor The amount in the smallest unit of the currency, such as 125050 for 1250.50. Empty for a new lead
data.currency The currency of the amount. Empty for a new lead

And three headers:

Header Value
X-Webhook-Event The event name
X-Webhook-Delivery The delivery ID, the same as id. Use it to ignore a repeat
X-Webhook-Signature t=TIMESTAMP,v1=SIGNATURE

Check the signature

SIGNATURE is the hex HMAC-SHA256 of TIMESTAMP.BODY, keyed with the signing secret, where BODY is the raw body exactly as received. Check it before you trust a delivery:

$body = file_get_contents('php://input');
preg_match('/t=(\d+),v1=([a-f0-9]+)/', $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '', $parts);
$expected = hash_hmac('sha256', ($parts[1] ?? '').'.'.$body, getenv('WEBHOOK_SECRET'));
$fresh = abs(time() - (int) ($parts[1] ?? 0)) <= 300;
if (! $fresh || ! hash_equals($expected, $parts[2] ?? '')) {
    http_response_code(403);
    exit;
}
// Answer 2xx quickly, then do the work.

The same scheme is used for webhooks created through the API; see Webhooks.

Delivery and retries

  • Deliveries go out within about a minute of the event, up to 50 a minute for each company.
  • Your address must answer with a 2xx status within 5 seconds. Redirects are not followed.
  • A failed delivery is tried again after 1 minute, 5 minutes, 30 minutes and 2 hours: 5 attempts in all. After that it is marked Failed.
  • After 10 deliveries in a row have failed, the webhook is paused. Fix your address, then click Resume.
  • A delivery is sent only once for each webhook, event and subject, even if the event happens again.
  • Events that happen while a webhook is paused are not queued for it.
  • A delivery still waiting after 30 days is dropped as Skipped, and so is one whose contact has been erased. The delivery history is kept for 30 days.
  • The address is checked before every send: addresses inside private networks are refused.

Test it

  1. Add a webhook with a test address from your automation tool, or a request inspector you control.
  2. Send a website form with new details. This creates a new lead.
  3. Within a minute, your address receives lead_created, and Recent events on the Webhooks page shows it as Delivered with the answer code.

Troubleshooting

What you see What to do
Use an https address with no port or password. Use https://, no :port, no user name or password, and no # part
This company has reached 5 webhooks. Paused webhooks still count. Click Remove on one you no longer need, then add the new one
Failed with a code such as 401 or 404 Your address refused the delivery. Check the address and that it accepts POST without a login
Failed with no code Your address did not answer within 5 seconds, could not be reached, or points to a private network
The webhook shows Paused 10 deliveries in a row failed. Fix the cause and click Resume
Nothing for A new lead arrives Contacts added by hand or imported do not send it. Test with a form, a store order or a message

Last updated 2026-11-08