Webhooks
Webhooks send real-time HTTP notifications to your application when events happen in a Harmony workspace. Use them to trigger workflows, sync data, notify external systems, or avoid polling the Public API.
Where to manage webhooks
- Open Workspace Settings.
- Open Webhooks under the Connections section.
- Create or manage webhook subscriptions from there.
Managing webhooks (create, edit, pause, resume, re-enable, regenerate secret, test, delete) requires the organization:update:all permission, which is granted to the Admin role by default. Users with only read-level organization permissions see the subscriptions list but the actions menu is replaced with a View only indicator.
Create a webhook
- Click Create Webhook.
- Fill in:
- Name (≤ 100 characters).
- Description (optional, ≤ 500 characters).
- Webhook URL — must be a valid URL; HTTPS in production. Use the Test button next to the field to verify reachability before saving (see Test a URL before saving).
- Events to Subscribe To — pick at least one event from one of the categories. You can use Select All / Deselect All per category, or expand a category to pick individual events.
- Click Create Webhook.
After creation, Harmony shows the webhook signing secret once as a 64-character hex string. Copy it immediately and store it securely — it is not displayed again. If you miss it, use Regenerate Secret from the row actions, but note that the old secret is invalidated immediately.
Event categories
Webhook events use the format <entity>.<action>, such as contact.created, conversation.transcribed, or insight.generated.
The current event catalog covers nine categories:
- Contact
- Account
- Conversation
- Project
- User
- Organization
- Integration
- Insight
- Billing
The event selector in Settings → Webhooks is the authoritative list for your workspace. Some events fire only when the related product area is enabled or used. The full event reference (with payload shapes) is available in the knowledge-base events reference.
What Harmony sends
For each matching event, Harmony sends an HTTP POST request with JSON content and webhook headers:
Content-Type: application/jsonUser-Agent: Harmony-Webhooks/1.0X-Harmony-Signature—sha256=<hex digest>X-Harmony-Event-TypeX-Harmony-Event-IDX-Harmony-Delivery
Your endpoint should respond quickly with any 2xx status and process slow work asynchronously. The default per-delivery timeout is 30 seconds.
Signature verification
Every webhook delivery is signed with HMAC-SHA256 using your subscription's secret. Verify the signature before processing the event.
The signature header format is:
sha256=<hex digest>
To verify:
- Read the raw request body before parsing JSON.
- Compute
sha256=+ the HMAC-SHA256 hex digest of the raw body using your webhook secret. - Compare with
X-Harmony-Signatureusing a constant-time comparison. - Reject the request if the signature is missing or invalid.
If you regenerate a webhook secret in Harmony, update your receiving application immediately. The old secret stops working as soon as the new one is generated.
Manage webhooks
From the Webhooks page, users with organization:update:all can:
- Test a webhook (see Two test flows).
- Pause delivery, then Resume.
- Re-enable a webhook that was auto-disabled after repeated failures.
- Regenerate Secret.
- Edit the name, description, URL, or selected events.
- Delete the subscription.
The list shows a per-row Status badge (Active / Paused / Disabled), Success Rate, and Last Delivery.
Two test flows: Test vs Test URL
Harmony has two different test flows with different behaviour. They are easy to confuse.
Test a URL before saving
- Where: in the create / edit dialog, click Test next to the URL field.
- What it does: sends a minimal, unsigned payload (
{ "event": "webhook.test", "timestamp": "…", "organizationId": <id>, "data": { "message": "This is a test webhook delivery from Harmony" } }) with a 10-second timeout. - Use it for: quickly checking whether the URL is reachable from Harmony before you commit the subscription.
- No signature. Because no subscription has been created yet, there is no secret to sign with. Don't reject this payload for failing signature checks — there isn't one.
The URL is also validated for safety before the test request is sent: HTTPS only, and localhost / loopback, private IPv4 (10/8, 172.16–31/12, 192.168/16, 169.254/16, etc.), and private IPv6 ranges are rejected.
Test an existing subscription
- Where: the ⋮ actions menu on a saved subscription → Test.
- What it does: sends a fully signed event with
event_type: "webhook.test",entity_type: "webhook_subscription", and the standard headers (X-Harmony-Signature,X-Harmony-Event-Type: webhook.test, etc.) — using your subscription's current secret. Uses the standard per-delivery timeout (30 s default). - Use it for: validating end-to-end signature verification in your application before turning on real events.
Delivery and retries
Harmony queues webhook delivery asynchronously — event creation in Harmony does not wait for your endpoint.
If delivery fails, Harmony retries with exponential backoff. After repeated consecutive failures, Harmony automatically disables the webhook until you fix the endpoint and use Re-enable in the actions menu.
Your handler should be idempotent: retried deliveries carry the same event ID, so store processed IDs if duplicate processing would be harmful.
Programmatic management
Webhook subscriptions can also be managed via the Public API at /v1/webhook-subscriptions/... using X-API-Key authentication. The same operations are available — create, list, update, delete, test a URL, test an existing subscription, get delivery stats. See API access.
Troubleshooting
Not receiving events. Confirm the subscription is Active, the URL is reachable from the public internet, and the events you've subscribed to actually fire for your workspace. Check that your endpoint returns a 2xx within the timeout.
Auto-disabled. Open the subscription, fix the endpoint issue, then click Re-enable in the row actions menu.
Signature verification fails. Confirm you're using the current secret. Verify against the raw body, not a re-serialised JSON object. Include the sha256= prefix when comparing values. Remember that the Test URL flow sends an unsigned payload — it has no signature header to verify.