> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://developers.upwardli.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://developers.upwardli.com/_mcp/server.

# Webhook Security

## Authentication

Upward's webhooks are secured using a Hashed Message Authentication Code (HMAC) in the webhook message header.
The name of this header value is `Upwardli-Signature`. The Upwardli-Signature header contains two comma-separated
key-value pairs encoding information about the request. The first key-value pair will be in the form `t=<unix_timestamp>`
and represents the unix time that the request was sent. The second key-value pair will be in the form `v1=WeNeedSomethingHere`, where the signature
is a sha256 hash computed from the consumers webhook secret and a dot-separated string composed of the unix timestamp
joined with the request body.

Note: computing the signature is sensitive to the exact characters input into the algortihm. The request data should be a json string with no whitespace formatting.

### Sample Data

```
client_id => 'public'
```

```
signature => 't=2023-10-12T20:44:58.082694+00:00,v1=263a5f79d899f7d5e04eb9a902b173d5901a9088966b932edca7174aec3d9e12'
```

```
message => '{"id":"954935cb-be33-47a4-99af-ec8bbc662ec7","createdAt":"2023-10-05T17:39:21.097794+00:00","eventName":"consumer_created","partnerId":"cb739356-5f69-429c-8157-756876d08d27","resources":["api/v2/consumers/00000000-0000-0000-0000-000000000000"],"lastAttemptedAt":"2023-10-05T17:39:21.097794+00:00"}'
```

### Example decode in Python

```python
t, v1 = [value.split('=')[1] for value in request.headers['Upwardli-Signature'].split(',')]
computed_digest = hmac.new(<YOUR_CLIENT_ID>.encode(), (t + '.' + request.data.decode('utf-8')).encode(), 'sha256').hexdigest()

if hmac.compare_digest(v1, computed_digest):
    # Process webhook
```

### Example decode in Node.JS

```nodejs
const header = "<Upwardli-Signature>"

const bodyRaw = `{
  "id": "e1bc276c-1705-431b-98ad-0c63d4502c35",
  "created_at": "2025-10-29T15:25:33.980841+00:00",
  "event_name": "consumer.created",
  "partner_id": "cb739356-5f69-429c-8157-756876d08d27",
  "resources": [
    "https://api-sandbox.upwardli.com/v2/consumer/00000000-0000-0000-0000-000000000000"
  ],
  "last_attempted_at": "2025-10-29T15:25:33.980841+00:00"
}`
const body = JSON.stringify(JSON.parse(bodyRaw))

const CLIENT_ID = "<YOUR_CLIENT_ID>"

const [t, v1] = header.split(",").map((p) => p.split("=")[1])

const computed = crypto
    .createHmac("sha256", CLIENT_ID)
    .update(`${t}.${body}`)
    .digest("hex")

const v1Buffer = Buffer.from(v1, "hex")
const computedBuffer = Buffer.from(computed, "hex")

if (crypto.timingSafeEqual(v1Buffer, computedBuffer)) {
    console.log("Valid signature")
} else {
    console.log("Invalid signature")
}
```