# Yardi

- Tool: Bubble
- Difficulty: Advanced
- Time required: 3–5 hours (plus administrator coordination time)
- Last updated: July 2026

## TL;DR

Connecting Bubble to Yardi requires your Yardi system administrator to enable API access and provide service account credentials. Voyager uses a SOAP XML API; Breeze uses a modern REST API. Both integrate cleanly through Bubble's API Connector with credentials in Private headers, keeping enterprise property management data fully server-side.

## Yardi + Bubble: Enterprise Property Management in a Custom Interface

Yardi is the enterprise backbone of large residential and commercial property management, covering leasing, accounting, maintenance, and resident services for portfolios that range from a few hundred to tens of thousands of units. Building a custom Bubble app on top of Yardi lets you create branded resident portals, operations dashboards for property managers, or owner reporting tools — all pulling live data from Yardi as the system of record.

The integration is different from most API connections: there is no public developer portal, no self-serve signup, and no universal base URL. Everything starts with your Yardi system administrator. They must enable API access on your Yardi subscription, create a service account, and supply the endpoint URL specific to your organization's deployment (cloud-hosted Yardi has different URLs than on-premises installations).

Once you have credentials, Bubble handles both Yardi Voyager's SOAP XML API and Yardi Breeze's REST JSON API through the API Connector. Voyager requires a SOAP envelope as the request body — Bubble's API Connector supports this with body type set to Text (XML) and dynamic variables injected into the template. Breeze is a standard REST connection with a Private API key header. In both cases, Bubble proxies all calls server-side: your Yardi credentials never reach the browser.

Because Yardi holds sensitive resident PII — names, lease terms, financial records — Bubble privacy rules are non-negotiable. Every data type you create to store Yardi-sourced records must have privacy rules restricting access to the appropriate user roles before you go live.

## Before you start

- A Yardi subscription (Voyager or Breeze) with API access enabled by your Yardi system administrator
- Service account credentials from your Yardi administrator: username, password, serverName, databaseName, and interfaceEntity (Voyager) or API key (Breeze)
- The endpoint URL specific to your organization's Yardi deployment (provided by your administrator)
- A Bubble app on a paid plan (Starter or above) — required for Backend Workflows used in scheduling and data refresh
- The Toolbox plugin installed in your Bubble app (for XML parsing of Voyager SOAP responses)
- Basic familiarity with Bubble's API Connector and Workflow editor

## Step-by-step guide

### 1. Step 1: Coordinate with Your Yardi Administrator

Before touching Bubble, contact your Yardi system administrator. This step is mandatory — there is no way to begin the integration without administrator involvement.

You need three things from your administrator:

1. **API access enabled** on your Yardi account. Yardi Voyager API access is controlled at the system level; your admin must turn it on for your subscription and create a dedicated service account. For Yardi Breeze, they need to generate an API key from the Breeze admin settings.

2. **Your endpoint URL.** There is no single public Yardi API URL. Cloud-hosted Yardi Voyager instances typically use a URL in the format https://[yourorg].yardiasp.com, but this varies. On-premises deployments use an internal network URL. Your administrator must provide this.

3. **Service account credentials.** For Voyager: username, password, serverName, databaseName, and interfaceEntity name (the specific Yardi interface your integration will use, e.g., 'ILSGuestCard' for leads or 'Maintenance' for work orders). For Breeze: an API key.

Once your administrator provides these details, make a note of every value and keep them secure. Do not paste them into emails or documents — store them in a password manager and paste them directly into Bubble's API Connector as Private fields.

Build time depends heavily on your administrator's availability. Schedule this coordination early — before any Bubble development work begins — to avoid blocking the project.

**Expected result:** You have the Yardi endpoint URL, service account credentials, and a list of available interface entities from your system administrator.

### 2. Step 2: Configure the API Connector for Yardi Voyager (SOAP)

If your organization uses Yardi Voyager, the API is SOAP-based. Every request is an HTTP POST with an XML body formatted as a SOAP envelope. Here is how to configure this in Bubble's API Connector.

Go to your Bubble editor → click **Plugins** in the left sidebar → click **Add plugins** → search for 'API Connector' (by Bubble) → click **Install**.

Inside the API Connector, click **Add another API** and name it 'Yardi Voyager'. Set the base URL to your organization's Yardi endpoint (e.g., https://yourorg.yardiasp.com/voyager/webservice/wservice.asmx).

For SOAP calls, do not add a shared Authorization header. Instead, Yardi Voyager credentials go inside the SOAP XML body as fields in the authentication element. Click **Add shared parameter**, set the type to **Header**, name it **Content-Type**, value **text/xml;charset=UTF-8**. Check **Private** for any sensitive value.

Click **New call**. Set method to **POST**. For the body type, select **Text** (not JSON). In the body field, enter your SOAP envelope template. Use Bubble's dynamic variable syntax `<dynamic_value>` for any value that will change per request. Here is a skeleton template:

```xml
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <GetUnitList xmlns="http://tempuri.org/">
      <UserName><dynamic_value>username</dynamic_value></UserName>
      <Password><dynamic_value>password</dynamic_value></Password>
      <ServerName><dynamic_value>serverName</dynamic_value></ServerName>
      <Database><dynamic_value>databaseName</dynamic_value></Database>
      <InterfaceEntity><dynamic_value>interfaceEntity</dynamic_value></InterfaceEntity>
      <PropertyCode><dynamic_value>propertyCode</dynamic_value></PropertyCode>
    </GetUnitList>
  </soap:Body>
</soap:Envelope>
```

For each dynamic variable marked `<dynamic_value>`, mark the credential ones (UserName, Password, ServerName, Database, InterfaceEntity) as **Private** by clicking the eye-slash icon next to that field in the API Connector parameter list.

Add a **SOAPAction** header (required by Voyager SOAP services) with the value matching the operation you are calling — your administrator or Yardi's WSDL document specifies the correct SOAPAction string for each operation.

```
{
  "method": "POST",
  "url": "https://yourorg.yardiasp.com/voyager/webservice/wservice.asmx",
  "headers": {
    "Content-Type": "text/xml;charset=UTF-8",
    "SOAPAction": "http://tempuri.org/GetUnitList"
  },
  "body_type": "Text",
  "body": "<?xml version='1.0' encoding='utf-8'?><soap:Envelope xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'><soap:Body><GetUnitList xmlns='http://tempuri.org/'><UserName><username_private></UserName><Password><password_private></Password><ServerName><serverName_private></ServerName><Database><databaseName_private></Database><InterfaceEntity><interfaceEntity_private></InterfaceEntity><PropertyCode><dynamic_propertyCode></PropertyCode></GetUnitList></soap:Body></soap:Envelope>"
}
```

**Expected result:** The API Connector shows a POST call configured with body type Text and a SOAP XML template. Credential fields are marked Private.

### 3. Step 3: Configure the API Connector for Yardi Breeze (REST)

If your organization uses Yardi Breeze, the API is a standard REST JSON interface — far simpler than Voyager's SOAP setup.

In the API Connector, click **Add another API** and name it 'Yardi Breeze'. Set the base URL to the Breeze API endpoint provided by your administrator (e.g., https://api.yardiyardibreeze.com/v1 — confirm the exact URL with your admin).

Click **Add shared headers or parameters**. Add a header:
- **Name**: Authorization
- **Value**: Bearer [your Breeze API key]
- **Private**: checked (click the lock/eye icon)

Also add:
- **Name**: Content-Type
- **Value**: application/json
- **Private**: unchecked (not a secret)

Click **New call**. Give it a descriptive name like 'Get Units'. Set method to **GET**. Set the endpoint path to the units endpoint your administrator specifies (e.g., /units).

Add any required query parameters — typically a property ID to scope the results to a specific property in your portfolio.

Click **Initialize call** to test the connection. Yardi's Breeze API must return a real, successful response during initialization — Bubble uses this response to detect the data structure. If your test returns an empty array, that is acceptable; if it returns an error, verify your API key and endpoint URL with your administrator before proceeding.

```
{
  "method": "GET",
  "url": "https://api.yardiyardibreeze.com/v1/units",
  "headers": {
    "Authorization": "Bearer <api_key_private>",
    "Content-Type": "application/json"
  },
  "params": {
    "property_id": "<dynamic_property_id>"
  }
}
```

**Expected result:** Bubble detects the response structure from the Yardi Breeze API. Fields like unit_id, unit_number, status, and rent_amount appear in the API Connector response definition.

### 4. Step 4: Parse XML Responses and Set Up Privacy Rules

**For Yardi Voyager (SOAP):** SOAP responses are XML, not JSON. Bubble's native API Connector expects JSON. You need to convert the XML response string into a usable data structure using the Toolbox plugin.

If you haven't already, go to Plugins → Add plugins → search 'Toolbox' → Install.

In a Workflow triggered after the SOAP API call, add the action **Toolbox: Run JavaScript**. In the script field, write JavaScript that uses the browser's DOMParser to parse the XML string and extract the fields you need:

```javascript
var xmlStr = properties.xmlInput;
var parser = new DOMParser();
var xmlDoc = parser.parseFromString(xmlStr, 'text/xml');
var units = xmlDoc.getElementsByTagName('UnitRecord');
var result = [];
for (var i = 0; i < units.length; i++) {
  result.push({
    unit_id: units[i].getElementsByTagName('UnitId')[0].textContent,
    unit_number: units[i].getElementsByTagName('UnitNumber')[0].textContent,
    status: units[i].getElementsByTagName('Status')[0].textContent,
    rent: units[i].getElementsByTagName('MarketRent')[0].textContent
  });
}
return JSON.stringify(result);
```

Pass the SOAP response body as the `xmlInput` property to this script. Store the returned JSON string in a Bubble custom state, then parse it in subsequent workflow steps to create or update records in Bubble's database.

**For Yardi Breeze (REST):** JSON responses parse automatically. Use Bubble's 'Get data from external API' in a workflow to store the results into Bubble data types.

**Set up privacy rules (mandatory for both):** Go to **Data** tab → **Privacy**. Select each data type that stores Yardi data (e.g., YardiUnit, YardiResident, YardiWorkOrder). Add privacy rules:
- For resident-facing data: 'This User = Creator' or a role-based condition
- For manager-facing data: 'Current User's role = Manager'
- Block all fields from being visible to unauthenticated users

Do not go live without these rules. Yardi data includes tenant PII, lease financials, and unit-level details that must not be publicly accessible.

```
// Toolbox Run JavaScript — parse Yardi Voyager SOAP XML response
var xmlStr = properties.xmlInput;
var parser = new DOMParser();
var xmlDoc = parser.parseFromString(xmlStr, 'text/xml');
var units = xmlDoc.getElementsByTagName('UnitRecord');
var result = [];
for (var i = 0; i < units.length; i++) {
  result.push({
    unit_id: units[i].getElementsByTagName('UnitId')[0].textContent,
    unit_number: units[i].getElementsByTagName('UnitNumber')[0].textContent,
    status: units[i].getElementsByTagName('Status')[0].textContent,
    rent: units[i].getElementsByTagName('MarketRent')[0].textContent
  });
}
return JSON.stringify(result);
```

**Expected result:** Yardi data is stored in Bubble's database. Privacy rules are active on all Yardi data types. Residents only see their own records; managers see their assigned properties.

### 5. Step 5: Build the Unit Availability Dashboard

With the API Connector configured and privacy rules in place, you can build the core property management view: a unit availability dashboard.

**Set up the data type:** Go to Data tab → Data types → New type → name it 'YardiUnit'. Add fields: unit_number (text), property_name (text), status (text), market_rent (number), bed_count (number), last_synced (date).

**Create the sync workflow:** In the Workflow editor, create a workflow triggered by a 'Sync Units' button click (or on page load if the data is stale). Inside the workflow:
1. Call the Yardi API (GET units or the SOAP unit list operation)
2. For Voyager: pass the XML response to the Toolbox JavaScript parser
3. For each unit in the result, use 'Create a new YardiUnit' or 'Make changes to YardiUnit' (searching by unit_id) to upsert the record into Bubble's database
4. Store current date/time in a last_synced field

**Build the repeating group:** On the dashboard page, add a Repeating Group. Set Type of content to YardiUnit. Set Data source to 'Do a search for YardiUnit' with constraints on status or property_name as needed. Add columns for unit number, status, market rent, and bedroom count.

**Add vacancy filter:** Add a checkbox or dropdown to the page that filters the repeating group by status = 'Vacant'. This gives property managers an instant vacancy view without additional API calls — the data is already in Bubble's database from the last sync.

Add a 'Last synced: [last_synced date]' text element to the dashboard so users know how fresh the data is. For high-frequency portfolios, a 'Sync now' button is more cost-effective in Workload Units than auto-syncing on every page load.

**Expected result:** A repeating group displays Yardi unit data with vacancy filtering. The last-synced timestamp is visible. Syncing on demand reduces unnecessary API calls.

### 6. Step 6: Build the Work Order Panel and Lease Expiration Alerts

**Work order panel:** Add a new page (or tab within the dashboard) for maintenance work orders.

Create a YardiWorkOrder data type: work_order_id (text), unit_number (text), description (text), status (text), submitted_date (date), completed_date (date), priority (text).

Create a workflow to sync work orders from Yardi (GET work orders endpoint or corresponding SOAP operation). Use the same upsert pattern from Step 5: search for existing records by work_order_id, update if found, create if not.

In the workflow editor, build a status-update workflow: when a manager clicks a 'Mark Resolved' button on a work order row, call Yardi's work order update endpoint (POST or PATCH to the work order update operation with the new status value in the XML body or JSON body). After the Yardi call succeeds, update the local YardiWorkOrder record in Bubble's database to match.

Add confirmation dialogs: any workflow that writes data back to Yardi should prompt 'Are you sure you want to mark this as resolved?' before firing the API call. This prevents accidental state changes in your enterprise property management system.

**Lease expiration alerts:** Create a YardiLease data type: lease_id, tenant_name, unit_number, lease_end_date, status. Sync active leases from Yardi into this data type.

On the dashboard, add a conditional section: 'Show leases expiring within 60 days' using a Bubble search constraint: YardiLease's lease_end_date is within 60 days of today AND status = 'Active'. Display these in a warning-highlighted repeating group at the top of the dashboard.

For automated alerts, consider a Backend Workflow (requires paid Bubble plan) that runs daily, queries YardiLease records expiring within 30 days, and sends an email via Sendgrid or Postmark to the assigned property manager. This saves managers from manually checking expiration dates and prevents lease renewal gaps in large portfolios.

RapidDev's team has built dozens of enterprise property management Bubble apps with integrations to Yardi, Buildium, and CoStar — if you need help architecting the data model or troubleshooting SOAP responses, book a free scoping call at rapidevelopers.com/contact.

**Expected result:** The work order panel shows active requests with status update workflows. The lease expiration alert section highlights upcoming renewals within 60 days. Backend workflows (on paid plans) can automate daily email alerts to property managers.

## Best practices

- Always store Yardi credentials (SOAP body values or API key) as Private fields in the API Connector — Bubble proxies calls server-side, so secrets never reach the browser even without Private marking, but marking them Private also hides them from Bubble's visual editor previews and prevents accidental exposure in logs.
- Store Yardi data in Bubble's database and sync on demand (manual refresh or scheduled Backend Workflows) rather than calling the Yardi API on every page load. This keeps WU costs predictable, avoids hitting Yardi's call limits, and makes your app load faster.
- Configure Bubble privacy rules on every Yardi data type before you write a single record. Yardi data includes tenant PII, financial records, and lease details — these must not be readable by unauthenticated users or users outside their assigned property.
- For Voyager SOAP: log the raw XML response in a Bubble debug data type during development. Seeing the actual XML element names from your specific Yardi deployment prevents hours of debugging JavaScript parsers that reference the wrong tag names.
- Use a last_synced timestamp field on all Yardi-sourced data types. Display it on the dashboard so property managers know how fresh the data is and can decide when to trigger a manual sync.
- For Voyager SOAP calls, use 'Use as Action' in the API Connector (not 'Use as Data') since SOAP responses are XML strings that need JavaScript processing before they can bind to Bubble UI elements.
- If Yardi is on-premises at your organization, the API endpoint URL is an internal network address — your Bubble app needs to be able to reach it. Bubble proxies calls from its own servers, so your internal URL must be accessible from the internet (or via a VPN/firewall rule allowing Bubble's server IP ranges). Confirm this with your IT team before building.

## Use cases

### Unit Availability Dashboard

Build a branded operations dashboard that shows real-time vacancy status across a portfolio. Property managers see which units are available, when leases expire, and which units are in the turn process — all fetched from Yardi and displayed in Bubble repeating groups with filtering by property or unit type.

### Resident Self-Service Portal

Create a Bubble-powered resident portal where tenants can view their lease details, submit maintenance work orders, and check the status of existing requests — all reading and writing back to Yardi as the system of record, with role-based privacy rules ensuring residents only see their own data.

### Owner Reporting Interface

Build a custom investor/owner portal that pulls financial summaries, occupancy rates, and maintenance cost data from Yardi and displays them in clean charts and tables. Owners get a branded reporting experience without needing Yardi login credentials.

## Troubleshooting

### API Connector returns a 401 Unauthorized error on every Yardi call

Cause: Service account credentials (username, password, serverName, databaseName, or interfaceEntity) are incorrect, or the Yardi administrator has not yet enabled API access for the service account.

Solution: Verify every credential value character by character with your Yardi administrator. For Voyager SOAP, confirm the serviceAccountName matches the account they created in Yardi's admin panel. For Breeze REST, confirm the API key is still active and hasn't been rotated. Double-check that API access is enabled at the system level — it does not activate automatically.

### The Initialize call step in Bubble shows 'There was an issue setting up your call' for the Voyager SOAP endpoint

Cause: Bubble's Initialize call expects a JSON response, but Yardi Voyager returns XML. Additionally, the SOAP body may be malformed — a missing closing tag, incorrect namespace, or wrong SOAPAction header prevents Yardi from parsing the request.

Solution: For SOAP, the Initialize call will never work cleanly in Bubble's standard mode because the response is XML, not JSON. Instead, use 'Use as Action' (not Data) for Voyager SOAP calls — this lets you trigger the call from a workflow and capture the raw response as a text string for JavaScript parsing. Validate your SOAP XML structure using an online SOAP tester or Postman before importing to Bubble.

### The Toolbox JavaScript XML parser returns undefined or throws an error on the SOAP response

Cause: The XML element names in the SOAP response do not match the element names used in the JavaScript parser — Yardi's actual element names depend on the specific interface entity and may differ from documentation examples.

Solution: Add a temporary debug step in your Bubble workflow: store the raw SOAP response string in a Bubble database text field and view it in the Data tab. Copy the actual XML and inspect the exact element names before writing the JavaScript parser. Update getElementsByTagName calls to match the actual element names from your specific Yardi interface entity.

```
// Debug: store raw XML response for inspection
// In Bubble workflow after SOAP call:
// 'Create a new DebugLog' with field rawResponse = Result of Step 1's body
// Then check Data tab to view the full XML string
```

### Bubble privacy rules are silently blocking Yardi data from appearing to logged-in users

Cause: Privacy rules are too restrictive — they may be blocking data from users who should have access, or the 'Everyone' rule has been removed without a replacement rule that covers logged-in users.

Solution: Go to Data tab → Privacy → select the affected data type. Review each rule in order (Bubble evaluates from top to bottom). Add a rule: 'When Current User is logged in' → allow the required fields. For role-based access, check that the Current User's role field matches what the rule expects. Use Bubble's built-in 'Preview as' feature to test the page as different user types.

### Backend Workflow for daily lease expiration alerts shows 'Workflow API is not enabled' error

Cause: Scheduled Backend Workflows require a paid Bubble plan. The free tier does not support API Workflows or scheduled workflows.

Solution: Upgrade to Bubble's Starter plan or above to enable Backend Workflows. Once on a paid plan, go to Settings → API → check 'This app exposes a Workflow API' to enable the Backend Workflow section. Then create the scheduled workflow under the Backend Workflows tab.

## Frequently asked questions

### Does Yardi have a public developer portal or self-serve API key?

No. Yardi does not have a public developer portal. API access is enabled entirely by your organization's Yardi system administrator on a per-deployment basis. The endpoint URL, service account credentials, and available interface entities are all provided by your administrator — there is nothing you can configure or test without their involvement.

### What is the difference between Yardi Voyager and Yardi Breeze for Bubble integration?

Yardi Voyager uses a SOAP-based API: every request is an HTTP POST with an XML body (SOAP envelope), and responses are XML. Bubble handles this via the API Connector with body type Text (XML) and a Toolbox JavaScript parser for responses. Yardi Breeze uses a modern REST JSON API, which is a standard API Connector setup with a Private API key header. If you have a choice, Breeze is significantly easier to integrate.

### Can I test the Yardi API without involving my system administrator?

No. The endpoint URL itself is organization-specific and not publicly accessible. There is no sandbox or public demo environment to test against. All testing requires real credentials on your organization's Yardi deployment.

### Do I need a paid Bubble plan to integrate with Yardi?

A paid Bubble plan is required if you want to use Backend Workflows for scheduled data sync or lease expiration alerts (Starter plan or above). The basic API Connector calls work on Bubble's free plan, but any recurring, automated data refresh or alert workflow requires an upgrade.

### Why is the SOAP XML response not parsed automatically by Bubble?

Bubble's API Connector natively expects JSON responses and auto-detects field names from JSON objects. SOAP responses are XML, which Bubble treats as a raw text string. You need the Toolbox plugin's 'Run JavaScript' action to parse the XML string using DOMParser and extract the fields you need into a format Bubble can work with.

### Is storing Yardi data in Bubble's database a data security risk?

Not if you configure Bubble's privacy rules correctly. Every data type that stores Yardi-sourced records must have privacy rules restricting visibility to authorized user roles. Without these rules, Bubble's default behavior may expose records to all users of the app. Configure rules before writing any Yardi data to the database, and test access under different user roles before going live.

---

Source: https://www.rapidevelopers.com/bubble-integrations/yardi
© RapidDev — https://www.rapidevelopers.com/bubble-integrations/yardi
