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

Gamma Duo Partner Application API

Submit loan applications collected on your own website, form or system into the Gamma Duo platform. Each submission is authenticated, validated, mapped to our lender's schema and forwarded in real time.

Prefer to let applicants fill in our form on your website instead of building your own? See the Form & Embed guide: a drop-in <script> widget that submits through the same pipeline.


1. Environments & base URLs

Environment Base URL
UAT / testing https://<uat-deployment-host>
Production https://<prod-deployment-host>

We provide your exact host(s) at onboarding. All examples below use $BASE_URL. The current upstream lender environment is Nimo UAT.

Health probe (no auth): GET $BASE_URL/api/v1/health

{
  "ok": true,
  "service": "gamma-duo-intake",
  "time": "2026-07-13T05:00:00.000Z",
  "nimo": "configured",
  "db": "configured",
  "submitToNimo": true,
  "partners": 1
}

The response carries additional operational fields; the two that matter for integrators are nimo ("simulated" means lender credentials aren't configured yet; see §6) and partners (how many partner keys are active). ?deep=1 additionally verifies database connectivity.


2. Authentication

Send your key on every request:

X-API-Key: pk_live_xxxxxxxxxxxxxxxxxxxxxxxx

Missing/invalid keys are rejected before any processing (see §5).


3. Endpoint

POST $BASE_URL/api/v1/partner-applications
Content-Type: application/json
X-API-Key: <your key>

Request envelope:

{
  "externalApplicationId": "your-ref-99881",  // optional: your own reference, echoed back
  "application": { /* canonical application, see §4 */ }
}

3.1 Hand-off drafts (server-side prefill)

If you don't hold a complete application - you captured some details and want the applicant to finish the rest themselves - create a hand-off draft instead of a submission:

POST $BASE_URL/api/v1/partner-drafts
Content-Type: application/json
X-API-Key: <your key>

Same envelope as §3, but every application field is optional (send what you have; unknown fields are ignored). The only requirement is someone to hand off to: at least one of applicant.firstName, applicant.email or applicant.phone, else 422 nothing_to_prefill.

{
  "externalApplicationId": "your-ref-99881",
  "application": {
    "product": "car",
    "applicant": { "firstName": "Ana", "lastName": "Reyes", "email": "[email protected]", "phone": "0400 999 888" },
    "loan": { "amount": 18000, "termYears": 5 }
  }
}

200 response:

{
  "applicationId": "c3d4e5f6-a7b8-4c9d-8e0f-1a2b3c4d5e6f",
  "resumeToken": "rt_9kq…",
  "resumeUrl": "https://apply.gammaduo.com.au/?resume=c3d4e5f6-…&rt=rt_9kq…",
  "status": "lead",
  "partnerId": "broker_123",
  "externalApplicationId": "your-ref-99881"
}

Send the applicant to resumeUrl (email/SMS it, or redirect them): the form opens with everything you supplied already filled in, and they complete the remaining steps and submit. It's one application file end to end - your partnerId/externalApplicationId stay attached through final submission, and our operations team sees the draft immediately.

Notes:


4. The application object (canonical schema)

Field names below are exact. Unknown fields are ignored. Send what you have; only the fields marked required must be present.

Note: fields that exist only in our broker portal's version of the form (e.g. the broker's own reference and authority declaration) are not part of this schema - use externalApplicationId for your reference.

4.1 Top level

Field Type Notes
product string required: "car" or "personal". ("commercial" asset finance exists on our platform but has no lender integration yet - sending it captures the application for manual processing instead of lodging it; treat it as unsupported here until announced.)
applicant object required, see §4.2
loan object required, see §4.3
consents object required, see §4.6
vehicle object car loans, see §4.4
employment object see §4.5
expenses object monthly amounts (AUD), see §4.7
assets array see §4.8
liabilities array see §4.8

4.2 applicant

Field Type Req Notes
firstName string min 2 chars
lastName string min 2 chars
dateOfBirth string YYYY-MM-DD, applicant must be 18 to 120
email string valid email
phone string AU mobile recommended, e.g. 0411223344 (≥ 8 digits)
address string single-line residential address (see note)
previousAddress string supply if < 3 years at current address
postalSameAsResidential boolean default true
postalAddress string required when postalSameAsResidential is false
maritalStatus string SINGLE | MARRIED | DEFACTO | DIVORCED | WIDOWED
residentialStatus string OWN_MORTGAGE | OWN_NO_MORTGAGE | RENTING | BOARDING | WITH_PARENTS | CARAVAN | OTHER
residencyStatus string AU_CITIZEN | AU_PERMANENT_RESIDENT | AU_TAX_RESIDENT | NON_AU_RESIDENT
dependents integer number of dependants
yearsAtAddress integer years at current address
isExistingCustomer boolean default false

Address note: send a single line as "<unit>/<number> <street>, <suburb> <STATE> <postcode>" (e.g. "8/210 Coronation Drive, Milton QLD 4064"). We parse it into structured components best-effort; the full line is always preserved.

4.3 loan

Field Type Req Notes
amount number > 0. Net amount to finance. Personal loans: the net amount (after any deposit) must be $8,000-$60,000, the lender product's pricing range - outside it the submission is rejected with a validation error
purpose string ✅ for personal DEBT_CONSOLIDATION | HOME_IMPROVEMENT | TRAVEL | WEDDING | MEDICAL | MAJOR_PURCHASE | EDUCATION | OTHER
purposeDetail string What the funds will be used for, in the applicant's words (up to 300 chars). Strongly recommended when purpose is OTHER or MAJOR_PURCHASE - the assessment team needs the true purpose. The hosted form requires it for both; the partner API does not, so existing integrations keep working
deposit number car loans: deposit / trade-in
termYears number preferred term; feeds the lender's product selection (defaults to 5 when omitted)
repaymentFrequency string weekly | fortnightly | monthly; feeds the lender's product selection (defaults to monthly)

4.4 vehicle (car loans)

Field Type Notes
found boolean true if a specific vehicle is identified
rego string registration plate
state string NSW | VIC | QLD | SA | WA | TAS | ACT | NT
make, model string
year string year of manufacture, e.g. "2022"
vin string
bodyType string free text, e.g. Sedan, SUV, Ute
value number estimated / purchase value
isNew boolean true new, false used

4.5 employment

Field Type Notes
type string PAYG | CASUAL | CONTRACT | SELF_EMPLOYED | RETIRED | UNEMPLOYED
time string FULL_TIME | PART_TIME | CASUAL. The lender requires a basis whenever type is PAYG/SELF_EMPLOYED - if omitted we submit FULL_TIME
employer string
employerContactNumber string required unless type is RETIRED/UNEMPLOYED (min 8 digits). The lender uses it to verify employment
occupation string
startDate string YYYY-MM or YYYY-MM-DD
onProbation boolean defaults to false when omitted
grossIncome number
incomePeriod string WEEKLY | FORTNIGHTLY | MONTHLY | ANNUALLY
averageAnnualIncome number required when type is SELF_EMPLOYED - taxable income from the business averaged over the last two years. The lender assesses a business on this rather than a single year
businessAddress string where the business trades from, single line. Defaults to the applicant's residential address
businessAddressSameAsResidential boolean default true
previousEmployer string supply when the business has traded for under 3 years - the lender asks what came before it
previousEmploymentStartDate string YYYY-MM or YYYY-MM-DD

Self-employed applicants need the business object as well (§4.9). The lender treats a self-employed income record as a business record and rejects the submission without the ABN, the entity type, the trading-since date and the two-year average.

4.9 business (self-employed applicants, and asset finance)

Required whenever employment.type is SELF_EMPLOYED. Asset finance uses the same object for the entity being financed, so one shape serves both.

Field Type Req Notes
abn string 11 digits. Spaces are fine - we strip them
entityName string the business name (max 50 chars reaches the lender)
entityType string SOLE_TRADER | COMPANY | PARTNERSHIP | TRUST
abnActiveSince string YYYY-MM-DD - when the ABN became active. Under 3 years ago, also send employment.previousEmployer
abnVerified boolean true if you checked it against the Australian Business Register
abnStatus string what the register said, e.g. Active
abnAgeMonths integer months since abnActiveSince
gstRegistered boolean

Our own form fills all of this from one ABN look-up; if you already hold the ABN, sending it plus the name, type and start date is enough.

4.6 consents

Field Type Req Notes
creditCheck boolean must be true
privacyPolicy boolean must be true - the Privacy Policy & Consent
dataExchange boolean must be true - the Consent to the Exchange of Data
creditGuide boolean must be true - applicant confirms they have read the Credit Guide
marketing boolean optional: marketing communications consent
contactOptIn boolean optional: service messaging about this application (progress / resume emails)

You must show the applicant the disclosure documents and obtain their consent before submitting: the Privacy Policy & Consent, the Consent to the Exchange of Data and the Credit Guide are all published at the links above on the application host. Submissions without all four required consents set to true are rejected (422). Hand-off drafts (§3.1) are different: consents can never be preset there - the applicant ticks them in the form.

4.7 expenses (monthly AUD)

Object with any of these numeric keys (note the subscriptionTv casing): rent, utilities, telecommunications, insurance, groceries, diningOut, vehiclesTransport, subscriptionTv, entertainment, educationChildcare.

4.8 assets / liabilities

"assets": [
  { "type": "SAVINGS", "value": 15000 },                 // PROPERTY|VEHICLE|SHARES|SUPERANNUATION|SAVINGS|OTHER
  { "type": "PROPERTY", "value": 700000, "purpose": "LIVE_IN", "propertyType": "HOUSE" },
  { "type": "OTHER", "description": "Boat", "value": 9000 }
],
"liabilities": [
  { "type": "CREDIT_CARD", "lender": "ANZ", "balance": 2000, "monthlyRepayment": 100, "limit": 15000 }
  // type: HOME_LOAN|CAR_LOAN|PERSONAL_LOAN|CREDIT_CARD|BNPL|SALARY_ADVANCE|OTHER
]

Property assets: the lender requires purpose (LIVE_IN | INVESTMENT) and propertyType (HOUSE | APARTMENT_UNIT | TOWNHOUSE_VILLA | RESIDENTIAL_VACANT_LAND | COMMERCIAL_PROPERTY) on every PROPERTY asset. Send them when you know them; if omitted we derive them (a homeowner's first property is treated as LIVE_IN, anything else as INVESTMENT, type HOUSE). Liabilities: the lender requires a credit limit on every liability; if omitted we submit the balance as the limit.


5. Examples

Copy-paste, tested payloads live in docs/examples/ and are verified by our test suite on every change:

Minimal request:

{
  "externalApplicationId": "acme-form-10231",
  "application": {
    "product": "personal",
    "applicant": {
      "firstName": "Sam", "lastName": "Lee", "dateOfBirth": "1988-07-01",
      "email": "[email protected]", "phone": "0400111222",
      "address": "44 Smith Street, Collingwood VIC 3066"
    },
    "loan": { "amount": 15000, "purpose": "DEBT_CONSOLIDATION" },
    "consents": { "creditCheck": true, "privacyPolicy": true }
  }
}

cURL:

curl -sS -X POST "$BASE_URL/api/v1/partner-applications" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: $PARTNER_API_KEY" \
  --data @docs/examples/partner-application.full.json

6. Responses

200: accepted & submitted

{
  "applicationId": "3f9d2c80-5b7e-4c1a-9f0a-2d6b8e4a1c55",
  "reference": "GGD-411394",
  "status": "submitted",
  "externalApplicationId": "acme-form-99881",
  "partnerId": "broker_123",
  "nimo": { "message": "Application Created Successfully" }
}

Error responses

All errors are JSON with an error code. Full catalogue:

HTTP error When Retry?
400 invalid_json body isn't valid JSON No, fix the request
400 missing_application no application object. If the canonical fields are at the top level instead, the message says so and foundAtTopLevel[] lists them - see below No, fix the request
401 unauthorized missing or invalid X-API-Key (message says which) No, fix the key
503 not_configured partner programme not enabled on this host No, contact us
405 method_not_allowed not a POST No
422 validation_failed see fields[] No, fix and resubmit
422 nothing_to_prefill hand-off drafts (§3.1): no applicant.firstName/email/phone supplied No, fix the request
409 application_already_submitted hand-off drafts (§3.1): that externalApplicationId already completed an application (applicationId echoed) No
429 rate_limited too many requests from one address (retryAfterSec in the body, Retry-After header) Yes, after Retry-After
424 nimo_rejected lender rejected it (nimoStatus, nimo echo the reason) Only after fixing the flagged data
424 nimo_auth_failed our lender credentials were rejected (server config), not your fault No, contact us
424 nimo_unreachable lender network/timeout error (detail) Yes, with backoff

The most common integration mistake is sending the fields flat, without the application wrapper:

// WRONG - fields at the top level
{ "product": "personal", "applicant": { … }, "loan": { … } }

// RIGHT - wrapped, with externalApplicationId BESIDE it
{ "externalApplicationId": "your-ref", "application": { "product": "personal", "applicant": { … }, "loan": { … } } }

The error names it rather than just saying a field is missing:

{
  "error": "missing_application",
  "message": "Wrap the application fields in an 'application' object: {\"application\": { … }}. Found 'product', 'applicant', 'loan' at the top level instead. 'externalApplicationId' stays beside 'application', not inside it (…).",
  "foundAtTopLevel": ["product", "applicant", "loan"]
}

422 shape (one entry per invalid field):

{
  "error": "validation_failed",
  "fields": [
    { "field": "applicant.dateOfBirth", "message": "required, format YYYY-MM-DD" },
    { "field": "consents.creditCheck", "message": "must be true" }
  ]
}

Fields validated: product, applicant.firstName, applicant.lastName, applicant.email, applicant.phone, applicant.address, applicant.dateOfBirth (18+), loan.amount (> 0; personal loans: net $8,000-$60,000), loan.purpose (personal loans), consents.creditCheck, consents.privacyPolicy, consents.dataExchange, consents.creditGuide, employment.employerContactNumber (min 8 digits; required whenever employment.type is a working type, i.e. anything other than RETIRED/UNEMPLOYED, because the lender uses it to verify employment), and - when employment.type is SELF_EMPLOYED - business.abn, business.entityName, business.entityType, business.abnActiveSince and employment.averageAnnualIncome.

424 nimo_rejected shape:

{ "error": "nimo_rejected", "nimoStatus": 400, "nimo": { "message": "Bad request", "errors": { "error": "…reason…" } } }

7. Idempotency, retries & rate limits

8. Limitations (current build)

9. Support

Contact your Gamma Duo integration contact for onboarding, a UAT key, host URLs, and to report issues (include applicationId and externalApplicationId).