Outbound Webhooks

Outbound Webhooks

Outbound webhooks allow your agency to receive real-time notifications whenever key events occur inside your DashboardFox account — such as a new workspace being created, a trial expiring, or a subscription payment failing. Instead of polling the platform for changes, your external systems (Zapier, Make, your own application, or any automation tool) are notified automatically the moment something happens.

Each webhook sends an HTTP POST request containing a JSON payload to a URL you configure. You can set up as many endpoints as you need, each with its own URL, event subscriptions, and optional security signature.


How It Works

  1. You configure an endpoint URL and choose which events to subscribe to.

  2. When a subscribed event occurs, DashboardFox sends an HTTP POST to your URL within seconds.

  3. Your receiving application processes the payload and takes action — creating a record, sending an email, triggering a workflow, etc.

  4. DashboardFox logs every delivery attempt. If your endpoint returns an error, it will retry automatically (see Retry Behaviour below).


Finding the Webhooks Settings

Webhooks are managed from your Agency settings panel:

  1. Log in as an Agency Owner.

  2. Click your agency name in the top navigation to open Agency Settings.

  3. Select the Webhooks tab.

Note: Only Agency Owners can create, edit, or delete webhook endpoints. Billing Admins and Members cannot access this tab.


Creating a Webhook Endpoint

  1. Click Add in the top-right corner of the endpoint list.

  2. Fill in the Name field — this is just a label for your reference (e.g. "Zapier — New Workspace").

  3. Enter your Endpoint URL. This must be an HTTPS address. HTTP endpoints are not accepted.

  4. Optionally enter a Signing Secret (see Signature Verification below). If provided, you will only be shown this value once — copy it immediately.

  5. Under Events to Subscribe, check each event type you want this endpoint to receive.

  6. Click Create. Your new endpoint will appear in the list on the left.

Tip: You can create multiple endpoints — for example, one pointed at Zapier for workspace events and another pointed at your own API for billing events.


Editing an Endpoint

Click any endpoint in the left panel to open its settings on the right. You can update the name, URL, and event subscriptions at any time, then click Save.

Changing the signing secret: The secret cannot be edited directly after creation. Click Regenerate to generate a new one. The new secret is shown once — update your receiving application before navigating away.


Enabling and Disabling an Endpoint

Each endpoint has a toggle switch in the endpoint list. Flipping it off pauses all deliveries to that endpoint without deleting it. Toggle it back on at any time to resume.


Deleting an Endpoint

Select the endpoint, then click Delete in the top-right of the editor panel. You will be asked to confirm. Deletion is permanent and will remove all delivery history for that endpoint.


Testing an Endpoint

With an endpoint selected, click the Test button. DashboardFox will send a sample payload for each of the endpoint's subscribed events, with the "test": true flag set in the envelope. The results are shown immediately and are also visible in the Delivery Log below the editor.

Tip: Use the Test button to verify your receiving application is reachable and parsing the payload correctly before going live.


Delivery Log

The delivery log shows the last 50 attempts for the selected endpoint. Each row displays:

  • Event type — which event triggered the delivery

  • HTTP status code — the response returned by your endpoint

  • Duration — how long your endpoint took to respond

  • Timestamp — when the attempt was made

  • Attempt number — 1 for the initial delivery, 2–4 for retries

Click any row to expand it and see the full payload that was sent and the response body your endpoint returned.


Retry Behaviour

If your endpoint returns a non-2xx status code or times out, DashboardFox will retry the delivery automatically:

Attempt

Delay After Previous Attempt

1 (initial)

Immediate

2

30 seconds

3

5 minutes

4

1 hour

After 4 attempts with no success, the delivery is marked as failed. No further retries will occur. Each attempt is logged individually in the Delivery Log.

Note: Your endpoint must respond within 10 seconds. Responses that take longer are treated as a timeout and counted as a failed attempt.


Signature Verification (Optional but Recommended)

If you configure a signing secret, every delivery will include two additional HTTP headers:

Header

Description

X-Webhook-Signature

HMAC-SHA256 signature of the request body, prefixed with sha256=

X-Webhook-Timestamp

Unix timestamp (milliseconds) when the request was sent

How to verify the signature

To confirm a delivery is genuinely from DashboardFox and has not been tampered with:

  1. Read the raw request body as a string (before any JSON parsing).

  2. Compute HMAC-SHA256 of the body string using your signing secret.

  3. Compare the result (prefixed with sha256=) against the value in X-Webhook-Signature.

  4. Optionally, check that X-Webhook-Timestamp is within an acceptable window (e.g. 5 minutes) to protect against replay attacks.

Example — Node.js

const crypto = require('crypto');
 
function verifyWebhook(rawBody, signature, secret) {
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(rawBody) // raw string, not parsed JSON
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signature)
);
}

Example — PHP

function verifyWebhook(string $rawBody, string $signature, string $secret): bool {
$expected = 'sha256=' . hash_hmac('sha256', $rawBody, $secret);
return hash_equals($expected, $signature);
}

Payload Envelope

Every event — regardless of type — uses the same outer envelope:

{
"event": "workspace.created",
"fired_at": "2026-02-24T17:00:00Z",
"test": false,
"agency_id": "agency-abc123",
"data": {
...event-specific fields...
}
}

Field

Type

Description

event

string

The event slug that triggered this delivery

fired_at

string

ISO 8601 UTC timestamp of when the event fired

test

boolean

true only when sent via the Test button — ignore for production logic

agency_id

string

Your agency identifier

data

object

Event-specific payload — see reference below


Event Reference

workspace.created

Fired when a new workspace is created under your agency.

"data": {
"workspace_id": 1042,
"workspace_name": "Acme Corp",
"workspace_domain": "acme-corp",
"owner_email": "jane@acme.com",
"tier_name": "professional",
"agency_id": "agency-abc123"
}

workspace.deleted

Fired when a workspace is permanently deleted.

"data": {
"workspace_id": 1042,
"workspace_name": "Acme Corp",
"workspace_domain": "acme-corp",
"owner_email": "jane@acme.com",
"tier_name": "professional",
"agency_id": "agency-abc123"
}

workspace.suspended

Fired when a workspace is suspended — typically due to a payment issue.

"data": {
"workspace_id": 1042,
"workspace_name": "Acme Corp",
"workspace_domain": "acme-corp",
"owner_email": "jane@acme.com",
"tier_name": "professional",
"agency_id": "agency-abc123"
}

workspace.reactivated

Fired when a suspended workspace is reactivated.

"data": {
"workspace_id": 1042,
"workspace_name": "Acme Corp",
"workspace_domain": "acme-corp",
"owner_email": "jane@acme.com",
"tier_name": "professional",
"agency_id": "agency-abc123"
}

workspace.transfer_completed

Fired when an ownership transfer of a workspace is accepted and completed.

"data": {
"workspace_id": 1042,
"workspace_name": "Acme Corp",
"workspace_domain": "acme-corp",
"from_email": "old-owner@acme.com",
"to_email": "new-owner@acme.com",
"agency_id": "agency-abc123"
}

workspace.trial_started

Fired when a new workspace enters its trial period.

"data": {
"workspace_id": 1042,
"workspace_name": "Acme Corp",
"workspace_domain": "acme-corp",
"owner_email": "jane@acme.com",
"subscription_status": "trial",
"is_lifetime": false,
"trial_ends_at": "2026-03-17T00:00:00Z",
"agency_id": "agency-abc123"
}

workspace.trial_expiring

Fired when a trial is 3 days away from expiring. Useful for sending reminder emails or prompting upgrade flows. This event fires once per workspace per day while the 3-day window applies.

"data": {
"workspace_id": 1042,
"workspace_name": "Acme Corp",
"workspace_domain": "acme-corp",
"owner_email": "jane@acme.com",
"subscription_status": "trial",
"is_lifetime": false,
"trial_ends_at": "2026-03-17T00:00:00Z",
"agency_id": "agency-abc123"
}

workspace.trial_expired

Fired when a trial period ends and the workspace has not been converted to a paid subscription.

"data": {
"workspace_id": 1042,
"workspace_name": "Acme Corp",
"workspace_domain": "acme-corp",
"owner_email": "jane@acme.com",
"subscription_status": "trial_expired",
"is_lifetime": false,
"trial_ends_at": "2026-03-17T00:00:00Z",
"agency_id": "agency-abc123"
}

workspace.trial_extended

Fired when a trial period is extended, either by an admin or automatically.

"data": {
"workspace_id": 1042,
"workspace_name": "Acme Corp",
"workspace_domain": "acme-corp",
"owner_email": "jane@acme.com",
"subscription_status": "trial",
"is_lifetime": false,
"trial_ends_at": "2026-03-24T00:00:00Z",
"extension_count": 1,
"is_admin_extension": true,
"agency_id": "agency-abc123"
}

Field

Description

trial_ends_at

The new trial expiry date after extension

extension_count

How many times this trial has been extended

is_admin_extension

true if extended by a DashboardFox admin, false if automatic


workspace.ltd_code_redeemed

Fired when a lifetime deal code is redeemed and a workspace is activated as a lifetime workspace for the first time.

"data": {
"workspace_id": 1042,
"workspace_name": "Acme Corp",
"workspace_domain": "acme-corp",
"owner_email": "jane@acme.com",
"subscription_status": "lifetime",
"is_lifetime": true,
"ltd_platform": "appsumo",
"ltd_codes_count": 1,
"ltd_tier": "lifetime_tier1",
"tier_name": "Lifetime Tier 1",
"agency_id": "agency-abc123"
}

Field

Description

ltd_platform

The platform the deal was purchased from (e.g. appsumo, stacksocial)

ltd_codes_count

Total codes applied to this workspace

ltd_tier

Internal tier code for the lifetime plan

tier_name

Human-readable lifetime tier name


workspace.ltd_code_stacked

Fired when an additional lifetime deal code is stacked onto an existing lifetime workspace, upgrading its tier or capacity.

"data": {
"workspace_id": 1042,
"workspace_name": "Acme Corp",
"workspace_domain": "acme-corp",
"owner_email": "jane@acme.com",
"subscription_status": "lifetime",
"is_lifetime": true,
"ltd_platform": "appsumo",
"ltd_codes_count": 2,
"ltd_tier": "lifetime_tier2",
"tier_name": "Lifetime Tier 2",
"agency_id": "agency-abc123"
}

Note: ltd_codes_count reflects the total number of codes applied after the stack, not just the new code.


workspace.lifetime_restored

Fired when a lifetime workspace is restored to its lifetime status after a paid subscription upgrade was cancelled. The workspace reverts to its original lifetime tier.

"data": {
"workspace_id": 1042,
"workspace_name": "Acme Corp",
"workspace_domain": "acme-corp",
"owner_email": "jane@acme.com",
"subscription_status": "lifetime",
"is_lifetime": true,
"tier_name": "Lifetime Tier 1",
"agency_id": "agency-abc123"
}

subscription.upgraded

Fired when a workspace moves to a higher subscription tier.

"data": {
"workspace_id": 1042,
"workspace_name": "Acme Corp",
"workspace_domain": "acme-corp",
"owner_email": "jane@acme.com",
"subscription_status": "active",
"is_lifetime": false,
"old_tier": "Starter",
"tier_name": "Professional",
"billing_cycle": "monthly",
"agency_id": "agency-abc123"
}

subscription.downgraded

Fired when a workspace moves to a lower subscription tier.

"data": {
"workspace_id": 1042,
"workspace_name": "Acme Corp",
"workspace_domain": "acme-corp",
"owner_email": "jane@acme.com",
"subscription_status": "active",
"is_lifetime": false,
"old_tier": "Professional",
"tier_name": "Starter",
"billing_cycle": "monthly",
"agency_id": "agency-abc123"
}

subscription.cancelled

Fired when a subscription is cancelled. The workspace will continue to function until period_end.

"data": {
"workspace_id": 1042,
"workspace_name": "Acme Corp",
"workspace_domain": "acme-corp",
"owner_email": "jane@acme.com",
"subscription_status": "cancelled",
"is_lifetime": false,
"tier_name": "professional",
"period_end": "2026-04-01T00:00:00Z",
"agency_id": "agency-abc123"
}

Field

Description

period_end

The date the workspace will lose access — end of the current billing period

is_lifetime

If true, this workspace had a paid upgrade on top of a lifetime deal and cancelled only the paid portion — it will automatically restore to its lifetime tier at period_end


subscription.reactivated

Fired when a previously cancelled subscription is reactivated before it fully lapses.

"data": {
"workspace_id": 1042,
"workspace_name": "Acme Corp",
"workspace_domain": "acme-corp",
"owner_email": "jane@acme.com",
"subscription_status": "active",
"is_lifetime": false,
"tier_name": "professional",
"agency_id": "agency-abc123"
}

subscription.payment_failed

Fired when a subscription payment attempt fails. The retry_count field indicates how many times payment has been attempted for this billing cycle.

"data": {
"workspace_id": 1042,
"workspace_name": "Acme Corp",
"workspace_domain": "acme-corp",
"owner_email": "jane@acme.com",
"subscription_status": "past_due",
"is_lifetime": false,
"amount": 49.00,
"currency": "USD",
"invoice_id": "INV-00892",
"retry_count": 1,
"agency_id": "agency-abc123"
}

user.registered

Fired when a new user completes registration under your agency.

"data": {
"user_id": 204,
"user_email": "jane@acme.com",
"user_name": "Jane Smith",
"agency_id": "agency-abc123"
}

user.deleted

Fired when a user exercises their right to deletion and their account is removed.

"data": {
"user_id": 204,
"user_email": "jane@acme.com",
"user_name": "Jane Smith",
"agency_id": "agency-abc123"
}

team.member_invited

Fired when an agency owner invites someone to join the agency team, or directly adds an existing user.

"data": {
"agency_id": "agency-abc123",
"agency_name": "My Agency",
"invitee_email": "colleague@acme.com",
"role": "member",
"invited_by_email": "owner@agency.com"
}

team.member_joined

Fired when an invited user accepts their invitation and joins the agency team.

"data": {
"agency_id": "agency-abc123",
"agency_name": "My Agency",
"invitee_email": "colleague@acme.com",
"role": "member",
"invited_by_email": "owner@agency.com"
}

team.member_removed

Fired when a team member is removed from the agency.

"data": {
"agency_id": "agency-abc123",
"agency_name": "My Agency",
"invitee_email": "colleague@acme.com",
"role": "member",
"invited_by_email": "owner@agency.com"
}

Event Quick Reference

Event

When it fires

workspace.created

A new workspace is created

workspace.deleted

A workspace is permanently deleted

workspace.suspended

A workspace is suspended

workspace.reactivated

A suspended workspace is reactivated

workspace.transfer_completed

A workspace ownership transfer completes

workspace.trial_started

A new workspace enters its trial period

workspace.trial_expiring

A trial is 3 days from expiring (fires daily)

workspace.trial_expired

A trial period ends without conversion

workspace.trial_extended

A trial period is extended

workspace.ltd_code_redeemed

A lifetime deal code is redeemed for the first time

workspace.ltd_code_stacked

An additional lifetime deal code is stacked

workspace.lifetime_restored

A lifetime workspace is restored after a paid sub cancellation

subscription.upgraded

A workspace moves to a higher tier

subscription.downgraded

A workspace moves to a lower tier

subscription.cancelled

A subscription is cancelled

subscription.reactivated

A cancelled subscription is reactivated

subscription.payment_failed

A payment attempt fails

user.registered

A new user registers

user.deleted

A user account is deleted

team.member_invited

A team member is invited or directly added

team.member_joined

An invited team member accepts their invite

team.member_removed

A team member is removed from the agency


Common Use Cases

Goal

Events to subscribe

Sync new customers to your CRM

workspace.created, user.registered

Automate trial nurture emails

workspace.trial_started, workspace.trial_expiring, workspace.trial_expired, workspace.trial_extended

Trigger dunning workflows

subscription.payment_failed, subscription.cancelled

Update billing records on plan changes

subscription.upgraded, subscription.downgraded, subscription.reactivated

Manage lifetime deal workspaces

workspace.ltd_code_redeemed, workspace.ltd_code_stacked, workspace.lifetime_restored

Audit team access in your own system

team.member_invited, team.member_joined, team.member_removed

Deprovision accounts on deletion

workspace.deleted, user.deleted

Build direct workspace links

Any workspace event — use workspace_domain to construct https://{workspace_domain}.dashboardfox.app


Tips & Best Practices

Always respond quickly. Return a 200 status as soon as you receive the request, then process the payload asynchronously. If your handler takes longer than 10 seconds, DashboardFox will treat it as a timeout and retry.

Check the test flag. Deliveries sent via the Test button have "test": true set. Filter these out in your production logic if needed.

Use signature verification. Always configure a signing secret and verify the X-Webhook-Signature header to confirm deliveries are genuinely from DashboardFox.

Make your handler idempotent. Due to retries, your endpoint may occasionally receive the same event more than once. Design your logic to handle duplicate deliveries gracefully.

Use workspace_domain to build links. Every workspace event includes workspace_domain — the unique subdomain slug for that workspace. You can use it to construct a direct link: https://{workspace_domain}.dashboardfox.app.

Use the delivery log for debugging. If your automation isn't triggering as expected, open the endpoint in the Webhooks tab and check the delivery log. The full request payload and your endpoint's response body are both visible there.