ResponseDock Docs
Menu

Webhooks

Webhooks let ResponseDock push real-time survey events to your systems — CRM, data warehouse, notification service, or custom backend.

Creating a Webhook

  1. Go to Workspace → Webhooks
  2. Click + Add Endpoint
  3. Enter the HTTPS URL that will receive events
  4. Select which events to subscribe to
  5. Save — ResponseDock generates a signing secret

Supported Events

EventWhen it fires
survey.response.startedA respondent starts a survey response
survey.response.completedA respondent completes and submits a survey
survey.response.hiddenAn admin hides a response from analytics and exports
survey.response.restoredAn admin restores a hidden response
survey.response.deletedAn admin permanently deletes a response
survey.responses.generatedAn admin generates sample responses
survey.attachment.deletedAn admin permanently deletes a file uploaded with a response
survey.createdA survey is created
survey.updatedSurvey metadata is changed
survey.publishedA draft survey version is published
survey.pausedA live survey is paused
survey.closedA survey is closed
survey.archivedA survey is archived
survey.deletedA survey is permanently deleted
template.createdA survey template is created
template.deletedA survey template is deleted
workspace.member.invitedSomeone is invited to the workspace
workspace.member.joinedAn invited person joins the workspace
workspace.member.role_changedA member's role changes
workspace.member.removedA member is removed from the workspace
workspace.invitation.revokedA pending invitation is revoked
api_key.createdAn API key is created (the key itself is never sent)
api_key.rotatedAn API key is rotated
api_key.revokedAn API key is revoked
trigger.createdA targeting trigger is created
trigger.updatedA targeting trigger is changed
trigger.deletedA targeting trigger is deleted
insight.createdAn insight is recorded manually or from an AI draft
insight.updatedAn insight is changed

Payload Structure

{
  "event": "survey.response.completed",
  "data": {
    "workspaceId": "ws_xxxx",
    "surveyId": "survey_yyyy",
    "surveySlug": "customer-feedback",
    "responseId": "resp_aaaa",
    "completedAt": "2024-11-14T10:30:00Z",
    "isAnonymous": false,
    "answers": { "count": 1 },
    "source": {
      "channel": "sdk",
      "campaignId": null,
      "utmSource": "email",
      "referrer": null
    },
    "followUpEmail": "[redacted]",
    "quiz": { "score": 8, "maxScore": 10, "scorePercent": 80, "passed": true, "pendingReview": 0 }
  }
}

Webhook payloads carry only the number of answers, never the answers themselves, and email addresses and tokens are redacted. For quizzes, identified responses also carry a quiz score summary (passed is null without a passing score or while answers await review); it never includes the answer key, and anonymous responses get no score. Fetch the response through the Responses API when you need the answers.

Signature Verification

Every request includes X-ResponseDock-Event, X-ResponseDock-Timestamp, and X-ResponseDock-Signature headers. Verify the signature to confirm the request comes from ResponseDock:

import crypto from 'crypto';

function verifySignature(payload: string, signature: string, secret: string): boolean {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signature.replace('sha256=', ''))
  );
}

Prefer X-ResponseDock-Signature-V2, which also covers the timestamp so you can reject replayed requests: it is sha256= followed by the hex HMAC-SHA256 of `${timestamp}.${body}`, keyed with the signing secret, where timestamp is the X-ResponseDock-Timestamp header (milliseconds since the epoch).

import { createHmac, timingSafeEqual } from 'node:crypto';

function verifySignatureV2(rawBody: string, headers: Record<string, string>, secret: string): boolean {
  const timestamp = headers['x-responsedock-timestamp'];
  if (Math.abs(Date.now() - Number(timestamp)) > 5 * 60_000) return false; // too old
  const expected = 'sha256=' + createHmac('sha256', secret).update(`${timestamp}.${rawBody}`).digest('hex');
  const received = headers['x-responsedock-signature-v2'] ?? '';
  return received.length === expected.length && timingSafeEqual(Buffer.from(received), Buffer.from(expected));
}

Retry Policy

If your endpoint returns a non-2xx status, ResponseDock retries with exponential back-off:

AttemptDelay
1st retry5 minutes
2nd retry15 minutes
3rd retry1 hour
4th retry3 hours
5th retry6 hours
After 5th failureDelivery marked failed; no further retries

Delivery Logs

Every delivery attempt is logged. View logs in Workspace → Webhooks → [endpoint] → Delivery Log to see the request body, response status, and next retry time.

Testing Webhooks Locally

# Use ngrok or a similar tunnel
ngrok http 3000

# Use the public URL as your webhook endpoint in the dashboard
# https://abc123.ngrok.io/webhooks/responsedock