Webhooks API
Webhooks allow you to receive real-time HTTP POST notifications when regulations change. This is an Enterprise tier feature ($499/mo).
POST /v1/webhooks
Register a new webhook subscription.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
endpoint_url | string | Yes | The HTTPS URL to receive webhook payloads. Must use https://. |
filters | object | No | Optional filters to scope which events trigger this webhook. |
filters.states | array | No | Array of state codes (e.g., ["CO", "CA"]). If omitted, all states. |
filters.substances | array | No | Array of substance IDs. If omitted, all substances. |
filters.product_types | array | No | Array of product type IDs. If omitted, all product types. |
filters.event_types | array | No | Array of event types to subscribe to. If omitted, all events. |
description | string | No | Optional description for this subscription. |
Request Example
curl -X POST "https://api.hempdataapi.com/v1/webhooks" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"endpoint_url": "https://yourapp.com/webhooks/hempdata",
"filters": {
"states": ["CO", "CA", "TX", "FL", "NY"],
"substances": ["delta-9-thc", "delta-8-thc"],
"event_types": ["legal_status_changed", "new_regulation"]
},
"description": "Monitor THC regulation changes in key sales states"
}'
Response Example
{
"success": true,
"data": {
"id": "whk_a1b2c3d4e5f6",
"endpoint_url": "https://yourapp.com/webhooks/hempdata",
"filters": {
"states": ["CO", "CA", "TX", "FL", "NY"],
"substances": ["delta-9-thc", "delta-8-thc"],
"event_types": ["legal_status_changed", "new_regulation"]
},
"description": "Monitor THC regulation changes in key sales states",
"signing_secret": "whsec_live_k8J2mN4pQ7rT1vX3zA5cE8gI0lO2qU4wY6bD9fH",
"status": "active",
"created_at": "2026-03-18T10:00:00Z"
},
"meta": {
"request_id": "req_whk_001"
},
"errors": []
}
Important: The signing_secret is only returned once, at creation time. Store it securely — you will need it to verify webhook signatures.
GET /v1/webhooks
List all webhook subscriptions for your account.
Request Example
curl -X GET "https://api.hempdataapi.com/v1/webhooks" \
-H "Authorization: Bearer YOUR_API_KEY"
Response Example
{
"success": true,
"data": [
{
"id": "whk_a1b2c3d4e5f6",
"endpoint_url": "https://yourapp.com/webhooks/hempdata",
"filters": {
"states": ["CO", "CA", "TX", "FL", "NY"],
"substances": ["delta-9-thc", "delta-8-thc"],
"event_types": ["legal_status_changed", "new_regulation"]
},
"description": "Monitor THC regulation changes in key sales states",
"status": "active",
"created_at": "2026-03-18T10:00:00Z",
"last_triggered_at": "2026-03-18T14:30:00Z",
"total_deliveries": 12,
"failed_deliveries": 0
}
],
"meta": {
"total": 1,
"request_id": "req_whk_002"
},
"errors": []
}
DELETE /v1/webhooks/:id
Remove a webhook subscription.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
id | string | Webhook subscription ID (e.g., whk_a1b2c3d4e5f6) |
Request Example
curl -X DELETE "https://api.hempdataapi.com/v1/webhooks/whk_a1b2c3d4e5f6" \
-H "Authorization: Bearer YOUR_API_KEY"
Response Example
{
"success": true,
"data": {
"id": "whk_a1b2c3d4e5f6",
"deleted": true
},
"meta": {
"request_id": "req_whk_003"
},
"errors": []
}
Webhook Payload Structure
When a matching event occurs, HempData sends an HTTP POST to your endpoint with this payload:
{
"event_id": "evt_x1y2z3a4b5",
"event_type": "legal_status_changed",
"regulation_id": "reg_f1e2d3c4-5678-9012-abcd-ef3456789012",
"jurisdiction": {
"state": "TX",
"state_name": "Texas"
},
"substance": "delta-8-thc",
"product_type": "edibles",
"before": {
"legal_status": "legal_with_restrictions",
"summary": "Delta-8 THC edibles permitted with labeling requirements.",
"thc_limit": "No per-serving limit specified",
"age_requirement": 21
},
"after": {
"legal_status": "prohibited",
"summary": "Delta-8 THC edibles prohibited following HB 1842 effective March 1, 2026.",
"thc_limit": "N/A — prohibited",
"age_requirement": null
},
"diff_summary": "legal_status: legal_with_restrictions -> prohibited; thc_limit: 'No per-serving limit specified' -> 'N/A — prohibited'",
"plain_english_summary": "Texas has banned Delta-8 THC edibles effective March 1, 2026. If you sell these products in Texas, you must cease sales immediately.",
"timestamp": "2026-03-01T06:15:00Z"
}
Payload Fields
| Field | Type | Description |
|---|---|---|
event_id | string | Unique event identifier |
event_type | string | Type of change (see event types below) |
regulation_id | string | The regulation that changed |
jurisdiction | object | State code and name |
substance | string | Affected substance |
product_type | string | Affected product type |
before | object | Regulation state before the change |
after | object | Regulation state after the change |
diff_summary | string | Machine-readable summary of changed fields |
plain_english_summary | string | Human-readable explanation of the change and its business impact |
timestamp | string | When the change was detected (ISO 8601) |
Event Types
| Event Type | Description |
|---|---|
legal_status_changed | The legal status changed (the most critical event) |
details_updated | Regulatory details changed (limits, requirements, etc.) |
new_regulation | A new regulation entry was created |
regulation_expired | A regulation reached its sunset date |
bill_linked | A new pending bill was linked to a regulation |
confidence_changed | Confidence score changed significantly (>0.1 shift) |
Signature Verification
Every webhook request includes an X-HempData-Signature header containing an HMAC-SHA256 signature. Always verify this signature before processing the payload.
How it Works
- HempData computes
HMAC-SHA256(signing_secret, raw_request_body)and sends the hex-encoded result in theX-HempData-Signatureheader. - Your server computes the same HMAC using the signing secret from webhook creation.
- Compare the two values using a constant-time comparison to prevent timing attacks.
Node.js Verification Example
const crypto = require('crypto');
function verifyWebhookSignature(rawBody, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody, 'utf8')
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature, 'hex'),
Buffer.from(expected, 'hex')
);
}
// In your Express handler:
app.post('/webhooks/hempdata', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-hempdata-signature'];
const isValid = verifyWebhookSignature(req.body, signature, process.env.HEMPDATA_WEBHOOK_SECRET);
if (!isValid) {
return res.status(401).send('Invalid signature');
}
const event = JSON.parse(req.body);
// Process the event...
res.status(200).send('OK');
});
Python Verification Example
import hmac
import hashlib
def verify_webhook_signature(raw_body: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(
secret.encode('utf-8'),
raw_body,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected)
Retry Schedule
If your endpoint returns a non-2xx status code or times out (30-second timeout), HempData retries with the following schedule:
| Attempt | Delay After Failure |
|---|---|
| 1 | 1 minute |
| 2 | 5 minutes |
| 3 | 30 minutes |
| 4 | 2 hours |
| 5 | 12 hours |
| 6 | 24 hours |
After 6 failed attempts, the delivery is marked as failed. You can view failed deliveries in the dashboard and manually retry them.
If an endpoint accumulates 50 consecutive failed deliveries, the subscription is automatically paused. You will receive an email notification and can reactivate it from the dashboard after fixing the endpoint.
Best Practices
- Respond quickly — Return a
200status code within 5 seconds. Process the payload asynchronously if needed. - Verify signatures — Always validate the
X-HempData-Signatureheader before trusting the payload. - Handle duplicates — Use the
event_idfield to deduplicate, as retries may deliver the same event more than once. - Use filters — Scope your subscriptions to only the states and substances you care about to reduce noise.
- Monitor health — Check the dashboard for failed deliveries and endpoint health.