> ## Documentation Index
> Fetch the complete documentation index at: https://docs.custral.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Quickstart

> Register an endpoint, verify a signature, and receive your first event.

Fifteen minutes, end to end. You need a URL Custral can reach over the public
internet and workspace **admin** access.

<Steps>
  <Step title="Stand up an endpoint">
    Anything that answers `POST` with a `2xx` will do. Using the
    [TypeScript SDK](/dev/sdks/typescript), the receiver is three lines:

    ```ts theme={null}
    import express from "express";
    import {Custral} from "@custral/sdk";

    const custral = new Custral({webhookSecret: process.env.CUSTRAL_WEBHOOK_SECRET!});
    const app = express();

    custral.on("record.created", async (event) => {
      console.log("New record", event.data.recordId);
    });

    // The raw body parser is required: verification needs the exact bytes.
    app.post("/hooks/custral", express.raw({type: "application/json"}), custral.webhooks.express());

    app.listen(3000);
    ```

    <Warning>
      Mount `express.raw({type: "application/json"})` on the webhook route. A
      JSON-parsed body cannot be verified, because re-serializing it does not
      reproduce the bytes that were signed.
    </Warning>
  </Step>

  <Step title="Expose it">
    In development, a tunnel is the quickest route:

    ```bash theme={null}
    ngrok http 3000
    ```

    Custral refuses `localhost`, private and link-local IP addresses, and any
    scheme other than `http` or `https`. See
    [What Custral will and will not call](/dev/webhooks/security#what-custral-will-and-will-not-call).
  </Step>

  <Step title="Register the endpoint">
    Open **Settings → Developers → Webhooks**, choose **Add webhook**, paste
    the URL, and tick the events you want.

    You can also ask the assistant, or any MCP client holding a key with the
    `webhooks:manage` scope:

    > Register a webhook at `https://abc123.ngrok.io/hooks/custral` for
    > `record.created` and `record.updated`.

    See [Managing subscriptions](/dev/webhooks/managing) for both paths in full.
  </Step>

  <Step title="Copy the signing secret">
    Creating the endpoint returns a `whsec_…` secret **once**. Put it in your
    environment as `CUSTRAL_WEBHOOK_SECRET` before you close the dialog.

    <Note>
      Lost it? Nothing can retrieve it. Rotate the secret to get a new one, and
      update your environment at the same time.
    </Note>
  </Step>

  <Step title="Fire a real event">
    Create a record on any object in the workspace. Within a second or two your
    handler should log its id.

    Nothing arrived? Open the endpoint in settings and read its delivery log:
    every attempt is recorded with the HTTP status your server returned. Start
    at [Troubleshooting](/dev/webhooks/troubleshooting).
  </Step>
</Steps>

## Verifying without the SDK

The signature is a Stripe-style HMAC, so it is a handful of lines in any
language. The full algorithm and the reasoning behind each step is on
[Verifying signatures](/dev/webhooks/security).

```ts theme={null}
import crypto from "node:crypto";

function verify(rawBody: string, header: string, secret: string): boolean {
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  const timestamp = Number(parts.t);
  if (!timestamp || Math.abs(Date.now() / 1000 - timestamp) > 300) return false;

  const expected = crypto.createHmac("sha256", secret).update(`${timestamp}.${rawBody}`).digest("hex");
  const a = Buffer.from(expected);
  const b = Buffer.from(parts.v1 ?? "");
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}
```

## What's next

<CardGroup cols={2}>
  <Card title="Event catalog" icon="list" href="/dev/webhooks/events/overview">
    The exact payload of every event.
  </Card>

  <Card title="Delivery and retries" icon="arrows-rotate" href="/dev/webhooks/delivery">
    Timeouts, retries, and why your handler must be idempotent.
  </Card>
</CardGroup>


## Related topics

- [TypeScript SDK](/dev/sdks/typescript.md)
- [Applications & API keys](/dev/applications.md)
- [Where Custral runs](/start/apps/overview.md)
- [Learn the Product](/start/onboarding/learn.md)
- [Introduction](/start/introduction.md)
