Webhook
Use webhooks to receive near real-time event notifications from HeyPeers in your system.
1) Configure your webhook
You can configure webhooks in either of these ways:
- manually in the HeyPeers organization dashboard
- programmatically via API
Manual setup in HeyPeers dashboard
- Sign in to HeyPeers with an organization admin account.
- Open your organization and go to the Webhooks settings page.
- Add your endpoint URL (must be HTTPS), choose events, and enable webhook delivery.
- Save your settings.
- Copy the signing secret when it is shown and store it securely in your system.
Note: The signing secret is only shown once when created or regenerated. If lost, use Regenerate Secret.
Configure via API
Endpoint
- GET:
/organizations/:organization_id/webhook - POST:
/organizations/:organization_id/webhook - PATCH:
/organizations/:organization_id/webhook
Request Body (POST/PATCH)
{
"webhook": {
"url": "https://your-domain.com/webhooks/heypeers",
"enabled": true,
"events": ["user.registered"]
}
}Fields
| Field | Required | Description |
|---|---|---|
webhook.url | Required when enabled=true | Must be a valid HTTPS URL. |
webhook.enabled | ✅ | Enables/disables event delivery. |
webhook.events | Required when enabled=true | List of subscribed events. |
regenerate_secret | Optional | If present ("1"), rotates the webhook signing secret. |
Supported Events
user.registered
Example (Create/Update)
curl -X PATCH "https://heypeers.com/api/v2/organizations/{ORG_ID}/webhook" \
-H "Authorization: Bearer {TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"webhook": {
"url": "https://your-domain.com/webhooks/heypeers",
"enabled": true,
"events": ["user.registered"]
}
}'Success Response
{
"success": true,
"webhook": {
"id": 123,
"url": "https://your-domain.com/webhooks/heypeers",
"enabled": true,
"events": ["user.registered"],
"secret": "webhook_signing_secret",
"created_at": "2026-02-12T15:00:00Z",
"updated_at": "2026-02-12T15:05:00Z"
},
"available_events": ["user.registered"]
}Error Responses
401 Unauthorized{ "error": "Unauthorized Access" }(invalid or missing token){ "error": "Not Authorized" }(organization ID does not match token organization)
422 Unprocessable Entity{ "success": false, "errors": [...] }- Common cases: non-HTTPS URL, unsupported events, missing URL when enabled, empty event list when enabled
2) What HeyPeers sends to your endpoint
When an event occurs, HeyPeers sends an HTTP POST request to your configured webhook.url.
Request Headers
| Header | Description |
|---|---|
Content-Type | application/json |
User-Agent | HeyPeers Webhook |
X-HeyPeers-Signature | HMAC SHA256 signature with prefix sha256= |
X-HeyPeers-Event | Event name (for example user.registered) |
X-HeyPeers-Event-Id | Unique event ID (UUID) |
Payload Example (user.registered)
{
"event": "user.registered",
"event_id": "3426822d-d9ac-4aa9-8f92-be9f360d2647",
"timestamp": "2026-02-12T15:32:22Z",
"organization": {
"id": 15,
"name": "MHA Ohio"
},
"user": {
"id": 2085,
"uuid": "cb230631-ff44-45b8-bdc2-d253a7337eb3",
"email": "webhook-test@example.com",
"first_name": "Webhook",
"last_name": "Test",
"created_at": "2026-02-12T15:32:21Z"
}
}3) Delivery behavior
- Delivery method is always HTTP
POST. - Connection timeout:
5s - Read timeout:
10s - A delivery is treated as successful only when your endpoint returns
2xx. - Failed deliveries are logged by HeyPeers; automatic retry is not currently performed.
4) Verify webhook signature
Compute:
HMAC_SHA256(secret, raw_request_body)
Then compare with X-HeyPeers-Signature (sha256=<digest>).
Important: use the raw request body string exactly as received before JSON parsing.
Next.js (Route Handler) Example
import crypto from 'crypto';
import { NextRequest, NextResponse } from 'next/server';
function safeCompare(a: string, b: string) {
const aBuf = Buffer.from(a);
const bBuf = Buffer.from(b);
if (aBuf.length !== bBuf.length) return false;
return crypto.timingSafeEqual(aBuf, bBuf);
}
export async function POST(req: NextRequest) {
const rawBody = await req.text();
const signatureHeader = req.headers.get('x-heypeers-signature') || '';
const secret = process.env.HEYPEERS_WEBHOOK_SECRET || '';
const digest = crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
const expected = `sha256=${digest}`;
if (!safeCompare(signatureHeader, expected)) {
return NextResponse.json({ success: false, error: 'Invalid signature' }, { status: 401 });
}
const payload = JSON.parse(rawBody);
// Queue async processing here; return 200 quickly.
return NextResponse.json({ success: true, received_event: payload.event });
}5) Receiver Best Practices
- Return a
2xxresponse quickly (do heavy work asynchronously). - Treat
event_idas an idempotency key to avoid duplicate processing. - Enforce HTTPS and IP/network controls in your environment.
- Rotate your webhook secret immediately if you suspect exposure.
Support
For onboarding, event expansion, or troubleshooting, contact support@heypeers.com with:
- organization name
- approximate event timestamp
- your endpoint URL
- any error logs from your receiver
Last updated on