> 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/embeds.md).

# Lending Marketplace

Implementation, Syntax, Consumer Experience, Tags, Tracking, and Best Practices

{% hint style="success" icon="person-waving" %}
This page outlines what's needed to integrate Engine's **Lending Embed** into your website. This is a high-level guide, and specific implementation details should be discussed with your Engine team.

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.
{% endhint %}

## The Lending Marketplace

Engine's Lending embed can be configured to return prequalified offers for **personal loans, student loan refinance, auto loan refinance, and home equity lines of credit (HELOC)**. The embed can be configured against two entrypoints where consumers can land:

* **Explore.** A rate-trend chart built on current APR data. It shows how rates have moved rather than listing offers, so a consumer can arrive with no information and still see something useful. Its call-to-action hands off to Search.
* **Search.** The question flow that qualifies the consumer and ends by creating an offer search with Engine's lending partners.

After completion of the Search funnel and providing consents to Engine, consumers will see a **Results Page.** where they will sort through their offers, narrow them with filters, and with each offer's call-to-action sending the consumer to that lender for application completion.

**Your Engine representative will set the entrypoint per zone to either Explore or Search while configuring new experiences.** Which entrypoint that you use should be decided strategically with you Engine team.

### Lending Consumer Experience Demo

See the Lending Embed in action:

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

## Standard Embed Implementation

Engine's embed code can be placed in any order within your page, but for optimal user experience we recommend placing the `script` tag within the same div where the content is being rendered.

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

### **Basic Embed Syntax**

{% hint style="info" icon="triangle-exclamation" %}
This is a high-level guide, and specific implementation details should be discussed with your Engine team. **Placeholders and will be replaced with precise values in the embed code provided to you.**

Your Engine representative will provide you with your specific code snippet. Do not try to reproduce from the sample snippet outlined below.
{% endhint %}

{% code overflow="wrap" %}

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

{% endcode %}

### **Embed Components**

* **Channel & Zone:** placeholders that will be replaced with precise values in the embed code provided to you.
* **Component Name:** corresponds to the specific product, experience, and consumer entrypoint that's being rendered.
  * `lending-explore`: a preview with experience where consumers can browse and filter offers before entering into the full search funnel.
  * `lending-search`: drops consumers into the full application Searcb funnel where they input data for the purpose of loan prequalification. At the end of the search funnel is Engine's Results page where prequalified offers are displayed.
* **Mounting**: in order to render the embed, `data-embed-type` must be set to `"auto-mount"`

## Including Tags for Data Prepopulation & Reporting 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):

{% code overflow="wrap" %}

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

{% endcode %}

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

{% code overflow="wrap" %}

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

{% endcode %}

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

{% code overflow="wrap" %}

```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>
```

{% endcode %}

### Supported Client Tag Keys

Below are the client tag keys that are currently supported. See [Attribution Tracking](/developer-center/channel-partner-reporting/attribution-tracking.md) for more information.&#x20;

{% columns %}
{% column %}
**Most common**

* `tag.clientId`
* `tag.subId`
* `tag.subId1`
* `tag.subId2`
* `tag.campaignId`
  {% endcolumn %}

{% column %}
**Also supported**

* `tag.agentId`
* `tag.clickId`
* `tag.deviceId`
* `tag.medium`
* `tag.sourceId`
* `tag.subId3`
* `tag.target`
* `tag.trafficsource`
* `tag.userId`
  {% endcolumn %}
  {% endcolumns %}

{% hint style="danger" %}
**Client tag keys are case-sensitive.** `subid` and `subId` are separate reporting keys.
{% endhint %}

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

### Lending Supported Prefill Fields

| 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.

## Tracking Events Emitted by Embed

When you embed the Lending experience, event callbacks will fire automatically that you can read and track directly from the browser.

### 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:

{% code overflow="wrap" %}

```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)
})
```

{% endcode %}

### Event Syntax, Payload, and Metadata

#### Message Envelope

{% code overflow="wrap" %}

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

{% endcode %}

<table><thead><tr><th width="144.51171875">Title</th><th>Description</th></tr></thead><tbody><tr><td><code>name</code></td><td>The event name (e.g. <code>"onCreate"</code>, <code>"onNavigate"</code>)</td></tr><tr><td><code>payload</code></td><td>Event-specific data; always includes <code>timestamp</code> (ISO-8601 string)</td></tr></tbody></table>

#### Funnel Navigation Events

{% tabs %}
{% tab title="onCreate" %}
`onCreate` is emitted when a lead-created model is mounted. For non-edit flows, `editingPage` is `null`.

{% code overflow="wrap" %}

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

{% endcode %}
{% endtab %}

{% tab title="onNavigate" %}
`onNavigate` is emitted when the flow moves from one step to another.

{% code overflow="wrap" %}

```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
  }
}
```

{% endcode %}
{% endtab %}

{% tab title="onUpdate" %}
`onUpdate` is emitted when a step is submitted. For normal submissions, `editingPage` is `null`. For confirmation edit submissions, `editingPage` is an array of edited pages.

{% code overflow="wrap" expandable="true" %}

```kotlin
clientTags = mapOf(
  "clientId" to listOf("client1"),
  "trafficSource" to listOf("email"),
)
```

{% endcode %}
{% endtab %}

{% tab title="onUpdate" %}
{% code overflow="wrap" %}

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

{% endcode %}
{% endtab %}

{% tab title="onSubmit" %}
`onSubmit`: Emitted when the final loan search is submitted.

{% code overflow="wrap" %}

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

{% endcode %}
{% endtab %}
{% endtabs %}

#### Exit & Error Events

{% tabs %}
{% tab title="onExit" %}
`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.

{% code overflow="wrap" %}

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

{% endcode %}
{% endtab %}

{% tab title="onErrorPageView" %}
`onErrorPageView`: Emitted when a search error page is viewed.

{% code overflow="wrap" %}

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

{% endcode %}
{% endtab %}

{% tab title="onErrorPageRetry" %}
`onErrorPageRetry`: Emitted when a user clicks retry from a search error page.

{% code overflow="wrap" expandable="true" %}

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

{% endcode %}
{% endtab %}
{% endtabs %}

#### Offer & Click Events

{% tabs %}
{% tab title="onRateTableRender" %}
`onRateTableRender`: Emitted when offers are rendered on the compare/results page.

{% code overflow="wrap" %}

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

{% endcode %}

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

{% tabs %}
{% tab title="loanOffers Array " %}
Present when **prequalified** **loans** results are displayed to consumers on the Results Page.

| 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 `""` |

**Sample Payload:**

{% code overflow="wrap" %}

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

{% endcode %}
{% endtab %}

{% tab title="specialOffers Array" %}
Emitted when **static** **special offers** (debt relief, credit builder, cash advance, bill reduction, etc.) are rendered on the Results Page.

| 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"`) |

**Sample Payload:**

{% code overflow="wrap" %}

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

{% endcode %}
{% endtab %}
{% endtabs %}
{% endtab %}

{% tab title="onOfferClick (Loans)" %}
`onOfferClick`: Emitted when the user clicks an offer call-to-action. Suppressed if no `leadUuid` is available.

{% code overflow="wrap" %}

```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
  }
}
```

{% endcode %}

| 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) |
| {% endtab %}               |                  |                                                                                   |

{% tab title="onOfferClick (Special Offers)" %}
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):

{% code overflow="wrap" %}

```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"
  }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

## **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 %}


---

# 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 by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

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

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

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.
