Plan with AI

Get started →
Guides
Webhooks6 min read

Webhooks Fundamentals

Webhooks let Workvivo notify your integration when supported events happen. Workvivo sends an HTTPS POST request containing the event, and your endpoint accepts and processes it.

The same receiving pattern applies to every webhook event type. This guide focuses on that pattern; use the Webhooks API reference to choose events and inspect their resource-specific payloads.

Webhook deliveries contain live production data. Workvivo does not provide a sandbox or staging environment, and there is no confirmed test-webhook sender. Prepare and secure the endpoint before saving the webhook configuration.

1. Register an endpoint

Webhook management must first be enabled for your organisation. Contact Workvivo if it is not available. Once enabled, a user with the Developer role can open Admin > Administration > Webhook Settings and create a webhook.

Configure:

  • Name: A clear name for the receiving integration.
  • URL: The HTTPS endpoint that accepts Workvivo's POST requests.
  • Authentication: No authentication or OAuth 2.0. With OAuth 2.0, Workvivo obtains an access token from your login URL and sends it in the Authorization header.
  • Custom headers: Optional fixed headers to include with each request.
  • Subscribed events: The event types this endpoint needs. Refer to the Webhooks API reference rather than assuming every event has the same resource data.
  • Content scope: Public content only, or all content. The all-content option can include private, hidden, and other limited-audience content. Chat events require the all-content scope.

Saving the configuration can begin delivery of real events. Confirm that the URL is publicly reachable over HTTPS, applies the authentication configured in Webhook Settings, and can durably queue accepted events before you enable subscriptions.

The public Customer API can list and inspect webhook configurations, but registration itself is performed in Webhook Settings.

2. Receive a delivery

Authentication is controlled by the webhook configuration described above. If you choose OAuth 2.0 or add a custom header, validate the corresponding header before accepting a delivery.

The example below focuses on validating the event envelope and durably queueing the event. Add the authentication handling that matches your Webhook Settings configuration. The webhookQueue import represents your durable queue; replace it with the queue used by your application.

import express from 'express';
import { webhookQueue } from './queue.js';

const app = express();

app.use(express.json());

app.post('/webhooks/workvivo', async (request, response) => {
    const event = request.body;

    if (!event.action || !event.meta?.id) {
        return response.sendStatus(400);
    }

    try {
        await webhookQueue.enqueue({
            id: event.meta.id,
            event,
        });

        return response.sendStatus(204);
    } catch (error) {
        console.error('Failed to queue Workvivo webhook:', error);

        return response.sendStatus(503);
    }
});

app.listen(3000);

Validate the configured authentication and event envelope, then enqueue the body. Return a 2xx response only after the event is durably accepted; slow business logic belongs in a background worker.

3. Read the event envelope

Every event has the same outer building blocks:

  • action identifies the event type, such as document.folder.created or article.published.
  • One or more event-specific keys contain resource data. Their names and fields depend on action.
  • meta describes this delivery invocation.

This example shows the complete envelope for one event type:

{
    "action": "document.folder.created",
    "folder": {
        "id": 88,
        "label": "HR Policies",
        "audience": {
            "is_global": true,
            "spaces": [],
            "teams": []
        },
        "parent_id": null,
        "created_at": "2024-03-15T08:45:00Z",
        "updated_at": "2024-03-15T08:45:00Z"
    },
    "meta": {
        "id": "4m32k5ykq5",
        "attempt": 1,
        "timestamp": 1725887565
    }
}

The metadata fields are:

  • meta.id: The unique ID for this webhook invocation. It remains the same when Workvivo retries the delivery.
  • meta.attempt: The delivery attempt number, starting at 1.
  • meta.timestamp: A Unix timestamp for when Workvivo sent this attempt.

meta.timestamp is a delivery time, not a resource version or a universal event-ordering timestamp. A retry can have a later meta.timestamp while retaining the same meta.id.

4. Design for the delivery contract

Workvivo treats any 2xx status as a successful delivery. A non-2xx response, connection failure, or response timeout makes the attempt unsuccessful.

The delivery behaviour is:

  • Connection timeout: 5 seconds.
  • Response timeout: 5 seconds.
  • Automatic attempts: Up to 5 total: the initial attempt and 4 retries.
  • Retry delays: 5 seconds before attempt 2, 30 seconds before attempt 3, 5 minutes before attempt 4, and 15 minutes before attempt 5.
  • Retry semantics: Treat delivery as at least once. The same event may reach your endpoint more than once.

Keep the synchronous path short: apply the configured authentication, validate the envelope, write the event to a durable queue, then respond with 2xx. If the queue is unavailable, return a non-2xx response so Workvivo can retry.

All 5 automatic attempts can still fail. Monitor exhausted deliveries rather than treating retries as a guarantee that your endpoint successfully processed every event.

Webhook Settings includes delivery logs from the last 30 days for monitoring and debugging.

5. Make processing idempotent

Use meta.id as the deduplication key. Do not deduplicate on the resource ID: different events for the same resource are separate invocations. Do not use meta.attempt: it changes on each delivery attempt.

Enforce uniqueness in durable storage and claim the ID in the same transaction as the event's side effects. If processing fails, roll back both the side effects and the receipt so the event can be retried.

import { database } from './database.js';
import { applyWebhookEvent } from './events.js';

export async function processWebhook(event) {
    await database.transaction(async (transaction) => {
        const claimed = await transaction.webhookReceipts.insertIfAbsent({
            id: event.meta.id,
            action: event.action,
            receivedAt: new Date(),
        });

        if (!claimed) {
            return;
        }

        await applyWebhookEvent(event, transaction);
    });
}

The webhookReceipts.id column should have a unique constraint. An in-memory set is not sufficient because it is lost on restart and is not shared safely across multiple workers.

6. Handle events that arrive out of order

Workvivo does not guarantee event order. For example, an article.published delivery can arrive before article.created for the same article. The envelope has no universal sequence number.

Build each event handler as a state reconciliation step:

  1. Map action to the affected resource type and resource ID.
  2. When the payload includes a resource-level created_at, updated_at, or other meaningful version, compare it with the version already applied.
  3. Do not let an older payload overwrite newer stored state. Treat deletion or archival as a tombstone when that matches your integration's data model.
  4. When the payload has no reliable version, or the action is ambiguous after reordering, read the resource's current state from the Workvivo REST API before applying a destructive or irreversible change.

Current-state reads use the normal API authentication pattern: a static API key from API Keys & JWT Settings as a Bearer token, plus the Workvivo-Id header. This credential is for outbound API calls from your integration; do not expose it through the webhook endpoint.

Do not order events by meta.timestamp. It records when an attempt was sent and changes across retries, so it cannot tell you which resource state is newest.

Receiver checklist

Before enabling the webhook:

  1. Expose an HTTPS POST endpoint.
  2. Apply the authentication configured in Webhook Settings.
  3. Validate action and meta.id.
  4. Durably enqueue the event before returning 2xx.
  5. Enforce a unique receipt for meta.id.
  6. Make handlers safe for duplicate and out-of-order delivery.
  7. Monitor the delivery logs in Webhook Settings.