How webhooks work
A webhook is an HTTPS address of yours that we POST to when an event happens. Each request is signed so you can check it came from RaviFurnitures.
Getting a webhook
Section titled “Getting a webhook”Send your RaviFurnitures contact:
- the HTTPS address (
https://…) that should receive deliveries, and - the events you want (see Webhook events and your integration’s page).
A staff member with the MANAGE_WEBHOOKS permission creates the webhook in the admin portal under System → Settings, on the Webhooks & API keys page, with Webhooks → New. The signing secret is shown once, straight after creation. Ask for it over a channel your organisation approves for secrets. It is 64 characters long; keep it with your other production secrets.
Use one webhook per receiving system. Staff can switch a webhook off and back on. While it is off, events are not queued for it at all, and deliveries already waiting are given up. Events raised in that time are not sent later.
To change the secret, staff create a new webhook (which gets a new secret), then switch the old one off. While both are on you receive every event twice, signed with different secrets; accept either secret and de-duplicate as described below.
The request
Section titled “The request”POST /your/webhook/path HTTP/1.1Content-Type: application/jsonX-RaviFurniture-Event: ORDER_CREATEDX-RaviFurniture-Signature: 3f1c…(64 lowercase hex characters)
{"data":{"currency":"INR","grandTotalPaise":2499900,"lines":[{"quantity":1,"sku":"DEMO-SOFA-3S","totalPaise":2299900},{"quantity":2,"sku":"DEMO-CUSHION-SET","totalPaise":200000}],"number":"RFO/2026-27/00000","paymentMethod":"PREPAID","segment":"HOME","status":"UNCONFIRMED"},"event":"ORDER_CREATED","timestamp":"2026-10-17T04:45:02.123456+00:00","version":"1"}The body is a JSON envelope with four keys:
| Key | Type | Meaning |
|---|---|---|
event |
string | The event type, also sent in X-RaviFurniture-Event. |
data |
object | The event’s fields. They differ per event: see Webhook events. |
timestamp |
string | When the event was raised, in UTC, ISO 8601 with +00:00. It has microseconds unless they happen to be zero. Retries keep the original value. |
version |
string | Payload version, currently "1". |
The body is compact JSON with keys sorted, and every non-ASCII character escaped as \uXXXX (a Telugu product name such as టేకు చెక్క కుర్చీ arrives as \u0c1f\u0c47\u0c15\u0c41 \u0c1a\u0c46\u0c15\u0c4d\u0c15 \u0c15\u0c41\u0c30\u0c4d\u0c1a\u0c40). Verify the signature over these raw bytes before you parse them: see Verifying signatures.
There is no event ID or delivery ID in the body or headers.
What counts as success
Section titled “What counts as success”Answer with any 2xx status within 10 seconds. Everything else is a failed attempt:
- any other status code, including
3xx: redirects are not followed, so give the exact final URL; - a connection or TLS error (the certificate is checked);
- no response in time. The 10 seconds apply to each stage: connecting, sending, and waiting for your response.
The first 1,024 characters of your response body are kept in the delivery log, where RaviFurnitures staff can read them. Keep it short, and never put secrets or personal data in it.
Answer quickly. Store the delivery (a database row or a queue message), reply 200, and do the work afterwards.
Retries
Section titled “Retries”A failed attempt is retried about 3 minutes later. There are at most four attempts in all:
| Attempt | When |
|---|---|
| 1 | about 2 seconds after the event |
| 2 | about 3 minutes after attempt 1 failed |
| 3 | about 3 minutes after attempt 2 failed |
| 4 | about 3 minutes after attempt 3 failed |
These are minimums. Each failed attempt can take up to 10 seconds before it counts as failed, and an attempt can wait for a free worker. The last attempt usually comes 9 to 10 minutes after the event. Occasionally the first attempt is skipped, when the change that raised the event takes more than 2 seconds to save; you then get the later attempts only.
The delivery log marks a delivery FAILED once attempt 3 fails, but attempt 4 still goes out. If attempt 4 succeeds, the log changes to SUCCESS.
After the last attempt nothing more is sent, and there is no way to resend an old event. Your integration must catch missed events with the reconciliation queries described on its page.
You only receive events for changes that were saved. If the change that raised an event is rolled back, no delivery is made.
Ordering
Section titled “Ordering”There is no ordering guarantee. Each delivery is retried on its own, so an event can arrive after a later one. For example, a retried ORDER_CREATED can arrive after ORDER_FULFILLED for the same order.
Don’t apply events as a sequence of changes. Use the event as a prompt, then read the current state with a query. If you must compare events, use timestamp.
Duplicates
Section titled “Duplicates”Expect the same delivery more than once. It can happen when:
- your server did the work but answered after 10 seconds, or with a non-
2xxstatus; - our delivery worker restarted part-way through an attempt (the job is only marked done after it finishes);
- the fourth attempt follows a third that you actually processed.
Make your receiver idempotent:
- De-duplicate deliveries. Every attempt of a delivery has a byte-identical body, and when several of your webhooks receive the same event, they get the same body too. Use the SHA-256 of the raw body as the key (or
eventplustimestamp). Keep keys for at least a day. - Upsert by the business key. Different events can concern the same record, and some fire on every save. Write by order number, product slug, challan number or sale ID, never “insert another row”.
Delivery log
Section titled “Delivery log”RaviFurnitures keeps a log of each delivery: status, number of attempts, your response status and the start of your response body. Entries older than 30 days are deleted by a weekly job (Sunday 03:30 IST by default). See Checking your receiver with PING for what staff can see there.
Source addresses
Section titled “Source addresses”The addresses deliveries come from are not documented, and you should not rely on them. Check the signature on every request instead.