> For the complete documentation index, see [llms.txt](https://docs-sdk.crypto-chief.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs-sdk.crypto-chief.com/processing/dotnet/guides/webhooks.md).

# Webhooks

Verify and handle Crypto Chief webhooks in ASP.NET Core with typed event payloads.

Outbound webhooks are signed with the **same algorithm** as outgoing requests, so the same API key verifies them.

## ASP.NET Core minimal API

Read the body, verify, decode in one call:

```csharp
using CryptoChief.Processing.Webhooks;
using CryptoChief.Processing.Webhooks.Events;

var apiKey = builder.Configuration["CryptoChief:ApiKey"]!;

app.MapPost("/webhooks/payout", async (HttpRequest req) =>
{
    using var ms = new MemoryStream();
    await req.Body.CopyToAsync(ms);
    var body = ms.ToArray();
    var sig  = req.Headers[WebhookVerifier.SignatureHeader].ToString();

    try
    {
        var evt = WebhookVerifier.VerifyAndDecode<PayoutWebhookEvent>(apiKey, body, sig);
        // process evt — evt.Uuid, evt.OrderId, evt.Status, evt.AmountToReceive, ...
        return Results.Ok();
    }
    catch
    {
        return Results.Unauthorized();
    }
});
```

## Manual verification

For a custom HTTP stack or controller-based API:

```csharp
if (!WebhookVerifier.TryVerify(apiKey, body, signatureHeader))
    return Unauthorized();
```

Or the throwing form:

```csharp
WebhookVerifier.Verify(apiKey, body, signatureHeader); // throws on mismatch
```

## Event types

Typed payloads live in `CryptoChief.Processing.Webhooks.Events`:

* `PayoutWebhookEvent` — `payout.*`
* `TransactionWebhookEvent` — `transaction.*`
* `PayInWebhookEvent` — `invoice.*`
* `StaticDepositWebhookEvent` — `static_deposit.*`
* `SweepWebhookEvent` — `sweep.*`

Payout and transaction webhooks fire **only on terminal status**.

{% hint style="warning" %}
**The two deposit-side events spell "nothing here" differently.** On `StaticDepositWebhookEvent` a native-coin transfer carries `Contract` as an **empty string**, not `null`; `ConfirmedAt` and `PaidAt` are `null` until the deposit is confirmed and paid, `BlockNumber` arrives only after block inclusion, and `AmountFiat` can be empty when no rate was available. On `SweepWebhookEvent` the platform leaves an unused key out of the payload instead, so a native sweep's `AssetContract` and an unfunded sweep's `GasPumpTxHash` decode as `null`. `string.IsNullOrEmpty` reads both correctly; `is null` alone does not.
{% endhint %}

`sweep.confirmed` is the moment swept funds are confirmed in your master wallet — the other half of a deposit's life, and what treasury reporting should key off. See [Sweep callbacks](/processing/dotnet/guides/sweep-callbacks.md).

{% hint style="info" %}
`WebhookVerifier.SenderIps` lists the addresses webhooks are delivered from — whitelist them at your edge for defence in depth.
{% endhint %}
