docs

Livra Integration Guide (Production)

This document describes how to call Livra integration endpoints from your app.

Contents

← Back to documentation index

Shared authentication headers

Use these headers for all Livra integration endpoints:

x-signature must be HMAC-SHA256(rawRequestBody, apiSecret) encoded as lowercase hex (optionally prefixed with sha256=).

Create Merchant

Request body

{
  "merchant": {
    "name": "Example Merchant LLC",
    "state": "Dubai",
    "city": "Dubai",
    "street": "Example Street 1",
    "phoneNumber": "+971500000000",
    "zipcode": "00000",
    "TRN": "100000000000003",
    "CIN": 12345678
  },
  "sender": {
    "name": "Example Sender LLC",
    "state": "Dubai",
    "city": "Dubai",
    "street": "Business Bay",
    "phoneNumber": "+971511111111"
  },
  "contract": {
    "deliveryPartnerId": 10,
    "deliveryFee": 12.5,
    "exchangeFee": 4.25,
    "cancellationFee": 3.0
  }
}

Rules

Success

Errors

Create Order

Request body

{
  "products": [
    { "name": "string", "quantity": 1, "price": 12.5 },
    { "name": "string", "quantity": 2 }
  ],
  "productsToRetrieve": [
    { "name": "string", "quantity": 1 },
    { "name": "", "quantity": 0 }
  ],
  "merchantId": 1,
  "deliveryPartnerId": 1,
  "primaryName": "string",
  "primaryPhone": "string",
  "primaryPhone2": "",
  "primaryStreet": "",
  "primaryZone": "",
  "primaryCity": "string",
  "primaryState": "string",
  "primaryZipcode": "",
  "deliveryInstructions": "",
  "amount": 12.5,
  "allowOpen": true,
  "isExchange": false,
  "isFragile": false,
  "callback_link": "https://your-app.example.com/livra/webhook"
}

callback_link is optional. Omit it or leave off to disable webhooks for that order.

Rules

Success

Errors

Update Order

Request body

Patch-style payload. Only orderId is required; all other fields are optional.

{
  "orderId": 1234,
  "products": [{ "name": "string", "quantity": 1 }],
  "productsToRetrieve": [{ "name": "string", "quantity": 1 }],
  "primaryName": "string",
  "primaryPhone": "string",
  "primaryPhone2": "",
  "primaryStreet": "",
  "primaryZone": "",
  "primaryCity": "string",
  "primaryState": "string",
  "primaryZipcode": "",
  "deliveryInstructions": "",
  "amount": 12.5,
  "allowOpen": true,
  "isExchange": false,
  "isFragile": false
}

Constraints

Success

Errors

Change Request

Request body

{
  "orderId": 1234,
  "changes": [
    {
      "type": "PHONE_CHANGE",
      "oldValue": "+971500000000",
      "newValue": "+971511111111"
    }
  ],
  "comment": "Customer requested phone correction",
  "makeRegular": false
}

Rules

Success

Errors

Order status webhooks

When you include callback_link on create order, Livra calls that URL with an outbound webhook on every meaningful change to the order.

Every request carries two headers that identify exactly what you are receiving:

X-Webhook-Type: advanced
X-Webhook-Version: 1

Use X-Webhook-Version to guard your parsing logic against future changes.


Advanced webhook

Current version: 1

The raw event payload as recorded by the platform. Each delivery represents one discrete change, with an explicit event name, a full snapshot of the current field values, and their previous values for comparison. Driver outcomes are separate driver.* events rather than a comment on an order event.

Full documentation: Livra Webhooks — Advanced


Shared delivery mechanics

The following applies to all Livra webhooks.

Verifying signatures

Every request includes an X-Webhook-Signature header containing an HMAC-SHA256 of the raw request body, hex-encoded, using your Livra API secret (the same secret you use to sign requests to Livra).

Always verify this header before processing the payload.

Node.js

const crypto = require('crypto');

function verifySignature(secret, rawBody, signature) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signature)
  );
}

// Express example
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const sig = req.headers['x-webhook-signature'];
  if (!verifySignature(process.env.WEBHOOK_SECRET, req.body, sig)) {
    return res.status(401).send('Invalid signature');
  }
  const event = JSON.parse(req.body);
  // process event...
  res.sendStatus(200);
});

Python

import hmac, hashlib

def verify_signature(secret: str, raw_body: bytes, signature: str) -> bool:
    expected = hmac.new(
        secret.encode(),
        raw_body,
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature)

Go

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
)

func verifySignature(secret, signature string, body []byte) bool {
    mac := hmac.New(sha256.New, []byte(secret))
    mac.Write(body)
    expected := hex.EncodeToString(mac.Sum(nil))
    return hmac.Equal([]byte(expected), []byte(signature))
}

Important: always read the raw request body for signature verification. Parsing the JSON first and re-serialising it may produce a different byte sequence and cause verification to fail.

Responding to events

Reply with any 2xx status code to acknowledge successful delivery. The response body is ignored.

If your endpoint returns a non-2xx status or does not respond within 10 seconds, the delivery is retried automatically.

Retry schedule

Attempt Delay before retry
1 30 seconds
2 5 minutes
3 30 minutes
4 2 hours
5 8 hours

After 5 failed attempts the delivery is marked permanently failed and no further retries are made. The platform team can manually re-queue a delivery on request.

Identifying deliveries

Each delivery has a unique UUID in the X-Webhook-ID header. Use it to deduplicate events if your endpoint receives the same delivery more than once.