सेलर्स के लिएबीटा

ऑर्डर API और वेबहुक

अपनी वेबसाइट या ERP से ऑर्डर Production Floor पर भेजें, और लाइनें मैप होने, जॉब आगे बढ़ने और ऑर्डर शिप होने पर साइन किए गए वेबहुक पाएँ।

अगर आपके ऑर्डर Shopify या WooCommerce के बजाय आपकी अपनी वेबसाइट या ERP में हैं, तो आपका सॉफ़्टवेयर उन्हें सीधे आपके Production Floor पर भेज सकता है, और काम आगे बढ़ने पर Yantra Bhavan आपके सॉफ़्टवेयर को वापस सूचना दे सकता है: जब कोई लाइन मैप हो, जब किसी जॉब की स्थिति बदले और जब वह शिप हो।

ऑर्डर भेजना

आपके सॉफ़्टवेयर को ऑर्डर एक्सेस वाला थर्ड-पार्टी क्रेडेंशियल चाहिए। Owner या Manager Integrations खोलता है, Connect → Third-party software चुनता है, और Can create orders पर टिक करता है। उस टिक के बिना वाला क्रेडेंशियल कोटेशन पढ़ सकता है, पर ऑर्डर रूट पर अस्वीकार किया जाता है। क्रेडेंशियल ऐक्सेस टोकन कैसे बनता है, यह इंटीग्रेशन बनाना में है। ऑर्डर इनटेक के लिए ऐसा प्लान चाहिए जिसमें इंटीग्रेशन और Production Floor दोनों हों।

curl -X POST "https://shriganesh.yantrabhavan.com/api/v1/integration/orders" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Idempotency-Key: 9b2f6c1e-3d4a-4f7b-8c55-0e1a2b3c4d5e" \
  -H "Content-Type: application/json" \
  -d '{
    "externalId": "SO-10442",
    "number": "#10442",
    "version": 1760342400000,
    "dueAt": "2026-10-20T00:00:00Z",
    "lines": [
      { "lineId": "L1", "sku": "BRACKET-PLA-01",
        "title": "Mounting bracket, black PLA", "quantity": 4, "unitPrice": 120 }
    ]
  }'
  • Idempotency-Key ज़रूरी है: आपके भेजे हर ऑर्डर के लिए एक नया रैंडम मान (एक UUID)। अगर अनुरोध टाइम आउट हो जाए, तो उसे उसी कुंजी और बॉडी के साथ फिर भेजें — आपको मूल जवाब वापस मिलेगा और दूसरा ऑर्डर नहीं बनेगा। वही कुंजी अलग बॉडी के साथ भेजने पर 422 के साथ अस्वीकार की जाती है। कुंजियाँ 30 दिनों तक याद रखी जाती हैं।
  • externalId आपकी ऑर्डर आईडी है। इसे (नई कुंजी के साथ) फिर से भेजने पर नया ऑर्डर बनाने के बजाय वही ऑर्डर अपडेट होता है। बढ़ता हुआ version भेजें — उदाहरण के लिए मिलीसेकंड में आपका updated-at समय — ताकि देर से पहुँचा पुराना अपडेट अनदेखा हो जाए। रद्द करने के लिए cancelled को true पर सेट करें।
  • नए ऑर्डर के लिए जवाब 201 और अपडेट के लिए 200 होता है, जिसमें ऑर्डर की इनटेक स्थिति और, हर लाइन के लिए, यह कि वह मैप हुई है या नहीं और उसके कौन-से जॉब हैं, शामिल होते हैं। GET …/orders/ बाद में यही संरचना लौटाता है।
  • ऑर्डर आपके Shopify और WooCommerce ऑर्डर के साथ Store orders में पहुँचता है। जिस लाइन के SKU का नियम है वह अपने आप फ़्लोर पर चली जाती है; बाकी हर लाइन आपके एक बार मैप करने का इंतज़ार करती है — आपके ऑनलाइन स्टोर के ऑर्डर देखें।

वेबहुक एंडपॉइंट जोड़ना

Integrations में, Webhooks के अंतर्गत, Owner या Manager वह URL जोड़ता है जिसे Yantra Bhavan को कॉल करना चाहिए और अपने चाहे इवेंट चुनता है। URL मानक पोर्ट पर https:// होना चाहिए और सार्वजनिक इंटरनेट तक पहुँचना चाहिए — निजी नेटवर्क के भीतर के पते अस्वीकार किए जाते हैं, सेव करते समय भी और हर डिलीवरी पर भी।

  • quotation.requested — किसी खरीदार ने आपकी दुकान से कोटेशन माँगा।
  • external_order.line_mapped — किसी स्टोर या API ऑर्डर लाइन को प्रोसेस दिया गया — SKU नियम से या हाथ से।
  • production_job.status_changed — कोई प्रोडक्शन जॉब एक स्थिति से दूसरी में गया (कतार में, प्रगति में, पूरा…)।
  • shipment.created — आपके किसी ऑर्डर के लिए कैरियर और ट्रैकिंग के साथ शिपमेंट बनाया गया।

डिलीवरी कैसी दिखती है

हर इवेंट एक JSON POST है। बॉडी में आईडी, स्थितियाँ और ट्रैकिंग होती है — आपके खरीदार की संपर्क जानकारी कभी नहीं। जब ऑर्डर आपके सिस्टम से आया हो, तो external_id और line_id आपकी अपनी आईडी होती हैं, इसलिए आप बिना खोजे उसका मिलान कर सकते हैं।

{
  "id": "0d6f2b1e-7c1a-4b8e-9a51-3f2e4d5c6b7a",
  "type": "production_job.status_changed",
  "created_at": "2026-10-13T09:30:00.000Z",
  "shop_id": "…",
  "data": {
    "job_id": "…", "from": "READY", "to": "IN_PROGRESS",
    "order_id": "…", "order_item_id": "…", "source": "API",
    "external_id": "SO-10442", "line_id": "L1"
  }
}
  • X-YB-Event-Id — इवेंट आईडी, बॉडी में id के समान।
  • X-YB-Event-Type — इवेंट का प्रकार।
  • X-YB-Timestamp — यह कब साइन किया गया, Unix सेकंड में।
  • X-YB-Signature — v1= के बाद टाइमस्टैम्प, एक पूर्ण विराम (.) और रॉ बॉडी का hex HMAC-SHA256, जिसकी कुंजी आपका पूरा whsec_ सीक्रेट है।

डिलीवरी की जाँच करना

किसी भी डिलीवरी पर भरोसा करने से पहले उसे जाँचें। चार नियम, सभी नीचे दिए स्निपेट में:

  1. सिग्नेचर रॉ रिक्वेस्ट बॉडी पर, बाइट-दर-बाइट, बनाएँ। JSON को पार्स करके फिर से सीरियलाइज़ करने से वह बदल जाती है, और सिग्नेचर मेल नहीं खाएगा।
  2. सिग्नेचर की तुलना कॉन्स्टेंट टाइम में करें (crypto.timingSafeEqual, hash_equals, hmac.compare_digest), कभी भी == या === से नहीं।
  3. आपकी घड़ी से पाँच मिनट से अधिक अंतर वाला टाइमस्टैम्प अस्वीकार करें, ताकि पकड़ी गई डिलीवरी को बाद में दोबारा न चलाया जा सके।
  4. जिन इवेंट आईडी को आप संभाल चुके हैं उन्हें याद रखें और दोहराव छोड़ दें। रीट्राई में वही आईडी होती है, और कभी-कभी एक इवेंट दो बार पहुँचता है।

Node.js

const crypto = require('node:crypto');

// Express: app.post('/yb-webhooks', express.raw({ type: 'application/json' }), handler)
// req.body must be the RAW bytes — re-serialised JSON will not verify.
function verifyYantraBhavan(secret, req) {
  const timestamp = req.get('X-YB-Timestamp') || '';
  const signature = req.get('X-YB-Signature') || '';
  if (!/^\d+$/.test(timestamp)) return false;
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;

  const expected =
    'v1=' +
    crypto.createHmac('sha256', secret)
      .update(timestamp + '.')
      .update(req.body)
      .digest('hex');
  const a = Buffer.from(expected);
  const b = Buffer.from(signature);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

PHP

<?php
$secret    = getenv('YB_WEBHOOK_SECRET');           // the whole whsec_… value
$payload   = file_get_contents('php://input');      // raw body
$timestamp = $_SERVER['HTTP_X_YB_TIMESTAMP'] ?? '';
$signature = $_SERVER['HTTP_X_YB_SIGNATURE'] ?? '';

if (!ctype_digit($timestamp) || abs(time() - (int) $timestamp) > 300) {
    http_response_code(400);
    exit;
}
$expected = 'v1=' . hash_hmac('sha256', $timestamp . '.' . $payload, $secret);
if (!hash_equals($expected, $signature)) {
    http_response_code(400);
    exit;
}
http_response_code(204);   // accept now, do the work afterwards

Python

import hashlib
import hmac
import time

def verify_yantra_bhavan(secret: str, headers, raw_body: bytes) -> bool:
    timestamp = headers.get("X-YB-Timestamp", "")
    signature = headers.get("X-YB-Signature", "")
    if not timestamp.isdigit() or abs(time.time() - int(timestamp)) > 300:
        return False
    mac = hmac.new(
        secret.encode(), timestamp.encode() + b"." + raw_body, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest("v1=" + mac, signature)

# Flask: verify_yantra_bhavan(secret, request.headers, request.get_data())

रीट्राई, और कोई एंडपॉइंट कब बंद किया जाता है

10 सेकंड के भीतर किसी भी 2xx से जवाब दें और धीमा काम बाद में करें। बाकी कुछ भी — एरर स्टेटस, टाइमआउट, रीडायरेक्ट — विफलता माना जाता है, और वह इवेंट आठ बार तक फिर से भेजा जाता है, बीच में 1, 2, 4 … 64 मिनट रुककर (कुल लगभग दो घंटे)।

अगर कोई एंडपॉइंट 24 घंटे तक लगातार विफल होता रहे, तो उसे बंद कर दिया जाता है और दुकान के मालिक को ईमेल मिलता है। Integrations में वह Disabled दिखता है; रिसीवर ठीक करें, उसे जाँचने के लिए Send test event का उपयोग करें, फिर Re-enable करें। बंद रहने के दौरान के इवेंट दोबारा नहीं भेजे जाते — छूटे हुए अपडेट पाने के लिए GET …/orders/ का उपयोग करें। Recent deliveries में हर प्रयास उसके स्टेटस कोड और एरर के साथ सूचीबद्ध होता है।