# Smartsheet Webhooks

## When to Use This Skill

* How do I receive Smartsheet webhooks?
* How do I answer the `Smartsheet-Hook-Challenge` verification request?
* How do I verify the `Smartsheet-Hmac-SHA256` signature?
* How do I handle `row.created`, `cell.updated`, or `sheet.updated` events?
* Why was my Smartsheet webhook disabled (`DISABLED_VERIFICATION_FAILED`)?

## Verification (core)

Smartsheet webhooks have two checks. Do the **challenge first**: Smartsheet sends a request with a `Smartsheet-Hook-Challenge` header when you enable the webhook and again about once every 100 callbacks. Answer it with a 200 that echoes the value in the `Smartsheet-Hook-Response` response header (or a JSON body `{"smartsheetHookResponse": "<value>"}`). Challenge requests may be unsigned, so do this before any signature check. If you fail it, Smartsheet disables the webhook (`DISABLED_VERIFICATION_FAILED`).

Callbacks carry `Smartsheet-Hmac-SHA256`: the **hex** HMAC-SHA256 of the **raw body**, keyed with the webhook's `sharedSecret` (assigned by Smartsheet on creation). Compare case-insensitively.

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

// 1. Handshake first (may be unsigned)
const challenge = req.get('Smartsheet-Hook-Challenge');
if (challenge) {
  res.set('Smartsheet-Hook-Response', challenge);
  return res.status(200).json({ smartsheetHookResponse: challenge });
}

// 2. Signature over the raw body (Buffer from express.raw({ type: '*/*' }))
function verify(rawBody, header, sharedSecret) {
  if (!header) return false;
  const expected = crypto.createHmac('sha256', sharedSecret).update(rawBody).digest('hex');
  const a = Buffer.from(header.trim().toLowerCase());
  const b = Buffer.from(expected);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

```

Python:

```python
import hmac, hashlib

def verify(raw_body: bytes, header: str, shared_secret: str) -> bool:
    expected = hmac.new(shared_secret.encode(), raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest((header or "").strip().lower().encode(), expected.encode())

```

Smartsheet's SDKs have no webhook verification helper, so verification is manual in every example. There is no timestamp in the signed content.

> **For complete handlers with tests**, see [examples/express/](https://github.com/hookdeck/webhook-skills/tree/main/skills/smartsheet-webhooks/examples/express/), [examples/nextjs/](https://github.com/hookdeck/webhook-skills/tree/main/skills/smartsheet-webhooks/examples/nextjs/), [examples/fastapi/](https://github.com/hookdeck/webhook-skills/tree/main/skills/smartsheet-webhooks/examples/fastapi/).

## Common Event Types

Each callback has `scopeObjectId` and an `events` array; every event has an `objectType` and an `eventType`. Callbacks are "skinny": fetch the object through the API for details.

| Event (`objectType.eventType`) | Triggered When |
| --- | --- |
| `row.created` | A row is added to the sheet |
| `row.updated` | A row is updated |
| `row.deleted` | A row is deleted |
| `cell.updated` | A cell value changes |
| `sheet.updated` | The sheet is updated |

Other sheet `objectType` values: `attachment`, `column`, `comment`, `discussion`. A webhook can filter with patterns such as `["row.created", "cell.updated"]` or `row.*`; the default is `*.*`. A **status change callback** (`newWebhookStatus`) is sent when a webhook is disabled or re-enabled because its scope became inaccessible or accessible.

See [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/smartsheet-webhooks/references/overview.md) for the payload shape.

## Environment Variables

```bash
# sharedSecret returned when you create the webhook (POST /webhooks)
SMARTSHEET_SHARED_SECRET=your_webhook_shared_secret

```

## Local Development

```bash
# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 smartsheet --path /webhooks/smartsheet

```

## Reference Materials

* [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/smartsheet-webhooks/references/overview.md) - Webhook concepts, events, payload shape, retries
* [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/smartsheet-webhooks/references/setup.md) - Create and enable a webhook, get the shared secret
* [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/smartsheet-webhooks/references/verification.md) - Challenge handshake, signature details and gotchas