# Taiga Webhooks

## When to Use This Skill

* How do I receive Taiga webhooks?
* How do I verify the `X-TAIGA-WEBHOOK-SIGNATURE` header?
* How do I handle `userstory`, `task`, `issue`, `epic`, `milestone` or `wikipage` events?
* Why is my Taiga webhook signature verification failing?
* Why are no webhooks arriving from my self-hosted Taiga? (`WEBHOOKS_ENABLED`)

## Verification (core)

Taiga signs the **raw request body** with HMAC-SHA1, keyed with the webhook's secret key (a string, used as UTF-8), and sends the bare hex digest in `X-TAIGA-WEBHOOK-SIGNATURE`. There is no timestamp and no `sha1=` prefix on this header. (taiga-back also sends `X-Hub-Signature: sha1=<same hex>`.)

Node:

```javascript
const crypto = require('crypto');

function verify(rawBody, signature, secret) {
  if (!signature || !secret) return false;
  const expected = crypto.createHmac('sha1', secret).update(rawBody).digest('hex');
  try {
    return crypto.timingSafeEqual(Buffer.from(signature.toLowerCase()), Buffer.from(expected));
  } catch {
    return false; // different lengths
  }
}

```

Python:

```python
import hmac, hashlib

def verify(raw_body: bytes, signature: str | None, secret: str | None) -> bool:
    if not signature or not secret:
        return False
    expected = hmac.new(secret.encode("utf-8"), raw_body, hashlib.sha1).hexdigest()
    return hmac.compare_digest(signature.lower().encode(), expected.encode())

```

Read the body as raw bytes (`express.raw({ type: '*/*' })` in Express) so verification works whatever `Content-Type` arrives.

> **For complete handlers with route wiring, event dispatch, and tests**, see:
> 
> * [examples/express/](https://github.com/hookdeck/webhook-skills/tree/main/skills/taiga-webhooks/examples/express/)
> * [examples/nextjs/](https://github.com/hookdeck/webhook-skills/tree/main/skills/taiga-webhooks/examples/nextjs/)
> * [examples/fastapi/](https://github.com/hookdeck/webhook-skills/tree/main/skills/taiga-webhooks/examples/fastapi/)

## Event Types

Every payload is `{action, type, by, date, data, change}`. There is no event header: dispatch on `type` and `action` in the body.

| `action` | Meaning |
| --- | --- |
| `create` | Object created |
| `change` | Object changed (`change` field describes the edit) |
| `delete` | Object deleted |
| `test` | Sent by the "test" button (`type` is `test`) |

| `type` | Object |
| --- | --- |
| `userstory` | User story |
| `task` | Task |
| `issue` | Issue |
| `epic` | Epic (sent by taiga-back; not listed on the docs page) |
| `milestone` | Sprint |
| `wikipage` | Wiki page |
| `test` | Test payload |

> **For payload examples**, see [Taiga webhooks](https://docs.taiga.io/webhooks.html)

## Important Headers

| Header | Description |
| --- | --- |
| `X-TAIGA-WEBHOOK-SIGNATURE` | Bare hex HMAC-SHA1 of the raw body |
| `X-Hub-Signature` | `sha1=<hex>`, same digest (sent by taiga-back) |
| `Content-Type` | `application/json` |

## Environment Variables

```bash
TAIGA_WEBHOOK_SECRET=your_webhook_secret_key   # "Secret key" field of the webhook in Taiga

```

## Local Development

```bash
# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 taiga --path /webhooks/taiga

```

## Reference Materials

* [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/taiga-webhooks/references/overview.md) - Taiga webhook concepts and events
* [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/taiga-webhooks/references/setup.md) - Enabling and configuring webhooks
* [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/taiga-webhooks/references/verification.md) - Signature verification details