---
title: Quickstart
description: Install @sigil/core, sign a webhook request, and verify it on the receiving end.
url: https://pr-1-10d18e06d3bf.thally.app/quickstart
---

# Quickstart

Install @sigil/core, sign a webhook request, and verify it on the receiving end.

## Before you begin

- **Node.js 20 or later.** `@sigil/core` is an ES module and requires
  Node.js 20+.
- A webhook secret shared between the sender and the receiver. In production,
  store it in an environment variable such as `WEBHOOK_SECRET`.

#### Install the package

#### npm

        ```bash
        npm install @sigil/core
        ```

#### pnpm

        ```bash
        pnpm add @sigil/core
        ```

#### yarn

        ```bash
        yarn add @sigil/core
        ```

#### Sign a request

    Import `sign` and pass the exact bytes you will send, plus the shared
    secret. The returned string is the value of the `Sigil-Signature` header.

    ```ts
    import { sign } from '@sigil/core';

    const body = JSON.stringify({ id: 'evt_1', type: 'invoice.paid' });
    const header = sign(body, process.env.WEBHOOK_SECRET!);
    // "t=1767225600,v1=6b1f…"

    await fetch(url, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Sigil-Signature': header,
      },
      body,
    });
    ```

    Sign the exact bytes you send. Never re-serialise the body between signing
    and sending — two JSON encoders can disagree about key order and
    whitespace, and the receiver cannot reproduce a digest over bytes it never
    saw.

#### Verify a request

    On the receiving end, read the raw request body and pass it to `verify`
    along with the signature header and the shared secret.

    ```ts
    import { verify, SigilError } from '@sigil/core';

    try {
      const timestamp = verify(
        rawBody,
        req.headers['sigil-signature'],
        process.env.WEBHOOK_SECRET!,
      );
      // timestamp is the Unix seconds the sender stamped the signature with
    } catch (error) {
      if (error instanceof SigilError) {
        return res.status(400).json({ error: error.code });
      }
      throw error;
    }
    ```

    `verify` returns the header's timestamp on success and throws `SigilError`
    on any rejection. Read the raw request body — a framework that has already
    parsed and re-encoded JSON has destroyed the bytes the signature covers.

#### Verify the result

    A successful `verify` call returns a number (the Unix timestamp from the
    header). A rejected request throws a `SigilError` whose `code` property
    identifies the failure — see [Error codes](/error-codes) for every
    possible value.

## Next steps

- Use [`constructEvent`](/verification#verify-and-decode-together) to verify
  and JSON-decode in one call.
- Learn the [signature format](/signature-format) to understand what the
  header carries and why.
- Set up [key rotation](/key-rotation) so you can change secrets without
  dropping events.