ResponseDock Docs
Menu

Embedding a survey builder for your customers

This guide puts a white-label survey builder inside your own product, so each of your customers (a club, a school, a customer account) creates, publishes and reviews their own questionnaires without leaving your app or signing up for ResponseDock. It covers the server route, the iframe, keeping track of what your customers create, and showing the published surveys to their end users. For every option, see Embedding ResponseDock.

ℹ️

The embedded workspace, the SDK, API keys, webhooks and registration forms need the Pro plan, which you can try free for 14 days without a card. Full white-labeling, including a custom domain, is on the Business plan.

Before You Start

  1. Create a secret API key (sk_live_…) for your workspace and keep it on your server only.
  2. Add your app's origin, for example https://app.yourplatform.com, under Allowed embed origins in Workspace Settings.

Step 1: Request an Embed URL on Your Server

Add a route to your backend that your signed-in customer's browser calls. It asks ResponseDock for a short-lived embed URL with the secret key. Everything in the body is signed into the token, so the browser cannot change the customer, the permissions or the theme.

// GET /api/survey-builder (your backend; requires your own session)
app.get('/api/survey-builder', async (req, res) => {
  const account = req.user.account; // the customer this user manages

  const response = await fetch('https://api.responsedock.com/api/v1/auth/embed-token', {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.RESPONSEDOCK_SECRET_KEY}`, // sk_live_…
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      workspaceId: process.env.RESPONSEDOCK_WORKSPACE_ID,
      role: 'admin',
      mode: 'workspace',
      allowedOrigin: 'https://app.yourplatform.com',
      tenant: { id: account.id, name: account.name },
      user: { id: req.user.id, name: req.user.name },
      capabilities: [
        'questionnaire:create',
        'questionnaire:edit',
        'questionnaire:publish',
        'results:view'
      ],
      subject: { type: 'account', id: account.id, label: account.name },
      theme: {
        brandName: 'Your Platform',
        logoUrl: 'https://app.yourplatform.com/logo.svg',
        primaryColor: '#0F766E',
        fontFamily: 'Inter, sans-serif',
        hidePlatformBranding: true
      }
    })
  });

  if (!response.ok) return res.status(502).json({ error: 'Survey builder unavailable' });
  const { embedUrl } = await response.json(); // { token, expiresIn, embedUrl }
  res.json({ embedUrl });
});
  • capabilities: grant only what the user needs. Choose from questionnaire:view, questionnaire:create, questionnaire:edit, questionnaire:publish and results:view; create, edit and publish include view. The iframe hides actions the session lacks, and the API refuses them.
  • subject (optional): the record the questionnaires belong to, such as a club or a customer account. A session with a subject sees only questionnaires with the same type and id, and new questionnaires are filed under it automatically.
  • theme (optional): your colors, font and logo, so the builder looks like part of your product. See White-labeling.

The token in embedUrl expires after 5 minutes, so request a new URL each time the page loads instead of storing it.

Step 2: Add the Iframe and Resize It

<iframe
  id="survey-builder"
  title="Survey builder"
  src="EMBED_URL_FROM_YOUR_BACKEND"
  width="100%"
  height="720"
  style="border:0; width:100%;"
  allow="clipboard-write"
></iframe>

The builder reports its height so the frame can grow with its content. Accept messages only from the ResponseDock origin and from this iframe:

const frame = document.getElementById('survey-builder');

window.addEventListener('message', (event) => {
  if (event.origin !== 'https://app.responsedock.com') return;
  if (event.source !== frame.contentWindow) return;

  if (event.data?.type === 'responsedock.lifecycle.resize') {
    frame.style.height = `${event.data.payload.height}px`;
  }
});

Step 3: Store the Questionnaires Your Customers Create

When a user creates a questionnaire, the iframe posts responsedock.questionnaire.created with the new questionnaire and the signed tenant and subject. Save its ID against the customer account so you can show the questionnaire to their end users later.

window.addEventListener('message', async (event) => {
  if (event.origin !== 'https://app.responsedock.com') return;
  if (event.source !== frame.contentWindow) return;

  if (event.data?.type === 'responsedock.questionnaire.created') {
    const { id, title } = event.data.questionnaire;
    await fetch('/api/questionnaires', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ questionnaireId: id, title })
    });
  }

  if (event.data?.type === 'responsedock.questionnaire.published') {
    // event.data.surveyId is now live: offer it to the account's end users.
  }
});

Treat these messages as a convenience for your UI. Your backend should record the questionnaire for the account of the signed-in user, not for an account named in the message. To be notified on the server instead, use webhooks.

Step 4: Show Published Surveys to End Users

End users (members, students, customers) answer the questionnaires in a respondent embed. Request it from your server with the questionnaire IDs you stored and your own ID for the user:

POST https://api.responsedock.com/api/v1/auth/embed-token
Authorization: Bearer sk_live_your_secret_key
Content-Type: application/json

{
  "workspaceId": "ws_xxxx",
  "role": "respondent",
  "allowedSurveyIds": ["questionnaire_id_stored_in_step_3"],
  "externalUserId": "member_12345",
  "allowedOrigin": "https://app.yourplatform.com"
}

The returned embedUrl opens a list of just those surveys, with what this user has already completed. To collect sign-ups with registration fields your backend reads back, see Registration Forms.

Checklist

  • The secret key is only used on your server.
  • allowedOrigin is listed in the workspace's allowed embed origins.
  • Each session gets only the capabilities, subject and survey IDs its user should have.
  • Your message listener checks both event.origin and event.source.
  • You use stable, non-sensitive IDs for externalUserId.