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

# Setup webhooks

> Learn how to set up webhooks to receive real-time notifications about events in your account.

## Subscribe to webhooks

To receive real-time notifications about events in your account, you can set up webhooks. Webhooks allow your application to be notified when specific events occur, such as document signing or verification completion.

<Steps titleSize="h3">
  <Step title="Create a webhook endpoint">
    First, create an endpoint in your application that can receive HTTP POST requests. This endpoint will handle incoming webhook notifications.

    <Tip>
      Make sure your endpoint is publicly accessible and can handle incoming POST requests. It must also have a valid SSL certificate (HTTPS).
    </Tip>
  </Step>

  <Step title="Register your webhook endpoint">
    Next, register your webhook endpoint by providing the URL of your endpoint.

    <Note>
      You can register your webhook endpoint by calling `events/endpoints/set` or within the iinked Sign web application under `Settings > Integrations > Endpoints`.
    </Note>
  </Step>

  <Step title="Authenticate requests (optional)">
    Optionally, update your webhook endpoint to authenticate requests using a secret token. This helps ensure that incoming requests are from the iinked Sign platform.
  </Step>
</Steps>

## Create endpoint via iinked Sign

You can manually configure your webhook endpoint within the iinked Sign platform. To do this, navigate to `Settings > Integrations > Endpoints` in the web application.

From there, you can add one or more endpoints by providing the URL of your webhook endpoint.

## Create endpoint via API

To help streamline the process of setting up webhooks, you can use the `events/endpoints/set` endpoint to register your webhook URL.

<Tip>
  When called with OAuth authorization, this endpoint is useful to create, update, or delete a webhook endpoint for the users account, overwriting any existing endpoint associated to your OAuth client ID.

  The endpoint will automatically be named after your application name as defined when registering your client ID.
</Tip>

### Request

```http Request theme={null}
POST /events/endpoints/set
{
    "url": "https://example.com/your-webhook-endpoint",
}
```

### Body

<ParamField body="name" type="string">
  A friendly name for your webhook endpoint.
</ParamField>

<ParamField body="url" type="string" required>
  The publicly accessible URL of your webhook endpoint. It must use HTTPS and have a valid SSL certificate.
</ParamField>

<ParamField body="delete" type="boolean">
  Set to true to delete the specified webhook endpoint.
</ParamField>

### Response

```http Response theme={null}
200 OK
{
    "endpoint": {
        "eventEndpointId": "e7b8f9c2-3d4a-4b5c-8e9f-1a2b3c4d5e6f",
        "name": "My Webhook Endpoint",
        "url": "https://example.com/your-webhook-endpoint",
        "secret": "your-secret-token"
    }
}
```

<ParamField body="endpoint" type="object">
  Details of the registered webhook endpoint.

  <Expandable title="properties" defaultOpen>
    <ParamField body="eventEndpointId" type="string">
      Unique identifier for the webhook endpoint.
    </ParamField>

    <ParamField body="name" type="string">
      Friendly name for the webhook endpoint.
    </ParamField>

    <ParamField body="url" type="string">
      The URL of the webhook endpoint.
    </ParamField>

    <ParamField body="secret" type="string">
      The secret token used for authenticating webhook requests.
    </ParamField>
  </Expandable>
</ParamField>

## Verify webhooks

To ensure that incoming webhook requests are from iinked Sign, you can authenticate them using a secret token. When you register your webhook endpoint, a secret token is generated and included in the response.

All notifications will include a X-Syngrafii-Signature header that you can use to authenticate each callback.

```http theme={null}
X-Syngrafii-Signature: 4d3nJjEP4JJXZBGZROEQKQr4w3E=
```

You can re-compute the signature by using the endpoint secret token to compute a HMAC-SHA256 hash of the endpoint URL concatenated with the raw JSON content. The URL must match exactly including case and any trailing slashes.

<Tabs>
  <Tab title="Example in Node.js">
    ```javascript theme={null}
    app.use(express.json({
        verify: (req, res, buf) => {
            req.rawBody = buf;
        }
    }));

    function verifySignature(req) {
        const url = process.env.WEBHOOK_ENDPOINT; // iinked Sign Endpoint URL (must match exactly)
        const secret = process.env.WEBHOOK_SECRET; // iinked Sign Endpoint Secret token
        const signatureBase64 = req.headers['x-syngrafii-signature']; // iinked Sign Signature (Node.js header names are lowercased)
        const rawBody = req.rawBody; // Raw body buffer (set in the express.json verify function)

        if (!url || !secret || !signatureBase64 || !rawBody) return false;

        const signatureBuff = Buffer.from(signatureBase64, 'base64'); // Convert base64 to bytes
        const secretBuff = Buffer.from(secret, 'utf8'); // Convert secret to bytes (use utf8, do not use hex)
        const json = rawBody.toString('utf8'); // Convert rawBody buffer to string
        const dataBuff = Buffer.from(url + json, 'utf8'); // Convert url + json to bytes

        const hmac = crypto.createHmac('sha256', secretBuff);
        const dataSignature = hmac.update(dataBuff).digest();

        return crypto.timingSafeEqual(signatureBuff, dataSignature);
    }
    ```
  </Tab>

  <Tab title="Example in C#">
    ```C# theme={null}
    async Task<bool> VerifySignature()
    {
        var url = Environment.GetEnvironmentVariable("WEBHOOK_ENDPOINT"); // iinked Sign Endpoint URL (must match exactly)
        var secret = Environment.GetEnvironmentVariable("WEBHOOK_SECRET"); // iinked Sign Endpoint Secret token
        var signatureBase64 = Request.Headers.GetValues("X-Syngrafii-Signature").FirstOrDefault(); // iinked Sign Signature
        var json = await Request.Content.ReadAsStringAsync(); // Unformatted JSON content
        
        if (string.IsNullOrEmpty(url) || string.IsNullOrEmpty(secret) || string.IsNullOrEmpty(signatureBase64) || string.IsNullOrEmpty(json)) return false;

        var signatureBuff = Convert.FromBase64String(signatureBase64); // Convert base64 to bytes
        var secretBuff = Encoding.UTF8.GetBytes(secret); // Convert secret to bytes (use utf8)
        var dataBuff = Encoding.UTF8.GetBytes(url + json); // Convert url + json to bytes

        var hmac = new HMACSHA256(secretBuff);
        var hash = hmac.ComputeHash(dataBuff);
        
        return hash.SequenceEqual(signatureBuff);
    }
    ```
  </Tab>
</Tabs>

<Tip>
  If your server-side platform automatically deserializes the JSON content, you will have to add a handler to the request pipeline to perform authentication of the event notification content before deserialization occurs or as in the example above read the request content directly and manually deserialize the JSON afterwards.

  This is because the signature is computed using the raw JSON content as it was sent and any changes to the content such as whitespace formatting or property ordering will result in a different hash value.
</Tip>
