Gareth Wilson Gareth Wilson

Guide to Swoogo Webhooks: Features and Best Practices

Published


Swoogo webhooks keep your other systems in step with your events. When someone registers, a registrant's details change, a session is added to the agenda, a speaker or sponsor is updated, or attendance is recorded, Swoogo can call your endpoint with the record's data. Event teams use this to sync registrations to a CRM or marketing platform, feed badge printing and access control, and update finance systems from registrant line items.

This guide covers how Swoogo webhooks are triggered and configured, the two payload formats, how to authenticate requests with a custom header, and the gaps in Swoogo's documentation that a receiver should allow for.

What are Swoogo webhooks?

Each Swoogo webhook watches one kind of record, its trigger object, and fires when records of that kind are inserted, updated or deleted. You can narrow it to specific events, to changes in particular fields, and to changes made by public users, admins or API calls. Webhooks are organised into webhook groups, and you can manage them in the Swoogo UI or through the API.

You also choose what the request carries: Swoogo's default fields or the fields you select, sent as JSON or as a form-encoded body.

Swoogo doesn't sign webhooks. Its documentation describes custom headers "for authentication with your receiving endpoint": you add a header such as Authorization with a secret value, and your receiver compares it.

Swoogo webhook features

FeatureDetails
ConfigurationSwoogo UI, or POST /webhooks/create and related endpoints in the Swoogo API
OrganisationWebhook groups
Trigger objectsevent, contact, registrant, speaker, sponsor, session, session_attendance, registrant_line_item
Operationstrigger_on_insert, trigger_on_update, trigger_on_delete
Origin filterspublic_user, admin_user, api_call
Other filtersevent_ids, changed fields, all_fields, any_event
Payload fieldsuse_default_fields, post_field_data, custom_fields
Formatjson (application/json) or post (form-encoded)
AuthenticationCustom headers (headers key/value pairs). No signature
SuccessAny 2xx
RetriesFailed deliveries are retried. Count, schedule and timeout not stated
Payload schemaNot documented

Triggers and payloads

Trigger objectWhat it covers
eventThe events themselves: conferences, meetings, webinars
contactPeople in your Swoogo CRM, independent of any event
registrantPeople registered for an event, with status, package, sessions and fees
speakerPresenters linked to sessions
sponsorSponsoring organisations
sessionAgenda items: talks, workshops, panels
session_attendanceAttendance records for sessions
registrant_line_itemItemised fees, taxes and discounts on registrant orders

Combine a trigger object with the insert, update and delete flags to get what other platforms would call event types, such as "registrant updated".

Swoogo doesn't publish a delivered webhook body, and doesn't document a field that names the trigger object or the operation. What arrives depends on the fields you choose. Two practical consequences:

  • Give each webhook its own URL path, such as /webhooks/swoogo/registrant, so you know the trigger object before you parse anything.
  • Capture one delivery per webhook before writing the handler, and include an ID field in what you send so you can deduplicate.

Inspect Swoogo webhooks as they arrive. Point a webhook at a Hookdeck Inspect URL to see the exact body, content type and headers Swoogo sends for your field selection, with no account or setup required.

Setting up Swoogo webhooks

Pick a header name, usually Authorization, and generate a long random value, for example with openssl rand -hex 32.

  1. Create a webhook in the Swoogo UI, or with the API.
  2. Set the URL, the trigger object, and which of insert, update and delete should fire it.
  3. Narrow it with event IDs, changed fields or origin flags if you only need some changes.
  4. Choose the fields to send and the content type, json or post.
  5. Add a custom header with your secret value, for example Authorization: Bearer <your random value>.
  6. Enable the webhook.

With the API, you get a bearer token through OAuth 2.0 client credentials at POST /oauth2/token, then call POST https://api.swoogo.com/api/v1/webhooks/create with a form-encoded body. Swoogo's own example:

curl -X POST "https://api.swoogo.com/api/v1/webhooks/create" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "name=Registrant Updates&webhook_group_id=1&trigger_object=registrant&trigger_on_insert=1&trigger_on_update=1&url=https://example.com/webhooks/registrant&content_type=json"

Custom headers are a headers array of key and value pairs on the webhook; Swoogo added API management of webhook headers in December 2024.

To route through Hookdeck, create a source with the Swoogo type and use its URL as the webhook URL. Enter the header name in API Key Header and the exact value, including any Bearer prefix, in API Key. Hookdeck finds the header case-insensitively and compares the whole value. The source pre-selects POST but doesn't manage the method. Swoogo's create-webhook summary mentions an HTTP method, but its schema has no method field, so check the method on your first delivery and, if it isn't POST, allow that method on the source too.

Securing Swoogo webhooks

Look up your header and compare its whole value with the stored secret, in constant time. Parse the body according to its content type, because a webhook can send JSON or form fields.

const crypto = require('crypto');
const express = require('express');

const HEADER_NAME = (process.env.SWOOGO_WEBHOOK_HEADER || 'authorization').toLowerCase();
const EXPECTED = process.env.SWOOGO_WEBHOOK_SECRET; // e.g. "Bearer 3f9c..."

function safeEqual(received, expected) {
  const a = Buffer.from(received, 'utf8');
  const b = Buffer.from(expected, 'utf8');
  // timingSafeEqual throws on unequal lengths, so check first.
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

function parseBody(req) {
  const text = req.body.toString('utf8');
  if ((req.get('content-type') || '').includes('application/x-www-form-urlencoded')) {
    return Object.fromEntries(new URLSearchParams(text));
  }
  return JSON.parse(text);
}

const app = express();

app.post('/webhooks/swoogo/:object', express.raw({ type: '*/*' }), (req, res) => {
  const received = req.get(HEADER_NAME);
  if (!EXPECTED || typeof received !== 'string' || !safeEqual(received, EXPECTED)) {
    return res.sendStatus(401);
  }

  let record;
  try {
    record = parseBody(req);
  } catch {
    return res.sendStatus(400);
  }

  // req.params.object tells you the trigger object. Queue the record and respond.
  res.sendStatus(200);
});

The same check in Python:

import hmac
import os

HEADER_NAME = os.environ.get("SWOOGO_WEBHOOK_HEADER", "Authorization")
EXPECTED = os.environ.get("SWOOGO_WEBHOOK_SECRET", "")


def verify_swoogo_request(headers) -> bool:
    if not EXPECTED:
        return False  # never accept when unconfigured
    received = headers.get(HEADER_NAME)  # most frameworks look headers up case-insensitively
    if not received:
        return False
    # Compare bytes: compare_digest raises TypeError on a non-ASCII str.
    return hmac.compare_digest(received.encode("utf-8"), EXPECTED.encode("utf-8"))

A static header authenticates the sender, not the body. It cannot detect a modified payload, and with no signature or timestamp a captured request can be replayed and still pass. Registrant data is personal data, so serve the endpoint over HTTPS only, use a long random value, and update the header and your receiver together if it might have leaked.

Make Swoogo webhooks production-ready. Hookdeck Event Gateway checks your custom header, accepts JSON and form-encoded bodies alike, and queues every registration change durably for your service.

Swoogo webhook limitations and pain points

No documented payload or event field

The Problem: You can't tell from the request alone which kind of record changed, or whether it was inserted, updated or deleted.

Why It Happens: Swoogo documents how to configure webhooks but not the body it sends, which depends on the fields you choose.

Workarounds:

  • Use one URL path per webhook so the path identifies the trigger object.
  • Create separate webhooks for insert, update and delete if your handler needs to tell them apart.
  • Capture a real delivery for each webhook and keep it as a test fixture.

How Hookdeck Can Help: Every request is stored with its full body and headers in Hookdeck, and filters can route on the path or on fields you find in the body.

Two content types

The Problem: A handler that assumes JSON fails on a webhook someone set to post.

Why It Happens: Each webhook chooses json or form-encoded delivery.

Workarounds:

  • Standardise on json for new webhooks.
  • Parse by Content-Type so either format works.

How Hookdeck Can Help: Hookdeck accepts both content types, and a transformation can turn form fields into JSON before delivery so your service handles one format.

Retries without details

The Problem: You can't size your downtime tolerance or know when Swoogo gives up.

Why It Happens: Swoogo says a failed delivery is retried, but not how many times, how far apart, or what timeout it uses.

Workarounds:

  • Respond with a 2xx as soon as the record is stored.
  • Reconcile registrations against the Swoogo API after incidents.

How Hookdeck Can Help: Hookdeck stores each request as it arrives and retries delivery to your service on your schedule, and you can replay any request after a fix.

Busy events, busy webhooks

The Problem: Registration opens, or an admin bulk-edits sessions, and your endpoint is flooded.

Why It Happens: Every insert or update on the trigger object fires the webhook, including changes made through the API.

Workarounds:

  • Use event_ids, changed-field filters and the public_user, admin_user and api_call flags to fire only on changes you care about.

How Hookdeck Can Help: Hookdeck absorbs the burst and delivers at a max delivery rate your service can handle. Hookdeck answers Swoogo with a 200 even when the header is wrong, but such requests are recorded as failed verification and never reach your service.

Best practices

One webhook, one path

Encode the trigger object, and the operation if you need it, in the URL path.

Send what you need, including an ID

Choose fields deliberately and always include the record's ID, so you can deduplicate and fetch more from the API. See our guide to webhook idempotency.

Prefer JSON

Use content_type=json unless a target can only take form posts.

Avoid loops

If your receiver writes back to Swoogo through the API, turn off api_call on the webhook, or your own updates will fire it again.

Acknowledge fast and process asynchronously

Store the record and respond, then sync other systems. See why to process webhooks asynchronously.

Conclusion

Swoogo webhooks fire on inserts, updates and deletes of one trigger object and send the fields you choose as JSON or form data, authenticated only by a custom header. Compare that header in constant time, give each webhook its own path, include an ID in the payload, and capture a real delivery before you build, because Swoogo documents neither the body nor its retry schedule.

Hookdeck Event Gateway checks the header, handles both content types, and keeps every change until your service has processed it.

Get started with Hookdeck for free and handle Swoogo webhooks reliably in minutes.


Gareth Wilson

Gareth Wilson

Product Marketing

Multi-time founding marketer, Gareth is PMM at Hookdeck and author of the newsletter, Community Inc.