# Guide to Nuvemshop Webhooks: Features and Best Practices

Nuvemshop (Tiendanube) webhooks notify your app when store activity happens: an order is created or paid, a product updates, an app is uninstalled. If you're building an app on Nuvemshop, webhooks are how you react in real time without polling the REST API.

This guide covers how Nuvemshop webhooks work, the events you'll handle, how to verify the `x-linkedstore-hmac-sha256` signature, the thin payloads, the retry behavior, and the best practices for production.

## What are Nuvemshop webhooks?

Nuvemshop webhooks are HTTP POSTs delivered to a URL you register per app, each carrying a thin payload about a store event. Nuvemshop signs every delivery with an `x-linkedstore-hmac-sha256` header: a hex-encoded HMAC-SHA256 of the raw body, keyed with your app's client secret (the OAuth app secret).

The payload is thin (it names the changed resource, not its data), so you fetch the full record from the REST API.

## Nuvemshop webhook features

| Feature | Details |
| --- | --- |
| Configuration | `POST /webhooks` with `{ event, url }` (per app; HTTPS required) |
| Signature header | `x-linkedstore-hmac-sha256` |
| Signature scheme | hex HMAC-SHA256 of the raw body, keyed with the app client secret |
| Payload | Thin: `store_id`, `event`, and a resource `id` |
| Event format | `resource/action` (e.g. `order/created`) |
| Acknowledgement | 2xx within 3 seconds |
| Retries | Immediately on timeout, then ~5/10/15 min, then exponential (x1.4), up to 18 attempts over 48h |
| SDK | None (community only) |

## Common events

Nuvemshop event names use `resource/action`:

| Event | Fires when |
| --- | --- |
| `order/created` | A new order is created |
| `order/paid` | An order is paid |
| `order/cancelled` | An order is cancelled |
| `product/updated` | A product updates |
| `app/uninstalled` | Your app is uninstalled from a store |

Note it's `app/uninstalled` (not `app/removed`). Subscribe to the events you process (one subscription per event), and consult Nuvemshop's webhook reference for the full resource/action list.

## Setting up Nuvemshop webhooks

Create webhooks per app via `POST /webhooks` with `{ event, url }` (the URL must be HTTPS). Each subscription is for a single event. Your app's client secret is the HMAC key.

## Securing Nuvemshop webhooks

Each delivery carries an `x-linkedstore-hmac-sha256` header: a hex-encoded HMAC-SHA256 of the raw request body, keyed with your app's client secret. Compute over the exact raw bytes before JSON parsing and compare in constant time.

```javascript
const crypto = require("crypto");

const CLIENT_SECRET = process.env.NUVEMSHOP_CLIENT_SECRET;

function verify(rawBody, signature) {
  const expected = crypto
    .createHmac("sha256", CLIENT_SECRET)
    .update(rawBody)
    .digest("hex");
  const a = Buffer.from(expected);
  const b = Buffer.from(signature || "");
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {
  if (!verify(req.body, req.headers["x-linkedstore-hmac-sha256"])) {
    return res.sendStatus(401);
  }

  res.sendStatus(200); // acknowledge within 3s
  const { store_id, event, id } = JSON.parse(req.body);
  processQueue.add({ store_id, event, id }); // fetch via REST API, async
});

```

The same check in Python:

```python
import hashlib
import hmac
import os

CLIENT_SECRET = os.environ["NUVEMSHOP_CLIENT_SECRET"].encode()

def verify(raw_body: bytes, signature: str) -> bool:
    expected = hmac.new(CLIENT_SECRET, raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature or "")

```

## Nuvemshop webhook limitations and pain points

### Thin payloads require an API fetch

The Problem: The payload carries only `store_id`, `event`, and a resource `id`, so you fetch the full record from the REST API (scoped by `store_id`) for anything useful.

Why It Happens: Nuvemshop keeps payloads minimal.

Workarounds:

* Acknowledge fast, then fetch the resource by `id` (scoped to `store_id`) via the REST API off a queue.

How Hookdeck Can Help: Hookdeck durably queues each event so your worker fetches records at a controlled rate, per store.

### The 3-second acknowledgement window

The Problem: You must return 2xx within 3 seconds. Any synchronous work (including the API fetch) risks the window and triggers the retry ladder.

Why It Happens: Nuvemshop caps the ack time.

Workarounds:

* Verify, enqueue, and return 2xx immediately; do the API fetch asynchronously.

How Hookdeck Can Help: Hookdeck acknowledges Nuvemshop within the window and durably queues events, decoupling the 3-second budget from your processing time.

### A long retry ladder means duplicates

The Problem: Failed deliveries retry immediately, then ~5/10/15 minutes, then exponentially (x1.4) up to 18 attempts over 48 hours, so the same event can arrive many times.

Why It Happens: At-least-once delivery favors eventual delivery.

Workarounds:

* Dedupe on `store_id` + resource `id` + `event`, and make side effects idempotent.

How Hookdeck Can Help: Hookdeck deduplicates deliveries at the edge, so the 48-hour retry window doesn't cause double-processing. See our [guide to webhook idempotency](/webhooks/guides/implement-webhook-idempotency).

### Handle `app/uninstalled`

The Problem: When a store uninstalls your app, you get `app/uninstalled` (not `app/removed`). Missing it leaves you holding stale tokens and calling the API for a store you no longer serve.

Why It Happens: Uninstallation is delivered as a regular event.

Workarounds:

* Handle `app/uninstalled` explicitly and revoke that store's tokens/data.

How Hookdeck Can Help: Hookdeck's filters can route `app/uninstalled` to a dedicated cleanup handler.

## Best practices

### Verify with the client secret over the raw body

Compute hex HMAC-SHA256 over the raw body with your app client secret and compare against `x-linkedstore-hmac-sha256` in constant time.

### Acknowledge within 3 seconds, fetch asynchronously

Return 2xx immediately and fetch the resource by `id` off a queue. See [why to process webhooks asynchronously](/webhooks/guides/why-implement-asynchronous-processing-webhooks).

### Dedupe across the retry ladder

Dedupe on `store_id` + resource `id` + `event` so the 48-hour retries don't double-process.

### Handle uninstalls

Process `app/uninstalled` to clean up store tokens and data.

## Conclusion

Nuvemshop (Tiendanube) webhooks deliver thin `resource/action` events verified with an `x-linkedstore-hmac-sha256` HMAC over the raw body, keyed with your app client secret. Fetch full records via the REST API by `id`, acknowledge within 3 seconds, dedupe across the 48-hour retry ladder, and handle `app/uninstalled`.

[Hookdeck](https://hookdeck.com) verifies the signature, deduplicates, and durably queues every event at the edge, so your app only ever processes verified, unique store events.

[Get started with Hookdeck](https://dashboard.hookdeck.com/signup) for free and handle Nuvemshop webhooks reliably in minutes.