Gamma Duo Form & Embed v1
Base URL for this environment use in place of $BASE_URL / <…-deployment-host> in the examples below. Markdown for agents ↓ .md

Gamma Duo Form & Embed Guide

Put the Gamma Duo loan application form on any website with one <script> tag, or link applicants to our hosted form. Either way, applications flow through the same validated pipeline as the Partner API and land with our operations team (and, on final submission, the lender).


1. Quick start

Script tag (any website)

<div id="loan-form"></div>
<script src="$BASE_URL/embed/gamma-duo-form.iife.js"></script>
<script>
  GammaDuoForm.mount("#loan-form", { product: "car" });
</script>

Declarative (auto-mounts on load)

<div data-gamma-duo-form='{"product":"personal"}'></div>
<script src="$BASE_URL/embed/gamma-duo-form.iife.js"></script>

React app

import { GammaDuoForm } from "gamma-duo-form";

<GammaDuoForm product="car" submitUrl="$BASE_URL/api/v1/applications" />

When embedding on a domain other than ours, always pass submitUrl (see §4); the widget derives its other endpoints from it.


2. Options

Option Default Notes
product "car" "car" or "personal"
direction "sectioned" layout style: "sectioned" · "flow" · "focus"
accent "#E8476A" brand accent colour (any CSS colour)
font "Plus Jakarta Sans / Inter" also "Sora / Inter", "Manrope"
mascot "milestones" "off" · "milestones" · "always"
height "auto" "auto" (flows in page), "fill", "640px", or a px number
persist true saves progress in the browser so applicants can resume
initialValues (none) object of form fields to prefill (same vocabulary as §3's hand-off links)
logoUrl / mascotUrl (none) white-label the brand assets
submitUrl /api/v1/applications required for cross-origin embeds: full URL of our intake endpoint
attribution auto captured from the host page URL (?refsource=, utm_*); pass an object to override or false to disable
onStepComplete(stepId, form) / onChange(form) (none) host page hooks (analytics etc.)

The layout responds to the container width, not just the viewport, so the form renders correctly inside a narrow column.

Attribution: add ?refsource=your_id to any page that hosts the widget (or to links pointing at our hosted form) and every application from that visit is tagged with your referral source; first touch persists across return visits. Note this is claimed attribution; if you need verified, per-application attribution and server-side control, use the Partner API instead.


If you drive applicants to our hosted form, you can hand over everything you already know about them so they start at Step 1 with those answers filled in.

Have an API key? Prefer the server-side hand-off: POST /api/v1/partner-drafts (Partner API guide, §3.1) stores what you send and returns a short resume link for the applicant. Nothing travels in the URL, and the lead exists on our side even if they never open the link. The ?prefill= links below need no credentials, but the data rides in the link itself.

Build the link:

$BASE_URL/?refsource=YOUR_PARTNER_ID&prefill=<encoded JSON>
// Building a hand-off link (browser or Node 18+)
const data = {
  firstName: "Jane", lastName: "Smith", email: "[email protected]",
  phone: "0411 223 344", dateOfBirth: "1990-04-15",
  address: "12 Smith Street, Richmond VIC 3121",
  loanAmount: 25000, depositAmount: 5000,
};
const b64 = btoa(String.fromCharCode(...new TextEncoder().encode(JSON.stringify(data))))
  .replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
const url = `$BASE_URL/?refsource=YOUR_PARTNER_ID&prefill=${b64}`;

Fields you can prefill

Any real form field is accepted; unknown keys are silently ignored. The useful set:

Field Type / values
firstName / lastName string
dateOfBirth YYYY-MM-DD
email string
phone Australian mobile, 04######## (spaces ok)
address single line, e.g. "12 Smith Street, Richmond VIC 3121"
previousAddress single line (if under 3 years at current)
yearsAtAddress number
residentialStatus OWN_MORTGAGE · OWN_NO_MORTGAGE · RENTING · BOARDING · WITH_PARENTS
maritalStatus SINGLE · MARRIED · DEFACTO · DIVORCED · WIDOWED
residencyStatus AU_CITIZEN · AU_PERMANENT_RESIDENT · AU_TAX_RESIDENT · NON_AU_RESIDENT
numberOfDependents number
loanAmount / depositAmount number (AUD)
loanTerm years, 1-7
paymentFrequency weekly · fortnightly · monthly
loanPurpose personal loans: DEBT_CONSOLIDATION · HOME_IMPROVEMENT · TRAVEL · WEDDING · MEDICAL · MAJOR_PURCHASE · EDUCATION · OTHER
employmentType PAYG · CASUAL · CONTRACT · SELF_EMPLOYED · RETIRED · UNEMPLOYED
employmentTime FULL_TIME · PART_TIME · CASUAL (basis, when employed)
employerCompanyName / occupationName string
employerContactNumber string, min 8 digits
grossSalary number
incomePeriod WEEKLY · FORTNIGHTLY · MONTHLY · ANNUALLY
vehicleFound boolean - with rego (string) if known

Behaviour and privacy rules


4. Cross-origin embedding

The widget talks to three endpoints, all derived from submitUrl:

Purpose Derived endpoint
Final submission submitUrl (e.g. $BASE_URL/api/v1/applications)
Bank-statements session (Step 6) …/api/v1/bank-session
Server-side draft saves / resume …/api/v1/drafts

CORS is open by default and can be locked to specific origins on our side; tell us the domains you'll embed on and we'll allowlist them.

These endpoints carry per-visitor rate limits sized well above real applicant behaviour. Because the widget calls them from each applicant's own browser, embedding sites never share a limit; a rate-limited response (429) simply retries after the Retry-After interval.


5. The form's endpoints (reference)

These are the public endpoints behind the widget. They serve the anonymous form, so they require no API key. Server-to-server integrators should use the Partner API instead, which authenticates you and attributes applications to your organisation.

POST /api/v1/applications (final submission)

{
  "applicationId": "3f9d2c80-…",   // optional UUID; reused across the whole journey
  "product": "car",                 // or "personal"
  "form": { /* the widget's form state */ },
  "attribution": { "refsource": "…", "utm_source": "…" }  // optional
}

POST /api/v1/bank-session (Step 6, bank statements)

Called when the applicant reaches the income-verification step. Validates the data captured so far (consents and employment are not yet required at this point), registers the application with the lender, and returns { nimoReference, bankStatementsUrl }; bankStatementsUrl is what the widget opens full-screen for illion bank-statement sharing. Bank credentials are entered on illion's hosted page only; they never touch Gamma Duo or your site.

GET /api/v1/bank-session?id=<applicationId> returns { completed: boolean }

POST /api/v1/drafts · GET /api/v1/drafts?id=…&token=…

Progressive server-side draft capture. The first save (after Step 1) creates the application and returns a resumeToken; the widget stores it and sends it with every later save. GET returns the draft for resume; this is what our "finish your application" email links use ($BASE_URL/?resume=<applicationId>&rt=<token>). Tokens are single-purpose, hashed at rest and expiring; a link that has expired simply starts a fresh application.

GET /api/v1/opt-out?id=…&token=…

One-click unsubscribe page linked from application emails. Stops service emails for that application without affecting the application itself.

GET /api/v1/health

No auth. Returns { ok: true, service: "gamma-duo-intake", nimo: "configured", … }. nimo: "simulated" means the lender connection isn't configured in that environment (submissions complete end-to-end but nothing is lodged). Add ?deep=1 to also verify database connectivity.


6. The applicant journey (what the widget does)

  1. Steps 1-5: details, vehicle/purpose, situation, ID, loan term. Progress is saved locally and (from Step 1) as a server-side draft, so applicants can leave and resume.
  2. Step 6: income verification via illion bank statements (full-screen, with a manual "I've finished" fallback).
  3. Steps 7-8: income, expenses, assets & liabilities. Working applicants must provide an employer contact number (the lender phone-verifies employment).
  4. Step 9: review, credit-check + privacy consents, submit. The applicant sees a reference (GGD-######) and a thank-you screen.

Abandoned applications receive a recovery email with a secure resume link (where the applicant opted in to contact), and are eventually purged under our data-retention policy.


7. Support

Contact your Gamma Duo integration representative for embed onboarding, CORS allowlisting, custom branding, or a walkthrough of the hosted form. For server-to-server submission, see the Partner API.