> For the complete documentation index, see [llms.txt](https://even-financial.gitbook.io/developer-center/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://even-financial.gitbook.io/developer-center/marketplace-integrations/web-embeds/personal-loans-marketplace.md).

# Personal Loans Marketplace

{% hint style="success" icon="person-waving" %}
This page outlines what's needed to integrate Engine's **Personal Loans Embed** into your website. In this guide, we will explore what Engine’s Embed syntax looks like and best practices for loading it on your site. This includes topics like proactively addressing layout shift for optimized performance.

This is a high-level guide, and specific implementation details should be discussed with your Engine team.
{% endhint %}

### Component Names

### User Experience (Search)

### **Supported Keys for Personal Loans Customer Data Prefill**

## Tracking Events Emitted by Embed

When you embed the Personal Loans experience, event callbacks fire automatically.

#### How It Works

As a consumer navigates through the experience, the embed emits **partner messages**: JSON objects with a `name` (the event type) and a `payload` (event-specific data). The iframe posts them to your host page via `window.parent.postMessage(message, '*')`.

* **You do not register callbacks with Engine.** You listen on your host page; messages arrive whether or not you handle them.
* **Not every UI action sends a message.** Only the events listed below are part of the partner contract.
* **Messages are one-way.** There is no acknowledgement or response channel.

#### Implementation Path

Partners using Web Embed receive events by listening for browser `message` events on the host page:

```javascript
window.addEventListener('message', (event) => {
  const message = event.data
  if (!message || typeof message !== 'object' || typeof message.name !== 'string') return
  console.log(message.name, message.payload)
})
```

#### Message envelope

```json
{
  "name": "eventName",
  "payload": {}
}
```

| Field     | Description                                                        |
| --------- | ------------------------------------------------------------------ |
| `name`    | The event name (e.g. `"onCreate"`, `"onNavigate"`)                 |
| `payload` | Event-specific data; always includes `timestamp` (ISO-8601 string) |

#### Events & Data

`onCreate`: Emitted when a lead-created model is mounted. For non-edit flows, `editingPage` is `null`.

```json
{
  "name": "onCreate",
  "payload": {
    "timestamp": "2026-06-03T14:22:10.123Z",
    "leadUuid": "00000000-0000-0000-0000-000000000000",
    "createPage": "search",
    "editingPage": null
  }
}
```

`onNavigate`: Emitted when the flow moves from one step to another.

```json
{
  "name": "onNavigate",
  "payload": {
    "timestamp": "2026-06-03T14:22:10.123Z",
    "leadUuid": "00000000-0000-0000-0000-000000000000",
    "fromPage": "income_info",
    "toPage": "financial_profile",
    "editingPage": null
  }
}
```

`onUpdate`: Emitted when a step is submitted. For normal submissions, `editingPage` is `null`. For confirmation edit submissions, `editingPage` is an array of edited pages.

```json
{
  "name": "onUpdate",
  "payload": {
    "timestamp": "2026-06-03T14:22:10.123Z",
    "leadUuid": "00000000-0000-0000-0000-000000000000",
    "updatePage": "loan-amount",
    "editingPage": null
  }
}
```

```json
{
  "name": "onUpdate",
  "payload": {
    "timestamp": "2026-06-03T14:22:10.123Z",
    "leadUuid": "00000000-0000-0000-0000-000000000000",
    "updatePage": "confirm",
    "editingPage": ["loan-amount"]
  }
}
```

`onSubmit`: Emitted when the final loan search is submitted.

```json
{
  "name": "onSubmit",
  "payload": {
    "timestamp": "2026-06-03T14:22:10.123Z",
    "leadUuid": "00000000-0000-0000-0000-000000000000"
  }
}
```

`onExit`: Emitted when the user closes the embed (e.g. via the close button). `experience` is `"search"` when closed from the form flow and `"compare"` when closed from the results page.

```json
{
  "name": "onExit",
  "payload": {
    "timestamp": "2026-06-03T14:22:10.123Z",
    "experience": "search"
  }
}
```

`onErrorPageView`: Emitted when a search error page is viewed.

```json
{
  "name": "onErrorPageView",
  "payload": {
    "timestamp": "2026-06-03T14:22:10.123Z",
    "leadUuid": "00000000-0000-0000-0000-000000000000",
    "fromPage": "confirm_details",
    "editingPage": null
  }
}
```

`onErrorPageRetry`: Emitted when a user clicks retry from a search error page.

```json
{
  "name": "onErrorPageRetry",
  "payload": {
    "timestamp": "2026-06-03T14:22:10.123Z",
    "leadUuid": "00000000-0000-0000-0000-000000000000",
    "toPage": "confirm_details",
    "editingPage": null
  }
}
```

`onRateTableRender`: Emitted when offers are rendered on the compare/results page.

```json
{
  "name": "onRateTableRender",
  "payload": {
    "timestamp": "2026-06-03T14:22:10.123Z",
    "rateTableUuid": "123e4567-e89b-12d3-a456-426614174000",
    "loanOffers": [],
    "specialOffers": []
  }
}
```

Both arrays are always present. Typically one is populated and the other is empty, depending on which compare view rendered.

**`loanOffers[]` item**

Sent when the **loans** compare view is shown.

| Field                      | Type             | Definition                                                                                                       |
| -------------------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------- |
| `offerUuid`                | string           | Offer UUID                                                                                                       |
| `financialInstitutionName` | string           | Lender name                                                                                                      |
| `financialInstitutionUuid` | string           | Lender UUID (empty string if unavailable)                                                                        |
| `productType`              | string           | Product type from the API (e.g. `"loan"`, `"Loan"`)                                                              |
| `productSubType`           | string           | Product subtype (e.g. `"personal_loan"`, `"secured_loan"`)                                                       |
| `loanAmount`               | number \| string | Maximum loan amount (`maxAmount`)                                                                                |
| `apr`                      | number \| string | APR — resolved from `maxApr`, then `meanApr`, then `minApr`, else `""`                                           |
| `termLength`               | number \| string | Term length in months, or `""` if unavailable                                                                    |
| `monthlyPayment`           | number \| string | Monthly payment — resolved from `maxMonthlyPayment`, then `monthlyPayment`, then `meanMonthlyPayment`, else `""` |

Example:

```json
{
  "offerUuid": "offer-123",
  "financialInstitutionName": "Lender One",
  "financialInstitutionUuid": "fi-123",
  "productType": "Loan",
  "productSubType": "personal_loan",
  "loanAmount": 10000,
  "apr": 14.99,
  "termLength": 36,
  "monthlyPayment": 340
}
```

**`specialOffers[]` item**

Sent when the **special / other offers** compare view is shown (debt relief, credit builder, cash advance, bill reduction, etc.).

| Field                      | Type   | Definition                                                                     |
| -------------------------- | ------ | ------------------------------------------------------------------------------ |
| `offerUuid`                | string | Offer UUID                                                                     |
| `name`                     | string | Offer headline / name shown in the UI                                          |
| `financialInstitutionName` | string | Partner name                                                                   |
| `financialInstitutionUuid` | string | Partner financial-institution UUID (empty string if unavailable)               |
| `productSubType`           | string | Product subtype (e.g. `"credit_builder"`, `"debt_relief"`, `"bill_reduction"`) |

Example:

```json
{
  "offerUuid": "special-offer-123",
  "name": "Special Offer",
  "financialInstitutionName": "Partner One",
  "financialInstitutionUuid": "partner-123",
  "productSubType": "credit_builder"
}
```

Special-offer render items do **not** include `productType`, `loanAmount`, `apr`, `termLength`, or `monthlyPayment`.

**What gets sent in each view**

| Compare view                     | `loanOffers`                      | `specialOffers`                                    |
| -------------------------------- | --------------------------------- | -------------------------------------------------- |
| Loans (default / paginated list) | Offers **displayed on that page** | `[]`                                               |
| Special / other offers           | `[]`                              | All special offers from the rate table             |
| No offers                        | `[]`                              | `[]` (may still fire when empty render is emitted) |

***

`onOfferClick`: Emitted when the user clicks an offer call-to-action. Suppressed if no `leadUuid` is available.

```json
{
  "name": "onOfferClick",
  "payload": {
    "timestamp": "2026-06-03T14:22:10.123Z",
    "leadUuid": "lead-123",
    "offerUuid": "offer-456",
    "financialInstitutionName": "Acme Bank",
    "financialInstitutionUuid": "bank-789",
    "productType": "loan",
    "productSubType": "personal",
    "loanAmount": 5000,
    "apr": 12.34,
    "termLength": 24,
    "monthlyPayment": 236.18
  }
}
```

**Payload fields**

| Field                      | Type             | Definition                                                                        |
| -------------------------- | ---------------- | --------------------------------------------------------------------------------- |
| `timestamp`                | string           | ISO-8601 time when the click occurred                                             |
| `leadUuid`                 | string           | Applicant lead UUID (required on click)                                           |
| `offerUuid`                | string           | Clicked offer UUID                                                                |
| `financialInstitutionName` | string           | Lender / partner name                                                             |
| `financialInstitutionUuid` | string           | Lender / partner UUID                                                             |
| `productType`              | string           | `"loan"` for loan offers; `"Special"` for special offers                          |
| `productSubType`           | string           | Product subtype                                                                   |
| `loanAmount`               | number \| string | Loan amount (loan offers) or `"undefined for specialOffers"` (special offers)     |
| `apr`                      | number \| string | APR (loan offers) or `"undefined for specialOffers"` (special offers)             |
| `termLength`               | number \| string | Term length (loan offers) or `"undefined for specialOffers"` (special offers)     |
| `monthlyPayment`           | number \| string | Monthly payment (loan offers) or `"undefined for specialOffers"` (special offers) |

**Loan offer click**

Same shape as a `loanOffers[]` item plus `leadUuid` and `timestamp`.

**Special offer click**

Uses the same field names, but `productType` is `"Special"` and the four metric fields are the literal string `"undefined for specialOffers"` (not `null`, not omitted):

```json
{
  "name": "onOfferClick",
  "payload": {
    "timestamp": "2026-06-03T14:22:10.123Z",
    "leadUuid": "lead-123",
    "offerUuid": "special-offer-123",
    "financialInstitutionName": "Freedom Debt Relief",
    "financialInstitutionUuid": "fi-special-123",
    "productType": "Special",
    "productSubType": "debt_relief",
    "loanAmount": "undefined for specialOffers",
    "apr": "undefined for specialOffers",
    "termLength": "undefined for specialOffers",
    "monthlyPayment": "undefined for specialOffers"
  }
}
```

One event is SDK-only and will not be emitted by the Personal Loans web embed:

* `onBack`: Emitted by the SDK header back button

Note: unlike the Credit Cards embed, `onExit` **is** emitted by the Personal Loans web embed (see above).

***

#### Common `productSubType` values

Examples seen in loan and special-offer payloads:

`personal_loan`, `secured_loan`, `line_of_credit`, `debt_relief`, `credit_builder`, `cash_advance`, `bill_reduction`, `installment_loans`

Treat this as a representative set, not a frozen enum — new subtypes may appear as products are added.

## **Embed Best Practices & Troubleshooting**

### How it works

Add this script tag wherever you want the embed to render. It loads an iframe that automatically fills its container and scales responsively.

```html
<script
  async
  src="https://www.moneylion.com/network/{channel}/{zone}/web-component/{component-name}/index.js"
  data-embed-type="auto-mount"
></script>
```

{% hint style="warning" %} Your Engine representative will provide your specific code snippet. Don't reproduce the placeholders above as-is. {% endhint %}

**Placeholders**

| Field             | What it is                                                 |
| ----------------- | ---------------------------------------------------------- |
| `channel`         | Provided in your embed code                                |
| `zone`            | Provided in your embed code                                |
| `component-name`  | The specific product/experience being rendered (see below) |
| `data-embed-type` | Must be `"auto-mount"` — without it, nothing renders       |

### Component types

| Component name    | Best for                                                                                                                 |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------ |
| `lending-search`  | A full application flow — user answers a few questions, gets matched offers in-session. Use as your primary entry point. |
| `lending-explore` | A browse-and-filter experience using data you already have or prefilled. Use inside an existing logged-in dashboard.     |

### Interactive Demo

See the Personal Loans Embed in action:

{% embed url="<https://www.moneylion.dev/network/moneylion/personal-loans/iframe>" %}

### User Experience Page-by-Page

<table data-view="cards"><thead><tr><th></th></tr></thead><tbody><tr><td></td></tr></tbody></table>

### Prefilling & attribution

Add client tags to your embed to prefill known, non-PII fields and attribute leads for reporting. Both reduce friction and improve conversion.

**Client tags** (`data-tags` attribute, for reporting):

```
tag.key_1=value_1&tag.key_2=value_2
```

**Prefill fields** (`data-tags` attribute, for auto-populating the form):

```
app.key_1=value_1&app.key_2=value_2
```

**Example** — tags `clientId` and `source` for reporting, prefilled zip code:

```html
<script
  async
  src="https://www.moneylion.com/network/{channel}/{zone}/web-component/{component-name}/index.js"
  data-tags="tag.clientId=c1&tag.source=email&app.zipcode=93105"
  data-embed-type="auto-mount"
></script>
```

#### Supported client tag keys

{% hint style="info" %} Keys are case-sensitive — use consistent casing across all requests. {% endhint %}

`agentId` · `campaignId` · `clickId` · `clientId` · `deviceId` · `medium` · `sourceId` · `subId` · `subId1` · `subId2` · `subId3` · `target` · `trafficsource` · `userId`

Need a different key? Ask your Partner Manager — nonstandard keys slow down reporting and aren't recommended.

#### Supported prefill fields (Personal Loans)

| Embed field                | Client tag key               | Notes                                                                                 |
| -------------------------- | ---------------------------- | ------------------------------------------------------------------------------------- |
| Loan Purpose               | `app.purpose`                | [Allowed values](https://engine.tech/docs/api-reference/#tocS_LoanPurpose)            |
| Requested Loan Amount      | `app.loanAmount`             | No dollar signs, e.g. `6000`                                                          |
| Credit Rating              | `app.providedCreditRating`   | [Allowed values](https://engine.tech/docs/api-reference/#tocS_ProvidedCreditRating)   |
| City\*                     | `app.city`                   | Spaces as `%20`, e.g. `New%20York`                                                    |
| State\*                    | `app.state`                  | [Allowed values](https://engine.tech/docs/api-reference/#tocS_State)                  |
| Zip Code\*                 | `app.zipcode`                | 5 digits, or 9 digits as 5+4                                                          |
| Property Status            | `app.propertyStatus`         | [Allowed values](https://engine.tech/docs/api-reference/#tocS_PropertyStatus)         |
| Date of Birth              | `app.dateOfBirth`            | Format `mm/dd/yyyy`                                                                   |
| Highest Level of Education | `app.educationLevel`         | [Allowed values](https://engine.tech/docs/api-reference/#tocS_EducationLevel)         |
| Employment Status          | `app.employmentStatus`       | [Allowed values](https://engine.tech/docs/api-reference/#tocS_EmploymentStatus)       |
| Annual Income              | `app.annualIncome`           | No dollar signs, e.g. `100000`                                                        |
| Pay Frequency              | `app.employmentPayFrequency` | [Allowed values](https://engine.tech/docs/api-reference/#tocS_EmploymentPayFrequency) |

\*Overwritten if the user's location is detected.

## **Best Practices & Troubleshooting**

{% hint style="success" %}

#### Best practices

1. **Use the snippet Engine gives you.** Don't hardcode channel, zone, or component paths yourself.
2. **Keep `async` on the script tag.** This prevents the embed script from blocking the rest of your page.
3. **Give the embed a real container.** Set a width on the parent. Set a `min-height` when possible. This gives the iframe room to render and avoids large layout shifts.
4. **Allow Engine in your CSP.** Your Content Security Policy must allow the Engine script in `script-src` and `https://www.moneylion.com` (or your Engine environment host) in `frame-src`.
5. **Follow this page's tags and prefill guidance.** Attributes differ by product. Use this page's sample, or your Engine-provided snippet, rather than copying syntax from another embed.
   {% endhint %}

{% hint style="warning" %}

#### Troubleshooting

**The component doesn't show**

1. Check your install mode:
   * **Explicit custom element:** Confirm the `moneylion-*` element from your snippet is on the page.
   * Auto-mount snippet: Confirm data-embed-type="auto-mount" is on the tag.
2. Open the browser console. Check for script load failures or CSP blocks.

**The page feels slow**

1. Confirm the `async` attribute is on the `<script>` tag.
2. Avoid nesting the embed in a hidden container, such as `display: none`, at first load. This can delay useful height and layout work.

**Events aren't firing**

1. Confirm you listen for `message` events. Check for `{ name, payload }` objects. See the tracking section on this page for product-specific events.
2. In production, filter by Engine's origin. This ignores unrelated `postMessage` traffic from other page scripts.
   {% endhint %}

### Related

See **Tracking Events** for the full list of event callbacks this embed emits (`onCreate`, `onNavigate`, `onOfferClick`, and more).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://even-financial.gitbook.io/developer-center/marketplace-integrations/web-embeds/personal-loans-marketplace.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
