# Inbound webhooks

An **inbound webhook** is an endpoint Meteor exposes so a third party can `POST` to it. When an event arrives, Meteor can **trigger a task** or **emit** an internal event. It needs the `webhooks:manage` scope.

> These are **inbound** webhooks (third party → Meteor). If what you want is for **Meteor to notify you** when something happens (`run.completed`, etc.), jump to [Outbound events](#outbound-events-meteor-your-server) below.

## Create a webhook

```ts
const wh = await met.webhooks.create({
  name: 'Lead intake',
  binding_type: 'generic',        // 'task' runs a task; 'generic' only emits an event
  verifier_type: 'hmac_sha256',   // 'none' for unsigned
});

console.log(wh.url);      // the URL the third party points at
console.log(wh.secret);   // ⚠️ shown ONCE only
```

Store the `secret` the moment you receive it: **it is never shown again**. With it you verify that each delivery really came from your integration.

## Verify the signature

The third party signs the body with the `secret` (HMAC-SHA256) and sends the signature in a header. On Meteor's side, verification is automatic if the webhook is `hmac_sha256`.

## Rotate the secret

If you suspect it leaked:

```ts
const rotated = await met.webhooks.rotateSecret(wh.id);
console.log(rotated.secret);   // new secret; invalidates the previous one
```

## See deliveries

```ts
const events = await met.webhooks.events(wh.id, { limit: 20 });
const sample = await met.webhooks.sample(wh.id);   // last payload received
```

`sample` is handy for setting up the payload's field mapping.

## Connect a flow

A `generic` webhook can trigger a flow when it receives an event (`connectFlow` in the API). That way an external `POST` starts a full automation inside Meteor.

## Outbound events (Meteor → your server)

An **outbound event** is Meteor `POST`ing to *you* when something happens in your workspace — for example, when a Run finishes. You register an endpoint, choose which events you want, and Meteor delivers each one **signed with HMAC** so you can verify it came from Meteor.

### Register an endpoint

You register the endpoint from your panel: **Developers → Activity → Webhooks**. You choose your server's URL and which events you want (or `*` for all of them). On creation, Meteor shows you the **signing secret** (`whsec_...`) **once only** — store it, you need it to verify every delivery.

### Event catalog

| Event | When it fires |
|---|---|
| `run.completed` | A Run finished successfully. |
| `run.failed` | A Run failed. |
| `contact.created` / `contact.updated` | A contact was created or changed. |
| `conversation.handoff` | A conversation moved to a human operator. |
| `task.completed` | An agentic task finished. |
| `billing.threshold` | A key's Energy spend crossed a threshold of its budget (50/80/100%). |
| `app.authorized` / `app.revoked` | A workspace authorized or revoked your OAuth app. Delivered to the workspace that **owns** the app (requires `integrations:read`). |

### Verify the signature

Every delivery carries the header `X-Met-Signature: t=<ts>,v1=<hmac>`. **Never** process an event without verifying it — use the SDK helper, which does the verification timing-safe and checks the timestamp (anti-replay):

```ts
import Met from '@meteor.ia/sdk';
const met = new Met(process.env.MET_API_KEY!);

app.post('/webhooks/met', (req, res) => {
  let event;
  try {
    event = met.webhooks.constructEvent(req.rawBody, req.headers['x-met-signature'], WHSEC);
  } catch {
    return res.status(400).send('invalid signature');
  }
  if (event.type === 'run.completed') { /* ... */ }
  res.sendStatus(200);   // answer 2xx quickly; Meteor retries otherwise.
});
```

> You need the **raw body** to verify the signature — configure your framework not to re-serialize the JSON before `constructEvent`.

### Retries

If your endpoint doesn't answer `2xx`, Meteor retries with exponential backoff (1 min → 5 min → 30 min → 2 h → 6 h → 24 h) up to 6 attempts. After that it marks the delivery as `failed`. The **attempt log** per endpoint is in your Workbench (Developers section).
