# How to Integrate Bolt.new with Propertybase

- Tool: Bolt.new
- Difficulty: Intermediate
- Time required: 45 minutes
- Last updated: April 2026

## TL;DR

Propertybase is a real estate CRM built on Salesforce, so integrating it with Bolt.new means using Salesforce's REST API with OAuth 2.0. Set up a Connected App in your Salesforce/Propertybase org, obtain access tokens via the OAuth flow through a Next.js API route, then query real estate objects like listings and contacts. All API calls go through server-side routes — never expose client credentials in your Bolt frontend.

## Connecting Bolt.new to Propertybase via the Salesforce REST API

Propertybase is not a standalone API — it is a managed package installed on top of a Salesforce org. This means everything you can do with Propertybase's data is done through Salesforce's REST API, using Salesforce's authentication (OAuth 2.0), Salesforce's query language (SOQL), and Salesforce's object model (standard Salesforce objects plus Propertybase custom objects). If you have used Salesforce APIs before, Propertybase integration will feel familiar. If you have not, the learning curve is steeper than a typical REST API, but the pattern is well-documented.

The integration approach in Bolt follows the standard server-side proxy pattern. Bolt generates a React frontend and a set of Next.js API routes. The API routes handle OAuth token acquisition using the Salesforce Username-Password flow (suitable for server-to-server, not user-facing login) and proxy data requests to Salesforce's REST endpoints. The frontend only calls your own API routes — it never touches Salesforce directly, which keeps your Connected App credentials private and server-only.

During development in Bolt's WebContainer, outbound HTTPS calls to Salesforce's servers (login.salesforce.com and your org's API endpoint) work perfectly — WebContainers support outbound HTTP/HTTPS. What does not work is incoming connections: if you want Salesforce to push real-time updates to your app via Outbound Messages or Platform Events webhooks, you need a deployed URL. For development, poll the Salesforce API or use Bolt's proxy pattern to simulate real-time updates. After deploying to Netlify or Vercel, register your webhook endpoints with Salesforce Workflow Rules or Process Builder.

## Before you start

- An active Propertybase subscription with a Salesforce org (Propertybase is installed as a managed package on Salesforce)
- Salesforce System Administrator access to create a Connected App in the org
- Your Salesforce username, password, and security token (found in Salesforce Settings → Personal Information → Reset My Security Token)
- A Bolt.new project using Next.js for server-side API routes
- Basic understanding of SOQL (Salesforce Object Query Language) for querying Propertybase objects

## Step-by-step guide

### 1. Create a Salesforce Connected App for API Access

To authenticate with the Salesforce REST API (and therefore Propertybase), you need a Connected App — a configuration in your Salesforce org that represents your external application and grants it API access. Connected Apps provide the client_id and client_secret used in OAuth flows.

To create a Connected App: log into your Salesforce org (or Propertybase portal, which takes you to Salesforce). Navigate to Setup (gear icon top-right) → search 'App Manager' → click 'New Connected App' in the top-right. Fill in: Connected App Name (e.g., 'Bolt Property Portal'), API Name (auto-fills), Contact Email (your email). Under 'API (Enable OAuth Settings)', check 'Enable OAuth Settings'. For Callback URL, enter http://localhost:3000/api/auth/callback (you will update this after deployment). Under 'Selected OAuth Scopes', add: 'Access and manage your data (api)', 'Perform requests on your behalf at any time (refresh_token, offline_access)', and 'Full access (full)'. Save the Connected App. Salesforce takes 2-10 minutes to propagate the new Connected App.

After propagation, click 'Manage Consumer Details' to retrieve your Consumer Key (this is your client_id) and Consumer Secret (this is your client_secret). Store these in your Bolt .env file — they grant full API access and must never appear in frontend code.

For the server-to-server Username-Password OAuth flow used in this integration, you do not need to configure the callback URL to match exactly during development. The flow sends credentials directly to Salesforce's token endpoint and receives an access token without a browser redirect. However, for production apps where users log in with their own Salesforce accounts, you would implement the Web Server OAuth flow with proper redirect handling.

```
# .env
SALESFORCE_CLIENT_ID=your_consumer_key_from_connected_app
SALESFORCE_CLIENT_SECRET=your_consumer_secret_from_connected_app
SALESFORCE_USERNAME=admin@yourorg.com
SALESFORCE_PASSWORD=yourpassword
SALESFORCE_SECURITY_TOKEN=yourSecurityToken123
# Security token is appended to password during OAuth flow
# Get it from: Salesforce → Settings → Personal Information → Reset My Security Token
```

**Expected result:** A Connected App exists in your Salesforce org with a Consumer Key and Consumer Secret. Your .env file has all five Salesforce credentials. The getSalesforceToken function is scaffolded and ready for the next step.

### 2. Build the Salesforce Authentication Utility

The Salesforce Username-Password OAuth 2.0 flow is the simplest path for server-to-server authentication. Your server posts credentials to Salesforce's token endpoint and receives an access_token and instance_url. The access_token is a short-lived JWT (typically valid for 1-2 hours, configurable in Salesforce), and the instance_url is your org's API base URL (e.g., https://yourcompany.my.salesforce.com).

For a Next.js API route application, token caching is important. Without caching, every API request would make a token fetch call before the actual Salesforce API call — doubling your latency and consuming Salesforce API limits faster. Since Next.js serverless functions can run in multiple concurrent instances, a simple module-level variable is not perfect for distributed caching, but it works well enough for most applications. For high-traffic apps, cache tokens in Redis or a database.

The combined password+security_token value is sent as the password parameter. Salesforce validates the credentials, checks the Connected App's OAuth scopes, and returns an access token. The token is used as a Bearer token in all subsequent API calls via the Authorization header. If your org is in a sandbox (for testing), use https://test.salesforce.com/services/oauth2/token as the login URL instead of https://login.salesforce.com.

Important: during development in Bolt's WebContainer, this OAuth call goes outbound to Salesforce's servers over HTTPS — this works perfectly in WebContainers. You can test the authentication flow directly in the Bolt preview. Only incoming webhooks from Salesforce require a deployed URL.

```
// lib/salesforce.ts
interface SalesforceToken {
  access_token: string;
  instance_url: string;
  expires_at: number;
}

interface SalesforceQueryResult<T> {
  totalSize: number;
  done: boolean;
  records: T[];
}

let tokenCache: SalesforceToken | null = null;

export async function getSalesforceToken(): Promise<SalesforceToken> {
  if (tokenCache && Date.now() < tokenCache.expires_at) {
    return tokenCache;
  }

  const params = new URLSearchParams({
    grant_type: 'password',
    client_id: process.env.SALESFORCE_CLIENT_ID!,
    client_secret: process.env.SALESFORCE_CLIENT_SECRET!,
    username: process.env.SALESFORCE_USERNAME!,
    // Password + security token concatenated with no separator
    password: process.env.SALESFORCE_PASSWORD! + process.env.SALESFORCE_SECURITY_TOKEN!,
  });

  const res = await fetch('https://login.salesforce.com/services/oauth2/token', {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: params.toString(),
  });

  if (!res.ok) {
    const err = await res.text();
    throw new Error(`Salesforce OAuth failed: ${err}`);
  }

  const data = await res.json();
  tokenCache = {
    access_token: data.access_token,
    instance_url: data.instance_url,
    expires_at: Date.now() + 1.5 * 60 * 60 * 1000, // 1.5 hours
  };
  return tokenCache;
}

export async function soqlQuery<T>(
  query: string
): Promise<SalesforceQueryResult<T>> {
  const { access_token, instance_url } = await getSalesforceToken();
  const encoded = encodeURIComponent(query);
  const res = await fetch(
    `${instance_url}/services/data/v58.0/query?q=${encoded}`,
    { headers: { Authorization: `Bearer ${access_token}` } }
  );
  if (!res.ok) {
    const err = await res.text();
    throw new Error(`SOQL query failed: ${err}`);
  }
  return res.json();
}

export async function salesforceUpdate(
  objectType: string,
  recordId: string,
  fields: Record<string, unknown>
): Promise<void> {
  const { access_token, instance_url } = await getSalesforceToken();
  const res = await fetch(
    `${instance_url}/services/data/v58.0/sobjects/${objectType}/${recordId}`,
    {
      method: 'PATCH',
      headers: {
        Authorization: `Bearer ${access_token}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(fields),
    }
  );
  if (!res.ok) {
    const err = await res.text();
    throw new Error(`Salesforce update failed: ${err}`);
  }
}
```

**Expected result:** The Salesforce authentication utility is complete. Calling getSalesforceToken() returns a valid access_token and instance_url from Salesforce. The soqlQuery helper can run SOQL queries. Test by making a call in a simple API route — you should see your org's data in the Bolt preview.

### 3. Query Propertybase Objects with SOQL

Propertybase installs custom objects on top of Salesforce's standard objects. The exact object names depend on your Propertybase version and any customizations your team has made. Standard Salesforce objects (Contact, Lead, Account, Opportunity) are always available. Propertybase-specific objects typically have a namespace prefix (e.g., pb__ or propertybase__) and include objects like Property__c (listings), Transaction__c (deals), and Commission__c.

To discover your org's Propertybase objects and their fields, use Salesforce's Schema API. In a browser, while logged into Salesforce, visit: YOUR_INSTANCE_URL/services/data/v58.0/sobjects/ — this lists all available objects. For a specific object's fields, visit /services/data/v58.0/sobjects/Property__c/describe/. This describe call returns every field name, type, label, and whether it is required or read-only. Use this to build accurate SOQL queries.

SOQL (Salesforce Object Query Language) looks like SQL but has important differences: no SELECT *, no JOINs (use relationship queries instead), and WHERE clauses require exact field API names (not labels). A basic property query: SELECT Id, Name, pb__Price__c, pb__Bedrooms__c, pb__City__c, pb__Status__c FROM pb__Property__c WHERE pb__Status__c = 'Active' ORDER BY CreatedDate DESC LIMIT 50. Relationship queries traverse foreign keys: SELECT Id, Name, pb__Property__r.Name, pb__Agent__r.FirstName FROM pb__Transaction__c WHERE IsClosed = false.

Create a separate Next.js API route for each resource type. Do not expose raw SOQL via a query parameter — that is a SOQL injection vulnerability. Build parameterized server-side queries where only safe values (validated from a known set) are interpolated into the SOQL string.

```
// app/api/properties/route.ts
import { NextResponse } from 'next/server';
import { soqlQuery } from '@/lib/salesforce';

interface Property {
  Id: string;
  Name: string;
  pb__Price__c: number;
  pb__Bedrooms__c: number;
  pb__City__c: string;
  pb__Status__c: string;
  pb__PhotoUrl__c?: string;
}

export async function GET(request: Request) {
  try {
    const { searchParams } = new URL(request.url);
    const conditions: string[] = ["pb__Status__c = 'Active'"];

    const minPrice = Number(searchParams.get('minPrice'));
    const maxPrice = Number(searchParams.get('maxPrice'));
    const bedrooms = Number(searchParams.get('bedrooms'));
    const city = searchParams.get('city')?.replace(/[^a-zA-Z\s]/g, '');

    if (minPrice > 0) conditions.push(`pb__Price__c >= ${minPrice}`);
    if (maxPrice > 0) conditions.push(`pb__Price__c <= ${maxPrice}`);
    if (bedrooms > 0) conditions.push(`pb__Bedrooms__c = ${bedrooms}`);
    if (city) conditions.push(`pb__City__c = '${city}'`);

    const where = conditions.join(' AND ');
    const soql = `SELECT Id, Name, pb__Price__c, pb__Bedrooms__c, pb__City__c, pb__Status__c, pb__PhotoUrl__c FROM pb__Property__c WHERE ${where} ORDER BY CreatedDate DESC LIMIT 100`;

    const result = await soqlQuery<Property>(soql);
    return NextResponse.json(result.records);
  } catch (error: unknown) {
    const e = error as { message: string };
    return NextResponse.json({ error: e.message }, { status: 500 });
  }
}
```

**Expected result:** GET /api/properties returns an array of property records from your Propertybase org. The Bolt preview shows live data from your Salesforce instance. Filters for price, bedrooms, and city work correctly.

### 4. Build the Property Listing Frontend in Bolt

With the API routes returning Propertybase data, prompt Bolt to build the React frontend components. The frontend should fetch from your own API routes (not directly from Salesforce), so the architecture remains: React component → your API route → Salesforce REST API → Propertybase data.

The property listing UI typically includes: a search form with filters (price range slider, bedroom selector, city dropdown or text input), a results grid showing property cards, a detail view or modal for individual properties, and pagination or infinite scroll for large result sets. Use Bolt to scaffold all of these components and wire them to your API endpoints.

For the agent dashboard portion — listing contacts, leads, and transactions — consider using a table layout with sortable columns, status badges (color-coded by Propertybase status values), and inline editing for quick updates. Inline edits should call your PATCH API routes that use salesforceUpdate().

State management for this type of data portal is straightforward: use React's useState and useEffect for data fetching, or a library like SWR or TanStack Query for automatic caching and revalidation. Bolt can scaffold either approach on request. For the Kanban pipeline view, prompt Bolt to build columns from the distinct StageName values returned by your SOQL query — these should match your org's opportunity stages exactly.

**Expected result:** The property search portal renders live data from Propertybase in the Bolt preview. Filters update the displayed listings in real time. Clicking a property card opens the detail modal with full information.

### 5. Deploy and Configure Salesforce Outbound Webhooks

Deploy your Bolt project to Netlify or Vercel to get a stable public URL. Salesforce Outbound Messages and Platform Events — Propertybase's mechanisms for pushing real-time updates when records change — require a publicly accessible HTTPS endpoint. During Bolt WebContainer development, incoming connections are impossible since the WebContainer has no public network address. This is a fundamental limitation of the browser-based runtime: Salesforce cannot call back to a localhost URL inside your browser tab.

To deploy to Netlify: in your Bolt project, open Settings → Applications → Netlify → Publish. After the first deploy, go to Netlify Dashboard → Site Settings → Environment Variables and add all five Salesforce credentials (SALESFORCE_CLIENT_ID, SALESFORCE_CLIENT_SECRET, SALESFORCE_USERNAME, SALESFORCE_PASSWORD, SALESFORCE_SECURITY_TOKEN). Trigger a redeploy.

For Salesforce Outbound Messages: in Salesforce Setup, go to Workflow Rules (or Process Builder). Create a rule that fires when a Property__c status changes. Add an Outbound Message action pointing to https://your-netlify-domain.netlify.app/api/webhooks/salesforce. Create a POST handler at that route that parses the SOAP XML Salesforce sends, processes the update, and returns a standard acknowledgment response.

For simpler event handling, use Salesforce Change Data Capture (CDC) via Platform Events — this is available on Developer Edition and Enterprise+ orgs. CDC pushes events to a Pub/Sub API endpoint. However, for most Propertybase integrations, polling the SOQL API every 30-60 seconds is sufficient and avoids the webhook complexity entirely.

```
# netlify.toml
[build]
  command = "npm run build"
  publish = ".next"

[build.environment]
  NODE_VERSION = "20"

[[plugins]]
  package = "@netlify/plugin-nextjs"
```

**Expected result:** The app is deployed to Netlify with all Salesforce credentials set as environment variables. The Salesforce API connection works in production. Outbound Message webhooks can be registered with the Netlify URL for real-time property and contact updates.

## Best practices

- Always use server-side API routes to proxy Salesforce calls — never expose your Connected App client_id or client_secret to the browser
- Cache Salesforce access tokens in memory with a 1.5-hour expiry to avoid making a token call before every API request
- Sanitize all user-provided values before interpolating them into SOQL queries to prevent SOQL injection — use a whitelist approach for field names and validate numeric values
- Use the Salesforce Describe API to discover actual object and field API names in your specific Propertybase org rather than guessing namespace prefixes
- Set up a health check endpoint that tests the Salesforce OAuth connection on startup so deployment failures are caught immediately
- During Bolt WebContainer development, outbound calls to Salesforce work fine — only incoming Salesforce webhooks require a deployed URL
- Use Salesforce sandbox orgs (accessible at test.salesforce.com) for development and testing before connecting to your production Propertybase org
- Consider polling the Salesforce API every 60 seconds for data freshness instead of implementing Outbound Message webhooks — polling is simpler and sufficient for most real estate dashboard use cases

## Use cases

### Property Listing Search Portal

Build a public-facing property search interface that reads listing data from Propertybase in real time. The frontend renders search filters (price range, bedrooms, location) and calls a Next.js API route that queries Propertybase via SOQL. Results display as a responsive card grid with property photos, pricing, and agent contact details.

Prompt example:

```
Build a property listing search portal connected to Propertybase via the Salesforce REST API. Create a Next.js API route at app/api/properties/route.ts that authenticates with Salesforce using the Username-Password OAuth flow (reading SALESFORCE_USERNAME, SALESFORCE_PASSWORD, SALESFORCE_SECURITY_TOKEN, SALESFORCE_CLIENT_ID, SALESFORCE_CLIENT_SECRET from env). The route should accept query params (minPrice, maxPrice, bedrooms, city) and run a SOQL query on the Property__c object returning Name, Price__c, Bedrooms__c, City__c, Status__c, PhotoUrl__c fields. Build a React frontend with a search form and property card grid that calls this API.
```

### Agent Dashboard for Lead and Contact Management

Create an internal agent dashboard that surfaces Propertybase contacts and leads in a clean interface. Agents can view their assigned leads, filter by status, and update contact information — all operations proxied through Next.js API routes to Salesforce. The dashboard pulls from standard Salesforce Contact and Lead objects that Propertybase extends.

Prompt example:

```
Build an agent dashboard connected to Propertybase. Create API routes: GET /api/contacts (SOQL: SELECT Id, FirstName, LastName, Email, Phone, LeadSource, Status__c FROM Contact WHERE OwnerId = ':userId' ORDER BY CreatedDate DESC LIMIT 50), GET /api/leads (similar query on Lead object), PATCH /api/contacts/[id] (update contact fields via Salesforce PATCH). All routes authenticate via the Salesforce Username-Password OAuth flow. Build a tabbed dashboard UI with contacts list, leads pipeline board, and a contact detail modal with an edit form.
```

### Transaction Tracker with Deal Pipeline

Build a deal pipeline view that pulls Propertybase transaction and opportunity data into a Kanban-style interface. Agents see all active deals organized by stage, with click-through to full transaction details including associated contacts, properties, and key dates. Read-only view suitable for team leaders who need pipeline visibility without logging into Salesforce.

Prompt example:

```
Create a real estate transaction pipeline tracker using Propertybase data. Build a GET /api/transactions API route that queries the Opportunity object (Propertybase stores transactions as Opportunities) with SOQL: SELECT Id, Name, StageName, Amount, CloseDate, AccountId, Property__c, Agent__c FROM Opportunity WHERE IsClosed = false ORDER BY StageName, CloseDate. Group results by StageName and render as a Kanban board with columns for each pipeline stage. Each card shows property name, amount, close date, and a link to full details via GET /api/transactions/[id].
```

## Troubleshooting

### OAuth error: 'invalid_grant' when calling Salesforce token endpoint

Cause: The Username-Password OAuth flow is failing due to incorrect credentials. Common causes: wrong security token (it changes every time you reset your Salesforce password), IP not whitelisted in org's trusted IP ranges, or the Connected App has not finished propagating (takes up to 10 minutes after creation).

Solution: Reset your Salesforce security token via Settings → Personal Information → Reset My Security Token — a new token is emailed to your Salesforce registered email. Update SALESFORCE_SECURITY_TOKEN in .env. If using a sandbox org, change the login URL to https://test.salesforce.com/services/oauth2/token. Wait 10 minutes after creating a new Connected App before testing.

### SOQL query returns 'INVALID_FIELD: No such column pb__Property__c'

Cause: The Propertybase object and field names in the SOQL query do not match the actual API names in the org. Namespace prefixes vary between Propertybase versions and org configurations.

Solution: Discover actual field names by calling the Salesforce Describe API: GET YOUR_INSTANCE_URL/services/data/v58.0/sobjects/YOUR_OBJECT/describe/ with the Bearer token. This returns all valid field names. In Salesforce Setup → Object Manager, the 'API Name' column shows the exact names to use in SOQL.

```
// Add this debug endpoint to discover your org's object fields:
// GET /api/debug/describe?object=pb__Property__c
export async function GET(request: Request) {
  const { searchParams } = new URL(request.url);
  const objectName = searchParams.get('object') ?? 'Contact';
  const { access_token, instance_url } = await getSalesforceToken();
  const res = await fetch(
    `${instance_url}/services/data/v58.0/sobjects/${objectName}/describe/`,
    { headers: { Authorization: `Bearer ${access_token}` } }
  );
  const data = await res.json();
  const fields = (data.fields as Array<{name: string; type: string; label: string}>)
    .map(f => ({ name: f.name, type: f.type, label: f.label }));
  return NextResponse.json(fields);
}
```

### Salesforce PATCH update fails with 'FIELD_INTEGRITY_EXCEPTION' or 'INSUFFICIENT_ACCESS_ON_CROSS_REFERENCE_ENTITY'

Cause: The Connected App's user does not have permission to edit the object or specific fields, or the field is read-only due to a Salesforce validation rule or field-level security setting.

Solution: In Salesforce Setup, check the Profile assigned to your integration user — go to Profiles → [Your Profile] → Field-Level Security and verify the target fields have Edit permission. For record-level access, check Sharing Rules. Propertybase adds its own permissions on top of standard Salesforce profiles, so consult your Propertybase admin.

### Outbound Message webhook not receiving events after deployment

Cause: The Salesforce Workflow Rule is inactive, the Outbound Message endpoint URL does not match the deployed URL, or Salesforce requires an HTTPS URL with a valid certificate (self-signed certs are rejected).

Solution: In Salesforce Setup → Workflow Rules, confirm the rule status is Active. Verify the Outbound Message endpoint URL is your exact Netlify HTTPS URL. Netlify automatically provides valid TLS certificates. Check Salesforce's Outbound Message delivery log in Setup → Monitoring → Outbound Messages for error details.

## Frequently asked questions

### Does Bolt.new work with Propertybase during development in the preview?

Yes, for outbound API calls. Bolt's WebContainer can make HTTPS requests to Salesforce's servers, so the OAuth token flow, SOQL queries, and record updates all work in the Bolt preview. What does not work is receiving incoming data from Salesforce — Outbound Messages and Platform Event webhooks require a public HTTPS URL, which the WebContainer cannot provide. For real-time Salesforce-to-app sync, deploy first and then register webhook endpoints.

### Do I need to know SOQL to integrate Propertybase with Bolt.new?

Basic SOQL knowledge is very helpful. SOQL is similar to SQL: SELECT fields FROM object WHERE condition ORDER BY field LIMIT n. The main differences are no SELECT *, no JOINs (use relationship queries like Account.Name instead), and all field names must be the API names (not display labels). Bolt's AI can generate SOQL queries for you if you describe what data you need — just ask it to query specific Propertybase objects with the fields you want.

### Can I use the Propertybase API without a Salesforce Connected App?

No. Since Propertybase runs entirely on Salesforce infrastructure, all API access goes through Salesforce's OAuth 2.0 system, which requires a Connected App. The Connected App acts as the API credential set for your integration. Creating one is free and takes about 5 minutes — it requires Salesforce System Administrator access to your org.

### How do I find the correct object names for Propertybase custom objects?

Open Salesforce Setup → Object Manager and check the API Name column for any Propertybase-specific objects. They typically have a namespace prefix like pb__ before the object name. Alternatively, call the Salesforce REST API at /services/data/v58.0/sobjects/ with your Bearer token to get a complete list of all objects in your org, including Propertybase custom objects.

### Is it possible to let end users log into Propertybase through a Bolt app with their own credentials?

Yes, using the Salesforce Web Server OAuth flow instead of the Username-Password flow used in this tutorial. The Web Server flow redirects users to a Salesforce login page, they authorize your Connected App, and Salesforce returns an authorization code that you exchange for an access token. This requires storing per-user tokens and implementing the full OAuth callback route. For an internal tool where a single integration user is sufficient, the Username-Password flow is simpler and works well.

---

Source: https://www.rapidevelopers.com/bolt-ai-integrations/propertybase
© RapidDev — https://www.rapidevelopers.com/bolt-ai-integrations/propertybase
