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

# Authentication

When Schedo sends a webhook, it includes a signature in the `x-schedo-signature` header. This signature allows you to verify that the request really came from Schedo and not from a malicious third party.

## Why Verify the Signature?

Anyone can send a POST request to your webhook URL. By verifying the signature, you ensure that only requests genuinely sent by Schedo (using your secret) are accepted and processed.

## How the Signature is Generated

1. Schedo takes the raw request body (the JSON payload).
2. Schedo computes an HMAC-SHA256 hash of this payload, using your unique signing secret as the key.
3. Schedo sends the resulting hash (as a hex string) in the `x-schedo-signature` header.

## How to Verify the Signature

To verify the request:

1. Read the raw request body (exactly as received).
2. Compute your own HMAC-SHA256 hash of the body, using your signing secret.
3. Compare your computed hash to the value in the `x-schedo-signature` header.
4. If they match, the request is authentic.

## Example: Manual Signature Verification in TypeScript/Node.js

```ts theme={null}
import { createHmac, timingSafeEqual } from "crypto";
import { NextRequest, NextResponse } from "next/server";

// Your secret from Schedo settings (keep this safe!)
const SCHEDO_SIGNING_SECRET = process.env.SCHEDO_SIGNING_SECRET!;

function computeSignature(payload: Buffer | string, secret: string): string {
  return createHmac("sha256", secret).update(payload).digest("hex");
}

function isValidSignature(payload: Buffer | string, signature: string, secret: string): boolean {
  const expected = computeSignature(payload, secret);
  // Use timingSafeEqual to prevent timing attacks
  try {
    return timingSafeEqual(Buffer.from(signature, "hex"), Buffer.from(expected, "hex"));
  } catch {
    return false;
  }
}

export async function POST(req: NextRequest) {
  // Read the raw body as a Buffer
  const body = await req.text();
  const signature = req.headers.get("x-schedo-signature") ?? "";

  if (!isValidSignature(body, signature, SCHEDO_SIGNING_SECRET)) {
    return NextResponse.json({ error: "Invalid signature" }, { status: 400 });
  }

  // If you get here, the request is authentic!
  const data = JSON.parse(body);
  // ... handle your logic

  return NextResponse.json({ success: true });
}
```

### Key Points

* Always use the raw body (not a parsed object) for signature calculation.
* Use `timingSafeEqual` for comparison to prevent timing attacks.
* Never share your signing secret.

***

By following these steps, you can ensure that only trusted requests from Schedo are processed by your webhook endpoint.
