# Tutorial: Subscribing to Shipment Webhooks

# Tutorial: Subscribing to Shipment Webhooks

Cosmo Cargo sends an HTTP POST request to your server when something happens to a shipment: `shipment.created`, `shipment.delivered` and the other events you register for with `POST /webhooks`.

> **Warning:**
> Always verify the HMAC SHA-256 signature on incoming webhooks to ensure payloads originate from Cosmo Cargo.

---

## Verifying Webhook Signatures

Each webhook request includes a header named `CosmoCargo-Signature`:

```
CosmoCargo-Signature: t=1697621400,v1=92f98e826b14a273b027cf8f4cbb23...
```

Verify the signature using your webhook signing secret:

<Tabs>
  <TabItem label="Node.js">
```javascript
import crypto from "node:crypto";

export function verifyCosmoCargoWebhook(rawBody, signatureHeader, secret) {
  const parts = Object.fromEntries(
    signatureHeader.split(",").map(p => p.split("="))
  );
  const timestamp = parts.t;
  const signature = parts.v1;

  const signedPayload = `${timestamp}.${rawBody}`;
  const expectedSignature = crypto
    .createHmac("sha256", secret)
    .update(signedPayload)
    .digest("hex");

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  );
}
```
  </TabItem>
  <TabItem label="Python">
```python
import hmac
import hashlib

def verify_cosmo_cargo_webhook(raw_body: str, signature_header: str, secret: str) -> bool:
    parts = dict(item.split("=") for item in signature_header.split(","))
    timestamp = parts["t"]
    signature = parts["v1"]
    
    signed_payload = f"{timestamp}.{raw_body}".encode("utf-8")
    expected_sig = hmac.new(secret.encode("utf-8"), signed_payload, hashlib.sha256).hexdigest()
    
    return hmac.compare_digest(signature, expected_sig)
```
  </TabItem>
</Tabs>
