dhango
dhango.
Embedded Sign-Up · How-To

Embed the dhango sign-up flow inside your own site.

dhango's merchant onboarding runs inside an iframe on your page — inline or as a modal — styled to match your brand and driven by a small JavaScript SDK. Your platform pre-fills what it already knows, controls which fields and steps the applicant sees, and gets a webhook the moment onboarding finishes.

Your backend creates a session → Your page embeds the flow → Events stream to your page → Webhook on completion

Try every capability in the interactive test harness

Toggle features, restyle the flow live, drive navigation from the host page, watch the postMessage traffic in real time, and copy the exact embed code for your configuration.

Open the test harness →

Integrate in three steps

1

Create a session (server-side)

POST /v1/applicationSessions with your API key. Pre-fill contact and organization data, lock fields read-only, skip steps, and set a theme — the session's configuration is authoritative.

2

Embed with the SDK

Load embed.js from the sign-up origin and call Dhango.signup(...) with the session id — inline in a container or as a modal overlay. The iframe sizes itself and closes itself on completion.

3

Listen and react

Typed events stream to your callbacks (steps, validation, completion — identifiers only, never applicant data). Your webhook endpoint is notified when onboarding finishes, before account activation.

<!-- Load the SDK from the sign-up origin, then: -->
var signup = Dhango.signup({
  container: '#signup-slot',          // or mode: 'modal'
  sessionId: '<from POST /v1/applicationSessions>',
  config: { theme: { primaryColor: '#0057ff', fontFamily: 'Poppins' } },
  onComplete: (e) => showThankYou(e.sessionId),
});

Get the SDK

<!-- Sandbox -->
<script src="https://signup.dhangodemo.com/embed.min.js"></script>

<!-- Production -->
<script src="https://signup.dhango.com/embed.min.js"></script>
Always load the SDK from the sign-up origin. It deploys together with the flow it talks to, so the two can never drift apart — and when loaded from that origin, Dhango.signup(...) needs no url option at all. A readable, unminified build is served at /embed.js for integration and debugging. Don't self-host or bundle a copy: the SDK is evergreen, and hosts can feature-detect via the capabilities array on the dhango:ready event.

Register your embed origins

1

Why this is required

The sign-up flow only allows framing from origins your platform has registered — they become the CSP frame-ancestors allowlist on the sign-up host. Until at least one origin is registered, browsers refuse to render the iframe anywhere.

2

Find the panel

In the dhango dashboard, open Accounts → your platform account → the API Credentials tab, and scroll to the Embedded Sign Up Origins panel (just below Webhook Integration). You'll need a dashboard user with permission to manage the platform's API credentials.

3

Add your origins

Click Edit, add each origin, and save. Changes reach the sign-up host within about five minutes — no deploy on either side. Removing every origin locks framing down again.

Origin rules: values must be bare origins — scheme, host, optional port; no path, query, or trailing content. https only, except http://localhost / http://127.0.0.1 for local development. Up to 10 origins per platform. A wildcard subdomain entry like https://*.partner.com matches subdomains at any depth — but the * is only valid as the entire leading label (https://*partner.com is rejected), and it never matches the apex: list https://partner.com itself separately if you embed there. Scheme and port must still match exactly. The panel validates entries as you add them and normalizes (lowercases, deduplicates) the list on save.

Consume the completion webhook

1

Register your endpoint

In the dhango dashboard — Accounts → your platform account → API Credentials tab → Webhook Integration panel — set your platform's webhook URL: a single HTTPS endpoint that receives all webhook events for your platform. There are no per-event subscriptions; dispatch on the payload's Type.

2

Generate the signing secret

Generate the HMAC key from the same panel. It's created server-side (never chosen by you) and stays retrievable there for authorized dashboard users — store it with your other secrets. Every delivery is signed with it so you can prove the POST came from dhango.

3

Verify, then act

Each POST carries AccountKey, Timestamp, and HMACSignature headers. Recompute the signature over the raw body, check freshness, then handle the event. Onboarding-finished is Type: "ApplicationSubmitted".

// Node/Express example — verify HMAC-SHA256 over "{Timestamp}.{rawBody}" (base64)
const crypto = require('crypto');

app.post('/webhooks/dhango', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.header('HMACSignature') || '';
  const timestamp = req.header('Timestamp') || '';

  const expected = crypto
    .createHmac('sha256', process.env.DHANGO_WEBHOOK_SECRET)
    .update(`${timestamp}.${req.body}`)     // sign the timestamp + RAW body, dot-joined
    .digest();                              // raw bytes; header carries base64

  // HMAC-SHA256 in base64 is exactly 44 chars. Enforce canonical base64 by regex
  // BEFORE decoding — Buffer.from(sig, 'base64') silently skips invalid characters,
  // so a mangled header could otherwise decode to the same bytes and pass.
  const canonical = /^[A-Za-z0-9+\/]{43}=$/.test(signature);
  const provided = Buffer.from(signature, 'base64');
  const authentic = canonical && provided.length === expected.length &&
    crypto.timingSafeEqual(provided, expected);
  const fresh = Math.abs(Date.now() / 1000 - Number(timestamp)) < 300; // bounds the replay window
  if (!authentic || !fresh) return res.status(401).end();

  const event = JSON.parse(req.body);     // { Type, Data } — PascalCase, string enum
  if (event.Type === 'ApplicationSubmitted') { // onboarding finished
    // event.Data: { ApplicationId, PlatformIdentifier, OrganizationName,
    //               DoingBusinessAs, ContactEmailAddress, SubmitDate }
    // Deliveries can repeat (retries, replays inside the freshness window):
    // make this idempotent — e.g. upsert keyed on (Type, ApplicationId).
    markMerchantOnboarded(event.Data.PlatformIdentifier, event.Data.ApplicationId);
  }
  res.status(200).end();
});
Verification rules that matter: sign the raw request body (parse JSON only after verifying — re-serialized JSON won't match), compare decoded signature bytes with a constant-time equality check, and reject stale timestamps. Freshness only bounds the replay window — durable protection is an idempotent handler, since dhango retries non-2xx deliveries and a captured request stays valid until it goes stale. Data.PlatformIdentifier echoes the identifier you set when creating the session — use it to correlate the webhook back to your own merchant record.

What you control

Theming

Background, card, text, and accent colors, border radius, font size, and any Google Fonts family — applied inside the iframe so the flow blends into your site.

Pre-fill & read-only fields

Send what you already know on the session — contact, organization, tax id, entity type — and mark fields read-only. Locked values are enforced server-side.

Skip steps

Waive steps your platform handles elsewhere. A session with a complete contact can even skip straight past step one.

Host-driven navigation

Hide the flow's own chrome — stepper, Next/Back, language menu, close ✕ — and drive it from your page with promise-returning SDK commands (next(), back(), getState()). With autoClose: false the iframe stays up until you call destroy().

Localization

English, Spanish, German, and French. Pass the language in with the embed or switch it live with setLanguage().

Events & webhooks

A versioned, bidirectional postMessage protocol with strict origin checks, plus HMAC-signed webhooks for onboarding-finished and account activation.

Security model: framing is allowed only for origins your platform has registered (CSP frame-ancestors from a runtime allowlist); every message crosses the boundary with a strict origin check and carries identifiers only — never applicant data. Read-only and skip-step configuration set on the session is enforced by the API, not just the UI.