Rutba CRM

Custom store

Send orders, quote requests and products from your own store software to Rutba CRM, signed with a shared secret.

If your store runs on software you build yourself, or on a platform without a ready connector, it can send signed events to a store connection. This page is for the developer who adds the sending side: it gives the address, the signature, the event formats and the answers.

Before you start

  • Who can create the connection: owners and administrators of the company.
  • Plan: available when your plan includes Store connections.
  • A pipeline must exist: quote requests become deals in the stage you choose. See Deals and pipelines.
  • Your store sends; Rutba CRM does not fetch. Nothing is imported automatically from a custom store, so past orders arrive only if your store sends them.

Create the connection

  1. In the sidebar open Products, then click Store connections.
  2. Under Connect a store, choose Our store platform or custom as Store platform. Store currency is optional for this platform.
  3. In New quote requests start in, choose the stage for quote requests, and in Assign new deals to, the person who receives them.
  4. Click Connect a store. Copy the Signing secret now: it is shown only once.
  5. Copy the address in Address for your store.

Keep both in your store's environment settings, not in its code:

STORE_EVENTS_URL=ADDRESS_FOR_YOUR_STORE
STORE_EVENTS_SECRET=YOUR_SIGNING_SECRET

Sign every request

Send POST with a JSON body and two headers:

Header Value
X-CRM-Timestamp The current time in Unix seconds
X-CRM-Signature sha256= followed by the hex HMAC-SHA256 of TIMESTAMP.BODY, keyed with the signing secret

BODY is the exact bytes you send. A request whose time is more than 5 minutes away from the server's clock is refused, so keep your server's clock in sync.

$body = json_encode(['event' => $event, $key => $document], JSON_THROW_ON_ERROR);
$time = (string) time();
$signature = 'sha256='.hash_hmac('sha256', $time.'.'.$body, getenv('STORE_EVENTS_SECRET'));
// POST $body to STORE_EVENTS_URL with Content-Type: application/json,
// X-CRM-Timestamp: $time and X-CRM-Signature: $signature

Send from a background job after the order is saved, so a slow answer never delays a purchase. Retry only on a 5xx answer or a timeout.

Events

Event Key in the body Effect
order.created order Saves the order and finds or creates the customer
order.updated order The same, for any change. Send a newer updated_at each time
quote.requested quote Creates a deal in the chosen stage with the lines, once per id
product.upserted product Creates or updates a product

Order and quote fields

{"event": "order.updated", "order": {
  "id": "1001", "number": "A-1001", "status": "paid", "currency": "SAR",
  "total": "1250.50", "placed_at": "2026-11-06T10:00:00Z", "updated_at": "2026-11-06T10:05:00Z",
  "customer": {"name": "Sample Customer", "email": "customer@example.com", "phone": "+966500000001", "company": "Sample Cafe"},
  "items": [{"sku": "M-1", "name": "Espresso machine", "quantity": 1, "total": "1250.50"}],
  "attribution": {"source": "google", "medium": "cpc", "campaign": "autumn-sale"}}}
Field Rule
id Required, your own order ID, up to 100 characters
number Optional, the number customers see, up to 60 characters
status Optional, up to 40 characters. Defaults to pending
currency Required: USD, EUR, GBP, TRY, SAR or AED
total Required, a string such as "1250.50", with a dot and at most two decimals
placed_at, updated_at placed_at is required. Both are dates such as ISO 8601
customer.name Required, up to 160 characters
customer.email, customer.phone At least one is required. A phone needs 6 to 15 digits, ideally with its country code
customer.company Optional
items Optional, up to 200 lines. Each needs name, a whole quantity of at least 1 and a string total; sku is optional
attribution Optional source, medium and campaign, up to 120 characters each. The campaign appears in reports
recovery_url Optional https:// link where the customer can still pay an unpaid order

A quote request uses the same fields under the key quote. Its deal amount is the sum of its lines, or the total when there are no lines.

Statuses

paid, processing, completed and delivered count as sales, and the first of them makes a lead a customer. Any other status is saved as it is and does not count. Use cancelled or refunded to take an order out of sales.

Product fields

{"event": "product.upserted", "product": {"id": "P-77", "name": "Espresso machine", "sku": "M-1", "price": "1250.50", "currency": "SAR", "active": true, "updated_at": "2026-11-06T10:00:00Z"}}

id and name are required. A product is matched by id, then by sku. "active": false archives it. A price is saved only with a currency. An update older than the saved one is ignored.

Answers

Answer Meaning
200 {"status": "received"} Saved, or a repeat of something already saved
200 {"status": "paused"} The company or its plan does not accept store events right now. Nothing was saved
403 Wrong signature, or a time more than 5 minutes off
404 The connection does not exist or is turned off
422 {"errors": [...]} The document is not valid. The list names the fields. Sending it again unchanged will not help

Each sending server can make up to 600 requests a minute.

Test it

  1. Send an order.updated with the status paid and a campaign in attribution.
  2. In Rutba CRM, check that the contact exists, the order shows on their record, and the store card shows Last event:.
  3. Send the same order again with the status cancelled and a later updated_at. In Reports, the order leaves sales and the campaign total drops.

Good to know

  • Repeats are safe. An order update older than the saved one, or identical to it, is ignored; a quote request with an id already seen is ignored.
  • Customer matching: the email decides first. A phone only joins a contact that has no other email. Details of an existing contact are never overwritten.
  • Replace the secret on the store card gives a new secret at once, and the old one stops working. Update your store's settings straight away.

Last updated 2026-11-08