# Travtus Webhooks

Travtus ([travtus.com](https://travtus.com/)) delivers webhooks through Svix, signed following
Standard Webhooks with the `svix-*` header names. Each
delivery is an HTTP `POST` with a signed JSON body and three headers: `svix-id`,
`svix-timestamp`, and `svix-signature`. The body is an envelope:
`{ "event_type": "task.update", "message_id": "51d155b3-...", "body": { ... } }`. `message_id`
is the Svix message id and `body` is the lease, the task (with a `creator` object) or the AI
reply. No header carries the event type.

## When to Use This Skill

* How do I receive Travtus webhooks?
* How do I verify Travtus webhook signatures (`svix-signature`, `whsec_` secret)?
* How do I handle `lease.update`, `task.create`, `task.update` or `task.delete` events?
* How do I handle `sms.respond`, `email.respond` or `web_chat.respond` AI replies from the Messaging API?
* Why is my Travtus webhook signature verification failing, or rejecting Hookdeck retries?

## Verification (core)

Signed content is `{svix-id}.{svix-timestamp}.{raw body}`. Svix signs it with HMAC-SHA256 using
the base64-decoded secret (the part after `whsec_`), base64-encodes the result, and sends one or
more space-separated `v1,<signature>` entries. Use the official `svix` library, which compares in
constant time. Read the `svix-*` headers, and when `svix-id` is absent fall back to the `webhook-*`
headers (`webhook-id`, `webhook-timestamp`, `webhook-signature`), as Hookdeck does.

```javascript
import { Webhook } from 'svix'; // svix 2.x is ESM-only

const prefix = req.headers['svix-id'] ? 'svix' : 'webhook';
const id = req.headers[`${prefix}-id`];
const timestamp = req.headers[`${prefix}-timestamp`];
const signature = req.headers[`${prefix}-signature`];
// rawBody: the raw Buffer/string, never re-serialized JSON
new Webhook(process.env.TRAVTUS_WEBHOOK_SECRET).verify(rawBody, {
  'svix-id': id, 'svix-timestamp': timestamp, 'svix-signature': signature,
  'webhook-id': id, 'webhook-timestamp': timestamp, 'webhook-signature': signature,
}); // throws on a bad signature or a timestamp outside the window
const event = JSON.parse(rawBody.toString('utf8')); // parse only after verify passes

```

```python
from svix.webhooks import Webhook, WebhookVerificationError

# svix-* headers, falling back to webhook-* as Hookdeck does
prefix = "svix" if headers.get("svix-id") else "webhook"
id, ts, sig = (headers.get(f"{prefix}-{n}") for n in ("id", "timestamp", "signature"))

wh = Webhook(os.environ["TRAVTUS_WEBHOOK_SECRET"])  # "whsec_..."
wh.verify(raw_body, {"svix-id": id, "svix-timestamp": ts, "svix-signature": sig,
                     "webhook-id": id, "webhook-timestamp": ts, "webhook-signature": sig})

```

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

Travtus support sets the webhook up (email support@travtus.com with your URL) and issues a
`whsec_...` secret, unique per endpoint. Hookdeck's configuration alert for this source reads:
"Travtus sets up webhooks through support@travtus.com and sends you the whsec_ secret. Paste it here."
Keep the full value in `TRAVTUS_WEBHOOK_SECRET`. Handlers fail closed: with the variable unset
(or not a `whsec_` value) they return 500 instead of accepting anything. Missing headers or a bad
signature return 400. The `svix` library reads either the `svix-*` or `webhook-*` header names, so
no mapping is needed beyond the fallback shown.

### Hookdeck retries and replays hit the 5-minute window

The `svix` and `standardwebhooks` libraries always enforce a fixed 5-minute timestamp window, and
it can't be configured. Hookdeck retries and replays resend the **original** `svix-timestamp`, so
the library rejects any retry or replay that happens more than 5 minutes after the original
delivery. If you verify behind Hookdeck (or replay old events), use the manual verifier in
[references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/travtus-webhooks/references/verification.md), where the window can be dropped or
widened. Deduplicate on `svix-id` instead of relying on the timestamp.

## Common Event Types

7 events are documented (the list may not be exhaustive). The name is `event_type` in the JSON
body, and the examples handle all of them:

| Event | Triggered When |
| --- | --- |
| `lease.update` | A lease is created or updated |
| `task.create` | A task is created (`body.creator` describes the originator) |
| `task.update` | A task is updated |
| `task.delete` | A task is deleted |
| `sms.respond` | AI response to an SMS message sent through the Messaging API |
| `email.respond` | AI response to an email message |
| `web_chat.respond` | AI response to a web chat message |

See [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/travtus-webhooks/references/overview.md) for payload fields and delivery details
(2xx within 15 s; retries immediately, 5 s, 5 min, 30 min, 2 h, 5 h, 10 h, 10 h).

## Environment Variables

```bash
# Signing secret of the webhook endpoint (Svix format, starts with whsec_)
TRAVTUS_WEBHOOK_SECRET=whsec_xxxxx

```

## Local Development

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

```

Give the tunnel URL to support@travtus.com (or add it in the webhook portal), then use the
portal's **Testing** tab to send an example event.

## Reference Materials

* [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/travtus-webhooks/references/overview.md) - Travtus webhook concepts, events, payload shapes, delivery
* [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/travtus-webhooks/references/setup.md) - Request setup from support, get the secret, portal, test events
* [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/travtus-webhooks/references/verification.md) - Svix verification details, header fallback, manual verifier, timestamp window