Order lookup
The signed request Manova sends your store when a customer asks where their order is.
When a customer asks about an order ("سفارشم کجاست؟", or the پیگیری سفارش button), the assistant asks for the order number and the mobile number they bought with. Manova then asks your store, and replies with the status, the items and the tracking code. Both numbers must match the same order, so nobody can read someone else's order by guessing a number.
Order lookup is on when the store is connected with an apiUrl and پیگیری سفارش is enabled in the panel.
The request
POST {apiUrl}/order
Content-Type: application/json
Accept: application/json
User-Agent: ManovaBot/1.0 (+https://manova.ir)
X-Manova-Timestamp: 1793371200
X-Manova-Signature: 5d1c2a0b9e...f4
{"number":"10457","phone":"09121234567"}| Field | Format |
|---|---|
number | The order number as the customer typed it, digits only (Persian digits are converted). |
phone | An Iranian mobile number, normalised to 09xxxxxxxxx. |
Manova waits up to 10 seconds, does not follow redirects and only calls public addresses.
Verify the signature
The signature is a hex HMAC-SHA256 of timestamp + "." + rawBody, keyed with the secret: the part of your
connection key after the last underscore.
Reject the request with 401 unless:
- the timestamp is within 5 minutes of your clock, and
- the signature matches, compared in constant time.
Always hash the raw body bytes exactly as received, before parsing the JSON.
<?php
$key = getenv('MANOVA_STORE_KEY'); // mnv_<store>_<secret>
$secret = substr($key, strrpos($key, '_') + 1);
$body = file_get_contents('php://input');
$time = $_SERVER['HTTP_X_MANOVA_TIMESTAMP'] ?? '';
$signature = $_SERVER['HTTP_X_MANOVA_SIGNATURE'] ?? '';
$expected = hash_hmac('sha256', $time . '.' . $body, $secret);
if (abs(time() - (int) $time) > 300 || !hash_equals($expected, $signature)) {
http_response_code(401);
exit(json_encode(['error' => 'unauthorized']));
}
$data = json_decode($body, true);Find the order
Look the order up by the number customers see (your order number, which may differ from your database id). Then
check that phone matches one of the order's phone numbers. Compare the last 10 digits of each, so
+989121234567, 00989121234567 and 09121234567 all match.
If there is no such order, or the phone does not match, answer the same way, so a caller cannot tell which one was wrong:
HTTP/1.1 404 Not Found
Content-Type: application/json
{"found": false}The response
{
"found": true,
"order": {
"number": "10457",
"status": "processing",
"status_label": "در حال انجام",
"date": "۸ آبان ۱۴۰۵",
"date_iso": "2026-10-30T10:24:00+03:30",
"total": "۴٬۹۰۰٬۰۰۰ تومان",
"items": [
{ "name": "کفش رانینگ مردانه مدل آریا", "qty": 1 },
{ "name": "جوراب ورزشی", "qty": 2 }
],
"shipping": "پست پیشتاز",
"tracking": {
"code": "123456789012345678901234",
"carrier": "پست",
"url": "https://tracking.post.ir/?id=123456789012345678901234"
},
"url": "https://shop.example.com/account/orders/10457"
}
}Everything inside order is optional except number and status. HTML is stripped from every field.
Prop
Type
status | Meaning shown to the customer |
|---|---|
pending | Waiting for payment |
on-hold | Placed, waiting for payment confirmation or review |
processing | Paid, being prepared and shipped |
completed | Completed and shipped |
cancelled | Cancelled |
refunded | Refunded |
failed | Payment failed |
Any other status is shown with your status_label.
Failures
A response other than 200 or 404 {"found": false}, a timeout or invalid JSON counts as a failure: the customer is
told the store cannot be reached right now, and the error appears in the panel's فروشگاه section.
Each conversation can make up to 6 lookups an hour, which stops number guessing in the chat itself.