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

# Webhooks: Receiving Real-Time Events

## Overview

Webhooks enable you to receive real-time notifications when important events occur in Explorium's data ecosystem. This guide will walk you through setting up a webhook to receive push notifications for business and prospect events you're interested in monitoring.

## Quick Start

1. **Register a webhook** - Create a single webhook endpoint that will receive all event notifications
2. **Implement a secure receiver** - Set up your handler to validate and process incoming events
3. **Test connectivity** - Verify your webhook is configured correctly
4. **Enroll for events** - Subscribe to specific events using the enrollment endpoints
5. **Process events** - Receive and handle real-time events as they occur

## Webhook Architecture

Each partner can register one webhook URL that serves as the destination for all event notifications. When you enroll businesses or prospects for event monitoring, all triggered events are sent to this webhook.\
To differentiate between various monitoring use cases, you can assign a unique enrollment\_key when enrolling entities. This key is included in every event notification, allowing you to route and process events according to your internal systems.

## Step 1: Register Your Webhook

Register a webhook URL where Explorium will send event notifications.

### Endpoint

* **Method**: `POST`
* **URL**: `https://api.explorium.ai/v1/webhooks`

### Request

```json JSON theme={null}
{   
    "partner_id": "your_partner_id",   
    "webhook_url": "https://your-domain.com/webhook-handler" 
}
```

### Response

```json JSON theme={null}
{   
  "response_context": {
    "correlation_id": "string",
    "request_status": "success",
    "time_took_in_seconds": 0
  },
      "created_at": "2025-03-16T11:35:17.720Z",   
      "last_modified_time": "2025-03-16T11:35:17.720Z",   
      "webhook_url": "https://your-domain.com/webhook-handler",   
      "webhook_secret": "your_generated_secret_key" 
}
```

**Important Security Note**: The webhook\_secret is used to verify event authenticity. Store this securely and never expose it publicly.

### Optional Registration Parameters

In addition to `partner_id` and `webhook_url`, the registration endpoint accepts:

| Parameter        | Type       | Description                                                                                                                                                                                                                                                                                            |
| ---------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `headers`        | `string[]` | Custom HTTP headers Explorium sends with **every** event delivery, in `"Header-Name: value"` format. Use this to attach authentication (e.g. a bearer token) or any header your receiver requires.                                                                                                     |
| `payload_format` | `string`   | Controls the shape of the delivered request body. `json` (default) sends the event object as-is. `stringified_json` wraps the event so it can be consumed by destinations that expect a single text field — see [Using Webhooks with Claude Code Routines](#using-webhooks-with-claude-code-routines). |

```json theme={null}
{
  "partner_id": "your_partner_id",
  "webhook_url": "https://your-domain.com/webhook-handler",
  "headers": [
    "Authorization: Bearer your_token",
    "X-Custom-Header: value"
  ],
  "payload_format": "json"
}
```

### Webhook Limitations

* You can register only one webhook URL per partner ID
* Registering a new webhook will override any previous webhook configuration and generate a new webhook secret
* All event notifications for a partner ID are sent to this single URL

## Step 2: Implement Your Webhook Handler

Your webhook handler needs to:

* Accept **HTTP POST** requests.
* Validate the signature to ensure event authenticity
* Process incoming event data based on the event type and enrollment key

### Example Implementation (Python with FastAPI)

```python Python expandable theme={null}
import base64
import hashlib
import hmac
import time
from fastapi import FastAPI, Request, HTTPException

app = FastAPI()

# Store your webhook secret securely
WEBHOOK_SECRET = "your_generated_secret_key"
TIME_WINDOW_SECONDS = 300  # 5 minutes

def verify_signature(payload, received_signature, received_timestamp):
    try:
        request_time = int(received_timestamp)
    except ValueError:
        return False  # Invalid timestamp

    # Check if timestamp is within the acceptable window
    current_time = int(time.time())
    if abs(current_time - request_time) > TIME_WINDOW_SECONDS:
        return False  # Reject old requests

    hmac_key = base64.urlsafe_b64decode(WEBHOOK_SECRET.encode('utf-8'))

    # Recreate message with timestamp + Payload
    message = f"{received_timestamp}.{payload.decode('utf-8')}".encode("utf-8")

    computed_hmac = hmac.new(hmac_key, message, hashlib.sha256)
    computed_signature_b64 = base64.urlsafe_b64encode(computed_hmac.digest()).decode('utf-8')
    return hmac.compare_digest(computed_signature_b64, received_signature)

@app.post("/webhook-handler")
async def receive_event(request: Request):
    """Receive and validate webhook events."""
    payload = await request.body()
    received_signature = request.headers.get("X-Signature")
    received_timestamp = request.headers.get('X-Timestamp')

    # Ensure Content-Type is application/json
    content_type = request.headers.get('Content-Type', '')
    if not content_type.startswith('application/json'):
        raise HTTPException(status_code=415, detail="Content-Type must be application/json")

    if not received_signature or not received_timestamp or not verify_signature(payload, received_signature, received_timestamp):
        raise HTTPException(status_code=400, detail="Invalid signature")

    # Process the event data
    event_data = await request.json()
    print(f"Received event: {event_data}")
    
    # Extract the enrollment_key to determine how to process this event
    enrollment_key = event_data.get('enrollment_key')
    event_name = event_data.get('event_name')
    entity_id = event_data.get('entity_id')
    
    # Process based on enrollment_key, event_name, and entity_id
    # ...

    return {"message": "Webhook processed successfully"}
```

**Security Implementation:** The webhook handler verifies that the event is authentic by checking the HMAC signature included in the X-Signature header. This prevents unauthorized systems from sending fake events to your endpoint.

## Step 3: Test Webhook Connectivity

After implementing your webhook handler, verify it's working correctly:

### Endpoint

* **Method**: `POST`
* **URL**: `https://api.explorium.ai/v1/webhooks/check_connectivity`

### Request

```json JSON theme={null}
{   
    "partner_id": "your_partner_id"
}
```

### Response

```json JSON theme={null}
{
  "response_context": {
    "correlation_id": "string",
    "request_status": "success",
    "time_took_in_seconds": 0
  }
}
```

* The system will send a test event to your webhook and return the response from your handler

## Advanced Testing

Once your webhook is set, You can run more enhanced testing, that will allow you to:

* Test with mock data for specific event types
* Validate your event handling logic with realistic event payloads
* Simulate various event scenarios for better integration testing

Use the same Test webhook connectivity url, with these parameters:

* **event\_name**: event type to simulate (currently available types: new\_office, closing\_office, funding\_round, new\_investment, outages\_and\_security\_breaches)
* **number\_of\_events**: the number of simulated events to receive.

### Request:

```json JSON theme={null}
{   
    "partner_id": "your_partner_id",
  	"simulation": {
    	"event_name": "funding_round",
    	"number_of_events": 3
  	}
}
```

### Response:

```json JSON theme={null}
{
  "response_context": {
    "correlation_id": "7f53fa0b79fd4edf9fce50d1e530aac7",
    "request_status": "success",
    "time_took_in_seconds": 0.563
  }
}
```

* This will result in pushing 3 "funding\_round" events to your webhook.\
  You can use the response to validate your event handling logic.

## Step 4: Enroll for Events

Once your webhook is set up, you can enroll entities (businesses or prospects) for event monitoring.

### Business Enrollment Endpoint

**Method**: `POST`\
**URL**: `https://api.explorium.ai/v1/businesses/events/enrollments`

### Example Request

```json JSON theme={null}
{
  "enrollment_key": "your_custom_key_123",
  "event_types": ["new_product", "new_funding_round"],
  "business_ids": ["business_id_1", "business_id_2"]
}
```

### Prospect Enrollment Endpoint

**Method**: `POST`\
**URL**:`https://api.explorium.ai/v1/prospects/enrollments`

### Example Request

```json JSON theme={null}
{
  "enrollment_key": "your_custom_key_123",
  "event_types": ["event1", "event2"],
  "prospect_ids": ["prospect_id_1", "prospect_id_2"]
}
```

### Key Enrollment Parameters:

* `enrollment_key`: A custom identifier you provide to categorize this set of enrollments. You can use this to:
  * Group enrollments by customer/client ID
  * Segment by monitoring campaign or use case
  * Differentiate between different business processes
  * This key will be included in each event notification, allowing you to route events appropriately
* `business_ids/prospect_ids`: Business or prospect IDs to monitor (up to 20 per request)
  * While each request is limited to 20 IDs, there's no limit on the total number of IDs you can enroll across multiple requests
  * You can enroll thousands of entities by making multiple enrollment requests

### Event Payload Structure

When an enrolled event is triggered, you'll receive a payload like this:

```json JSON theme={null}
{
"tenant_id": "con_dJJUz1jfqdwzfM4t",
"partner_id": "ExpTest",
"event_id": "691377c2df80547c70e26a13216a3944",
"event_name": "new_product",
"data": {
	"link": "https://www.williamsburgfamilies.com/tabitha-sewer-studio-classes-sewing-cricut-crochet-for-adults-kids-and-camps/",
	"title": "Sewing, Cricut, Craft, Art, Crochet for Adults & Kids at Tabitha Sewer Studio Classes in Williamsburg",
	"snippet": "If you are looking for sewing classes for kids and adults, craft classes and Adult Meet & Makes - click on this post!",
	"product_name": "Maker Lab",
	"product_description": "The ultimate creative escape for families offering a rotating lineup of trendy, social-media-inspired craft experiences designed for all ages, including activities like Sugarcoated faux icing crafts, Cap 'N Craft hat personalization, Adopt A Pal plush pet decorating, and The Glow Up candle-making for ages 16 and up."
},
"enrollment_key": "test",
"enrollment_id": "test",
"event_time": "2025-12-06 11:41:55.703000+00:00",
"business_id": "57e7fd862b2ac1c9358bccf8e90faeb1"
}
```

### Key Fields:

`enrollment_key`: The identifier you provided during enrollment\
`event_id`: A unique identifier for this specific event \
`event_name`: The type of event that was triggered \
`entity_type`: Either "business" or "prospect" \
`entity_id`: The ID of the business or prospect that triggered the event \
`data`: Event-specific data (varies by event type)

## Using Webhooks with Claude Code Routines

[Claude Code routines](https://docs.claude.com/en/docs/claude-code/routines) can run automatically in response to an external HTTP request through an **API trigger**. You can point an Explorium webhook directly at a routine's trigger URL so that every Explorium event fires the routine — and, with the right `payload_format`, the event data is delivered straight into the routine for processing. No relay or intermediate service is required.

### How it works

A routine's API trigger exposes a *fire* URL of the form:

```text theme={null}
POST https://api.anthropic.com/v1/claude_code/routines/{trigger_id}/fire
```

This endpoint:

* Requires an `Authorization: Bearer <token>` header (generated when you create the routine) and an `anthropic-version: 2023-06-01` header.
* Injects the request body's `text` field into the routine as its input, and ignores other top-level fields.

Because the routine only reads a `text` field, Explorium's default event object isn't consumed by the routine on its own. Setting `payload_format` to `stringified_json` tells Explorium to deliver the event in a routine-compatible shape:

```json theme={null}
{
  "text": "{\"event_name\": \"new_funding_round\", \"data\": { ... }, \"business_id\": \"...\", ...}"
}
```

The `text` value is the complete Explorium event object serialized as a JSON string, so the routine can parse it and act on the event.

### Step 1 — Create the routine

In **claude.ai → Code → Routines**, create a routine and choose the **API** trigger ("Trigger from your own code by sending a POST request"). Copy:

* the trigger's **fire URL**, and
* the **token** generated for the trigger.

In the routine instructions, tell Claude that each run will receive an Explorium event payload and describe what it should do with it (for example, summarize the event, enrich the entity, or alert a channel).

### Step 2 — Register the webhook

Register the routine's fire URL as your webhook, supplying the required headers and `payload_format: "stringified_json"`:

```json theme={null}
{
  "partner_id": "your_partner_id",
  "webhook_url": "https://api.anthropic.com/v1/claude_code/routines/{trigger_id}/fire",
  "headers": [
    "Authorization: Bearer sk-ant-oat01-...",
    "anthropic-version: 2023-06-01"
  ],
  "payload_format": "stringified_json"
}
```

<Note>
  The `anthropic-version` header is required — without it the routine endpoint rejects the delivery with a `400` error. Each event is delivered as a separate POST, so one routine run is triggered per event.
</Note>

### Step 3 — Test it

Use the [connectivity endpoint](#step-3-test-webhook-connectivity) to push simulated events to your routine:

```json theme={null}
{
  "partner_id": "your_partner_id",
  "simulation": {
    "event_name": "new_funding_round",
    "number_of_events": 3
  }
}
```

A `"request_status": "success"` response means the routine accepted the events. Open the routine's sessions in claude.ai to see each event rendered and processed.

<Warning>
  The token stored in `headers` can trigger your routine. Treat it as a secret, and rotate it if it may have been exposed.
</Warning>

## Managing Your Webhook

### Get Webhook Details

#### Endpoint

**Method**: GET\
**URL**: [https://api.explorium.ai/v1/webhooks/](https://api.explorium.ai/v1/webhooks/%7Bpartner_id%7D)

### Delete Your Webhook

#### Endpoint

**Method**: DELETE\
**URL**: [https://api.explorium.ai/v1/webhooks/](https://api.explorium.ai/v1/webhooks/%7Bpartner_id%7D)

## Best Practices

1. **Use the enrollment\_key effectively**: Structure your enrollment keys to match your internal systems and use cases.
2. **Set up monitoring**: Implement monitoring for your webhook endpoint to detect any delivery failures.
3. **Validate all signatures**: Always verify the event signature to ensure authenticity before processing.
4. **Use proper Content-Type**: Ensure your webhook handler accepts and responds with the application/json Content-Type.

## Frequently Asked Questions

<AccordionGroup>
  <Accordion title="How many webhooks can I register per partner ID?">
    You can register only one webhook URL per partner ID. This URL will receive all event notifications.
  </Accordion>

  <Accordion title="Is there a limit to how many businesses or prospects I can enroll?">
    While each enrollment request is limited to 20 IDs, there's no limit on the total number of IDs you can enroll across multiple requests.
  </Accordion>

  <Accordion title="How do I differentiate between different monitoring use cases?">
    Use the enrollment\_key parameter when enrolling entities. This key is included in every event notification, allowing you to route and process events according to your internal systems.
  </Accordion>

  <Accordion title="What happens if I register a new webhook?">
    Registering a new webhook will override your previous webhook configuration and generate a new webhook secret. All event notifications will be sent to the new URL.
  </Accordion>

  <Accordion title="How can I test my webhook implementation?">
    Use the /webhooks/check\_connectivity endpoint to validate your webhook handler is properly receiving and processing events.
  </Accordion>

  <Accordion title="Can I trigger a Claude Code routine from a webhook?">
    Yes. Point your webhook at the routine's API-trigger fire URL, pass the routine's `Authorization` and `anthropic-version: 2023-06-01` headers via the `headers` parameter, and set `payload_format` to `stringified_json`. See [Using Webhooks with Claude Code Routines](#using-webhooks-with-claude-code-routines).
  </Accordion>
</AccordionGroup>
