> ## Documentation Index
> Fetch the complete documentation index at: https://docs.launchpointhq.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhooks

> Get notified when things happen in your campaigns

Webhooks push events to your server as they happen, so you don't have to poll. They cover the two moments that are asynchronous by design — publish approval and creator activity — plus everything downstream.

Configure your webhook URL in [dashboard settings](https://dashboard.launchpointhq.com/settings#api). One URL per account receives all events.

## Events

| Event                     | Fires when                                                               |
| ------------------------- | ------------------------------------------------------------------------ |
| `campaign.status_changed` | A campaign changes status — publish approval/denial, pauses, resumes     |
| `creator.applied`         | A creator applies to a campaign                                          |
| `creator.approved`        | A creator application is approved (via API or dashboard)                 |
| `creator.rejected`        | A creator application is rejected                                        |
| `submission.created`      | A creator submits content for review, including revisions                |
| `submission.approved`     | A submission is approved (via API or dashboard)                          |
| `submission.rejected`     | A submission is rejected                                                 |
| `post.created`            | Approved content goes live                                               |
| `post.metrics_updated`    | A live post's engagement metrics are refreshed (at most hourly per post) |

Decision events (`creator.*`, `submission.*`) fire regardless of where the decision was made — so an integration stays in sync even when your team works in the dashboard.

## Payloads

Every event is a JSON `POST` with the event type, campaign context, the affected resource, and a timestamp:

```json theme={null}
{
  "event_type": "submission.created",
  "campaign_id": "3f8e4567-e89b-12d3-a456-426614174000",
  "brand_id": "9a2b4567-e89b-12d3-a456-426614174000",
  "submission": {
    "id": "7c1d4567-e89b-12d3-a456-426614174000",
    "creator_id": "cr_8f14e45fceea",
    "creator_username": "somecreator",
    "task_id": "5b0a4567-e89b-12d3-a456-426614174000",
    "status": "in_review",
    "media_type": "video",
    "media_url": "https://cdn.launchpointhq.com/...",
    "revision_count": 0,
    "submitted_at": "2026-07-02T15:04:05Z"
  },
  "timestamp": "2026-07-02T15:04:06Z"
}
```

`campaign.status_changed` carries `previous_status`, `status`, and `status_detail` instead of a resource object. `creator.*` events carry a `creator` object; `post.*` events carry a `post` object — the same shapes the read endpoints return.

## Verifying signatures

Each delivery is signed with HMAC-SHA256 using your webhook secret (shown alongside the URL in dashboard settings). The `X-Launchpoint-Signature` header contains the hex digest of the raw request body:

```javascript theme={null}
import { createHmac, timingSafeEqual } from "crypto";

function verify(rawBody, signatureHeader, secret) {
  const expected = createHmac("sha256", secret).update(rawBody).digest("hex");
  return timingSafeEqual(Buffer.from(signatureHeader), Buffer.from(expected));
}
```

Always verify before trusting a payload, and compute the HMAC over the raw bytes — not the re-serialized JSON.

## Delivery and retries

* Respond with any `2xx` status within 10 seconds to acknowledge receipt.
* Failed deliveries are retried with exponential backoff for up to 24 hours.
* Delivery is **at least once**: build your handler to be idempotent (the combination of `event_type`, resource `id`, and `timestamp` makes a good deduplication key).
* Events may occasionally arrive out of order — trust the resource's `status`, not the arrival sequence.
