# Coterie Webhooks

Coterie (small business commercial insurance API) sends real-time webhooks to a partner's
server for three kinds of object: **applications**, **quotes** and **policies**. Payloads are
not signed. The only authentication is an opt-in static header named `Authentication`.

## When to Use This Skill

* How do I receive Coterie webhooks?
* How do I verify the Coterie `Authentication: token <guid>` header?
* Does Coterie sign webhook payloads? (No: shared token header only)
* Why does my Coterie webhook endpoint return 401?
* How do I tell Coterie application, quote and policy webhooks apart?

## Verification (core)

Ask your Coterie contact to enable the `Authentication` header (all optional headers are
turned on by request only). Coterie then sends `Authentication: token {guid}`. The header is
`Authentication`, **not** `Authorization`, and the key is the **whole value including the
`token ` prefix**:

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

// Hash both sides: equal-length buffers, so timingSafeEqual never throws.
function safeEqual(a, b) {
  const ha = crypto.createHash('sha256').update(String(a)).digest();
  const hb = crypto.createHash('sha256').update(String(b)).digest();
  return crypto.timingSafeEqual(ha, hb);
}

function verifyCoterieRequest(headers, expected = process.env.COTERIE_AUTHENTICATION) {
  if (!expected) return false;                 // never accept when unconfigured
  const received = headers['authentication'];  // full value, e.g. "token 1234-..."
  return Boolean(received) && safeEqual(received, expected);
}

```

Return `401` when this fails, `400` for a body that isn't a JSON object, and `200` once
handled. Read the body raw (`express.raw({ type: '*/*' })`) so any content type works.
Python and Next.js equivalents are in [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/coterie-webhooks/references/verification.md).

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

## Webhook Types

Coterie's docs name three webhook actions but no event-type field. Tell them apart by the
identifier key in the payload (the examples do this):

| Kind | Identifier key | Documented status example |
| --- | --- | --- |
| `application` | `applicationId` (no `quoteId`) | `Status: "ReadyToQuote"` |
| `quote` | `quoteId` | `PolicyType`, `Premium`, `LineItems` |
| `policy` | `PolicyId` | `Status: "Pending"`, `PolicyNumber` |

Quote payloads also carry `applicationId`, so check `quoteId` first.

## Optional Headers

Also enabled on request: `x-timeStamp` (epoch milliseconds of the event), `x-applicationId`
and `x-leadId` (your own id, passed in the application `Metadata`).

## Environment Variables

```bash
COTERIE_AUTHENTICATION=token 00000000-0000-0000-0000-000000000000   # full header value

```

## Local Development

```bash
npx hookdeck-cli listen 3000 coterie --path /webhooks/coterie

```

No account required. Give the printed URL to your Coterie contact as the webhook URL.

## Resources

* [references/overview.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/coterie-webhooks/references/overview.md) - The three webhook kinds, payload fields, headers
* [references/setup.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/coterie-webhooks/references/setup.md) - Enabling webhooks and the Authentication header with Coterie
* [references/verification.md](https://github.com/hookdeck/webhook-skills/blob/main/skills/coterie-webhooks/references/verification.md) - Header check in Node, Python and Next.js, gotchas
* [examples/](https://github.com/hookdeck/webhook-skills/tree/main/skills/coterie-webhooks/examples/) - Runnable Express, Next.js and FastAPI handlers with tests