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

# Report Webhooks

> Register a webhook endpoint to receive an instant notification — with a signed download URL — when a campaign report run completes 🔔

Instead of polling the [Reports API](https://docs.connectly.ai/analytics/reports-api) on a schedule, register a webhook endpoint that Connectly calls the moment a report run completes. The payload includes a signed URL so you can download the CSV immediately — no extra API call required.

<Warning>
  The signed URL in a webhook payload expires after a **few minutes only**. Download the file immediately upon receiving the webhook. If the URL expires before you can download it, call the [Reports API](https://docs.connectly.ai/analytics/reports-api) to retrieve a fresh one.
</Warning>

## Registering the webhook

Use the [Create webhook](https://docs.connectly.ai/api-reference/create-webhook) endpoint with `"topic": "report"`:

```bash theme={null}
curl -X POST "https://api.connectly.ai/v1/businesses/{businessId}/create/webhooks" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "topic": "report",
    "address": "https://yourdomain.com/connectly-webhooks",
    "type": "custom"
  }'
```

| Field     | Description                                             |
| --------- | ------------------------------------------------------- |
| `topic`   | Must be `"report"` to receive report completion events. |
| `address` | Your publicly accessible HTTPS endpoint.                |
| `type`    | Set to `"custom"` for a generic HTTP webhook.           |

## Webhook payload

When a report run completes, Connectly POSTs the following payload to your endpoint:

```json theme={null}
{
  "topic": "report",
  "timestamp": "2025-04-30T12:34:56Z",
  "report": {
    "id": "c80716a5-62c5-4ba5-b99d-5c2a6fd39317",
    "name": "Daily Campaign Report",
    "type": "campaign.daily.custom",
    "last_run": {
      "id": "run-001",
      "status": "completed",
      "result": {
        "url": "https://reports.connectly.ai/abcdef",
        "expires_at": "2025-05-01T12:34:56Z",
        "created_at": "2025-04-30T12:00:00Z"
      }
    }
  }
}
```

| Field                               | Description                                                     |
| ----------------------------------- | --------------------------------------------------------------- |
| `topic`                             | Always `"report"` for this webhook type.                        |
| `timestamp`                         | ISO 8601 timestamp of when the webhook was sent.                |
| `report.id`                         | Unique ID of the report configuration.                          |
| `report.name`                       | Human-readable report name.                                     |
| `report.type`                       | Report type identifier.                                         |
| `report.last_run.id`                | ID of the completed run.                                        |
| `report.last_run.status`            | Always `"completed"` when the webhook fires.                    |
| `report.last_run.result.url`        | Signed URL to download the report CSV. Expires at `expires_at`. |
| `report.last_run.result.expires_at` | When the signed URL expires — download immediately.             |
| `report.last_run.result.created_at` | When the report file was generated.                             |

## Recommended handling

<Steps>
  <Step title="Acknowledge receipt immediately">
    Return a `2xx` response as soon as you receive the webhook — before doing any processing. Connectly may retry if it doesn't receive a timely acknowledgement.
  </Step>

  <Step title="Extract and validate the URL">
    Read `report.last_run.result.url` and check `expires_at` to confirm the URL is still valid.
  </Step>

  <Step title="Download the CSV immediately">
    Perform a GET request to the signed URL right away — it expires within minutes.
  </Step>

  <Step title="Handle expiry gracefully">
    If your handler is delayed and the URL has expired, call the [Reports API](https://docs.connectly.ai/analytics/reports-api) to retrieve a fresh signed URL for the same report.
  </Step>
</Steps>

<Tip>
  Your webhook endpoint must be reachable over HTTPS. Connectly does not deliver webhooks to plain HTTP addresses.
</Tip>
