← All documentationContents ↓

API Reference: Webhooks

Subscribe to event notifications via webhook subscriptions, verify signatures, and inspect delivery logs.

Overview

Webhooks notify your application when matching record events occur in Simply360. Register an HTTPS target URL, choose an event type, and Simply360 sends a signed HTTP POST request to that URL whenever a matching event is processed. Generic webhook management requires the TEAM_SETTINGS feature permission on your API key or token. Action Tags lifecycle webhooks are intentionally excluded from these routes and must use /v1/action-tags/webhooks, which enforces the Action Tags feature permission and signed-webhooks plan capability.

Key Concepts

  • Subscription — A webhook subscription registers one target URL for one event type. The target must be a public HTTPS URL.
  • Filter criteria — Optional JSON used to scope deliveries. For record events, use filterCriteria.dataCollectionIds with DataCollection simplyId values.
  • Delivery — Every matching event triggers an HTTP POST with a JSON body. Attempts are retried on failure (default: 3 retries, 60 seconds apart, 30-second timeout — all configurable per subscription) and every attempt is logged.
  • HMAC signature — Deliveries include a signature header, X-S360-Signature by default. Verify it against the raw request body before trusting the payload.

Available Event Types

EventTriggered When
DATA_RECORD_CREATEDA data record is created.
DATA_RECORD_UPDATEDA data record is updated.
DATA_RECORD_DELETEDA data record is deleted.
DATA_RECORDS_BULK_SYNCEDA bulk create, update, delete, or sync operation completes and emits one summary event.

All four event types support the same collection filter shape:

{
  "dataCollectionIds": ["XXXX-XXXX-XXXX"]
}

Despite the legacy key name dataCollectionIds, the values are DataCollection simplyId strings. Callers with full data access can create unfiltered record webhooks; other callers must filter to collections they can read.

Create a Webhook

Register a public HTTPS target URL for one event type. Simply360 rejects localhost, private network, link-local, internal, and reserved IP targets, including hostnames that resolve to those ranges. The response includes secret, the per-webhook signing secret. Store it immediately; it is returned only at creation time. Optional body properties retryCount, retryDelaySeconds, and timeoutSeconds tune delivery behavior.

TypeScript SDK

const webhook = await s360.webhooks.create({
  eventType: 'DATA_RECORD_CREATED',
  targetUrl: 'https://example.com/webhooks/simply360',
  filterCriteria: {
    dataCollectionIds: ['XXXX-XXXX-XXXX'],
  },
});

console.log(`Webhook ID: ${webhook.data.id}`);
console.log(`Secret: ${webhook.data.secret}`); // store securely

cURL

curl -s -X POST "https://api.simply360.app/v1/webhooks" \
  -H "Authorization: Bearer $S360_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "eventType": "DATA_RECORD_CREATED",
    "targetUrl": "https://example.com/webhooks/simply360",
    "filterCriteria": {
      "dataCollectionIds": ["XXXX-XXXX-XXXX"]
    }
  }'

List, Update, Delete

PUT accepts targetUrl, filterCriteria, isActive, isPaused, retryCount, retryDelaySeconds, and timeoutSeconds.

await s360.webhooks.list();
await s360.webhooks.get('WHKS-1234-ABCD');

await s360.webhooks.update('WHKS-1234-ABCD', {
  targetUrl: 'https://example.com/webhooks/simply360/v2',
});

await s360.webhooks.delete('WHKS-1234-ABCD');

Verify Webhook Signatures

Always verify the X-S360-Signature header before trusting a webhook payload. The signature is the hex-encoded HMAC-SHA-256 of the raw request body using the secret returned at creation.

import crypto from 'crypto';

function verifyWebhook(rawBody: string, signature: string, secret: string): boolean {
  if (!/^[0-9a-f]{64}$/i.test(signature)) return false;
  const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
  return crypto.timingSafeEqual(Buffer.from(signature, 'hex'), Buffer.from(expected, 'hex'));
}

app.post('/webhooks/simply360', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.header('x-s360-signature') ?? '';
  const rawBody = (req.body as Buffer).toString('utf8');

  if (!verifyWebhook(rawBody, signature, process.env.S360_WEBHOOK_SECRET!)) {
    return res.status(401).send('Invalid signature');
  }

  const payload = JSON.parse(rawBody);
  console.log(`Received ${payload.eventType}:`, payload.data);
  res.status(200).send('OK');
});

Delivery Headers

HeaderDescription
Content-Typeapplication/json
X-Simply360-Event-IdUnique delivery event ID. Use this for idempotency.
X-Simply360-Event-TypeSame value as eventType in the body.
X-Simply360-TimestampISO 8601 delivery timestamp.
X-S360-SignatureDefault signature header. Some legacy subscriptions may use a custom signature header configured on the subscription.

Delivery Logs

GET /v1/webhooks/{webhookSimplyId}/deliveries lists delivery attempts (newest first, limit/offset paginated). Each entry includes eventType, deliveryStatus, attemptNumber, responseStatus, responseTimeMs, errorMessage, and timing fields. Stored response body diagnostics are capped at 4,096 characters. The subscription itself tracks totalDeliveries, successfulDeliveries, failedDeliveries, and lastDeliveryStatus.

const deliveries = await s360.webhooks.listDeliveries('WHKS-1234-ABCD');

for (const delivery of deliveries.data) {
  console.log(`${delivery.eventOccurredAt}: ${delivery.deliveryStatus} (HTTP ${delivery.responseStatus})`);
}
curl -s "https://api.simply360.app/v1/webhooks/WHKS-1234-ABCD/deliveries" \
  -H "Authorization: Bearer $S360_API_KEY"

Usage Notes

  • Your endpoint must respond with a 2xx status. Non-2xx responses or network failures are treated as failed deliveries and retried per the subscription's retry settings.
  • Simply360 does not follow redirects when delivering webhooks. A 3xx response is recorded as a failed delivery; update the subscription's targetUrl to the final endpoint instead.
  • Use the complete X-Simply360-Event-Id or body eventId as an idempotency key. Most events use a CHAR-14 ID and retain the general at-least-once delivery contract. WIZARD_COMPLETED uses the stable key wizard:<dataWizardInstanceSimplyId>:completion:v1 and the narrower durable-claim contract below.
  • A Wizard-completion claim performs the subscription's bounded HTTP retries in one owned delivery attempt. Queue redelivery cannot start another attempt after a claim exists. An ambiguous durable pending claim is not retried automatically, because the receiving endpoint may already have accepted it; reconcile that case using the stable event ID before initiating any manual delivery.
  • Pause or re-enable a webhook by setting isPaused / isActive with PUT /v1/webhooks/{webhookSimplyId}.
  • Store the webhook secret securely. If lost, delete and recreate the webhook.
  • See Webhook Event Payloads for the delivery body schema.