<?php
$page_lang = 'en';
$page_title = 'Webhooks: signed events for enterprise clients | Corp-Merch.MX';
$page_description = 'HMAC SHA-256 signed webhooks: order, shipment, invoice, quality and catalogue events, with signature validation, retries and idempotency.';
$og_image = '/assets/images/og-sdk.png';
$page_section = 'sdk';
$page_slug    = 'webhooks';

$labels = [
    'name' => 'Name', 'name_placeholder' => 'Your name',
    'contact' => 'Work email', 'contact_placeholder' => 'name@company.com',
    'company' => 'Company', 'company_placeholder' => 'Legal entity name',
    'message' => 'Brief', 'message_placeholder' => 'Operations stack, required events, endpoint...',
    'submit' => 'Set up webhooks', 'sending' => 'Sending...',
    'turnstile_error' => 'Verify you are not a robot'
];

require __DIR__ . '/../../../includes/header.php';
?>
<link rel="alternate" hreflang="es" href="https://corp-merch.com.mx/es/pages/sdk/webhooks">
<link rel="alternate" hreflang="en" href="https://corp-merch.com.mx/en/pages/sdk/webhooks">
<link rel="alternate" hreflang="ru" href="https://corp-merch.com.mx/ru/pages/sdk/webhooks">
<nav class="breadcrumbs"><a href="/en/">Home</a><span class="sep">&rsaquo;</span><a href="/en/pages/sdk/">SDK</a><span class="sep">&rsaquo;</span><span>Webhooks</span></nav>

<section class="hero">
  <div class="container">
    <div class="hero-content">
      <p class="eyebrow">HMAC SHA-256 · Retry · Idempotency · Sandbox</p>
  <h1>Signed webhooks for enterprise clients</h1>
      <p class="hero-subtitle">How your system receives notifications from the workshop when something changes on an order, a delivery, an invoice, a quality alert or a catalogue item. Webhooks are signed, delivered with unique identifiers and retried with exponential backoff. Setup is from the customer portal against a valid HTTPS endpoint.</p>
      <div class="hero-cta"><a href="#contact-form" class="btn btn-primary">Set up webhooks</a></div>
    </div>
  </div>
</section>

<section class="section"><div class="container">
  <h2>How a webhook is signed</h2>
  <p>Each webhook is signed with HMAC SHA-256. The signature is calculated over the raw request body — unprocessed — concatenated with the timestamp and a unique event identifier. The signature is delivered in the <code>X-Corp-MX-Signature</code> header in hex format. You recalculate the signature with the shared secret and compare with the received signature. If they do not match, the webhook is discarded.</p>
  <p>Headers arriving with each webhook are <code>X-Corp-MX-Signature</code> with the HMAC signature, <code>X-Corp-MX-Timestamp</code> with the UNIX timestamp in seconds at send time, <code>X-Corp-MX-Event-Id</code> with the unique event identifier — useful for idempotency — and <code>X-Corp-MX-Event-Type</code> with the event type — <code>order.created</code>, <code>order.approved</code>, <code>order.shipped</code>, <code>invoice.issued</code>, among others.</p>
</div></section>

<section class="section section-alt"><div class="container">
  <h2>Order events</h2>
  <p>Order events cover the full lifecycle. <code>order.created</code> when an order is registered after quote approval. <code>order.in_production</code> when production kicks off. <code>order.stage_changed</code> when a stage advances — cutting, sewing, decoration, packing. <code>order.quality_check</code> when the order passes quality inspection. <code>order.shipped</code> when the order leaves the workshop with the carrier tracking number. <code>order.delivered</code> when delivery is confirmed with receipt.</p>
  <p>For customers with multiple plants, the <code>order.stage_changed</code> event includes the plant identifier. For runs with multiple lines, the <code>order.line_completed</code> event fires when each line finishes production, before the consolidated batch. This lets the customer know when a line is ready without waiting for the full order.</p>
</div></section>

<section class="section"><div class="container">
  <h2>Shipment events</h2>
  <p>Shipment events cover the logistics lifecycle. <code>shipment.created</code> when the tracking number is generated with the carrier. <code>shipment.in_transit</code> when the shipment is en route with the carrier's first update. <code>shipment.out_for_delivery</code> when the shipment goes out for delivery. <code>shipment.delivered</code> when delivery is confirmed with the recipient name and, when applicable, the scanned signature.</p>
  <p>For multi-stop deliveries — multi-site, multi-venue events — the <code>shipment.stop_completed</code> event fires when each stop is confirmed. The information includes the address, the receiving lead, the pieces delivered and the signature when applicable. Traceability is kept for the programme's warranty period.</p>
</div></section>

<section class="section section-alt"><div class="container">
  <h2>Invoice and quality events</h2>
  <p>Invoice events cover the billing cycle. <code>invoice.issued</code> when the CFDI 4.0 invoice is stamped by SAT and available for download. <code>invoice.paid</code> when payment is confirmed per the agreed payment method and timeline. <code>invoice.canceled</code> when the invoice is cancelled for the stated reason and re-billing is issued where applicable.</p>
  <p>Quality events cover non-conformities and corrective actions. <code>quality.non_conformity_opened</code> when an 8D is opened for a non-conformance detected in production or reported by the customer. <code>quality.non_conformity_contained</code> when immediate containment is executed. <code>quality.non_conformity_closed</code> when the 8D is closed with the corrective action verified. The customer can subscribe to all quality events or only to those affecting their orders.</p>
</div></section>

<section class="section"><div class="container">
  <h2>Retries and failure handling</h2>
  <p>When a webhook fails — endpoint returns a status code outside the 2xx range — the system retries automatically. The retry policy is five attempts with exponential backoff: at sixty seconds, five minutes, thirty minutes, two hours and twelve hours. When the last attempt fails, the event sits in a manual review queue and both the workshop and the customer's technical leads are notified.</p>
  <p>The system never delivers an event twice without you knowing. Each event carries a unique identifier in the <code>X-Corp-MX-Event-Id</code> header. You store the identifier and, on receiving an event, verify you have not processed it before. Idempotency is maintained by identifier — the second event with the same identifier is discarded.</p>
</div></section>

<section class="section section-alt"><div class="container">
  <h2>Setup from the customer portal</h2>
  <p>Customers configure webhooks from the portal — available via SSO — with a valid HTTPS endpoint. For each event the customer selects whether to receive it. For runs with selective subscription — order events only, no quality — the customer ticks the events they require. Configuration is saved and applied instantly with no maintenance window.</p>
  <p>For customers with separated environments — sandbox and production — two independent endpoints are managed. The sandbox delivers events with fictitious data for testing. Production delivers real events from customer orders. The customer can test integration in the sandbox before moving the endpoint to production with a single change in the portal.</p>
</div></section>

<section class="section"><div class="container">
  <h2>Customer-side best practices</h2>
  <p>We recommend four practices. First, validate the HMAC SHA-256 signature before processing the body — if the signature does not match, discard. Second, validate the timestamp with five-minute tolerance — if the event is more than five minutes old, discard to prevent replay attacks. Third, maintain idempotency by event identifier — store the identifier in a table with twenty-four-hour TTL.</p>
  <p>Fourth, respond fast — the endpoint must return a 2xx code within five seconds. If internal processing takes longer, the customer returns 2xx immediately and processes the webhook asynchronously with a background job. When the endpoint returns 5xx or times out, the system treats it as failure and triggers the retry.</p>
</div></section>

<section id="contact-form" class="section section-alt"><div class="container">
  <h2>Set up your webhooks with Corp-Merch.MX</h2>
  <p>If your technical team requires webhook integration with your operations stack, let's talk. We deliver the sandbox endpoint, the signing secret and full documentation in your format.</p>
  <?php require __DIR__ . '/../../../includes/form.php'; ?>
</div></section>

<section class="section role-note"><div class="container">
  <p class="role-note-text">Webhooks are delivered to clients on an active programme with a signed usage agreement. Documentation may update without prior notice; major versions are communicated with thirty days' notice. Corp-Merch.MX is a Mexican workshop for corporate merch; we operate independently.</p>
</div></section>

<script type="application/ld+json">
{"@context":"https://schema.org","@type":"WebPage","name":"Webhooks Corp-Merch.MX","description":"HMAC SHA-256 signed webhooks with order, shipment, invoice and quality events. Retries, idempotency and best practices for enterprise integration.","publisher":{"@type":"Organization","name":"Corp-Merch.MX"}}
</script>
<?php require __DIR__ . '/../../../includes/footer.php'; ?>