Custom UI
If the prebuilt React and Vue components don’t fit your design, you can build your own UI using client-js-core directly.
When to use this approach
- You need a layout that doesn’t match the prebuilt components
- You’re using a framework other than React or Vue (Angular, Svelte, vanilla JS)
- You want full control over every DOM element and CSS class
Setting up the client
import { OpendoorClient } from '@opendoor/partner-sdk-client-js-core';
const client = new OpendoorClient({ baseURL: '/api/opendoor/v1', timeout: 30000,});Address suggestions
const { addresses } = await client.getAddressSuggestions('123 Main St');
// addresses: Address[]// Each address has: street1, street2?, city, state, postalCodeaddresses.forEach((addr) => { console.log( `${addr.street1}, ${addr.city}, ${addr.state} ${addr.postalCode}` );});Creating an offer
const offer = await client.createOffer({ address: { street1: '123 Main St', city: 'Phoenix', state: 'AZ', postalCode: '85001', }, salesAssociateName: 'Jane Smith', // optional salesAssociateEmail: 'jane@partner.com', // optional});
console.log(offer.opendoorOfferRequestId); // UUIDconsole.log(offer.offerStatus); // 'PENDING' | 'DENIED'
if (offer.denialInfo) { console.log(offer.denialInfo.code); // e.g., 'DWELLING_TYPE' console.log(offer.denialInfo.explanation); // Human-readable reason}Updating an offer
Send questionnaire answers as a flat object. The server SDK maps the keys to GraphQL automatically.
You can also pass a correlationId to associate the offer with your own internal tracking identifier (e.g., a session ID or lead ID).
const updated = await client.updateOffer(offer.opendoorOfferRequestId, { correlationId: 'your-internal-id', // optional - link to your own tracking system sellerEmail: 'seller@example.com', sellerFullName: 'Jane Smith', sellerPhoneNumber: '555-0100', homeBedrooms: 3, homeBathroomsFull: 2, homeYearBuilt: 2005, homeKitchenCondition: 'standard', homeHoaFees: 250, homeHasUpgrades: true,});Polling for offer status
const status = await client.getOffer(offer.opendoorOfferRequestId);
if (status.offerStatus === 'OFFERED' && status.offerData) { console.log('Price:', status.offerData.headlinePriceCents / 100); console.log('Dashboard:', status.offerData.url);}Getting offer with pricing details
For consumer-facing views, use getOfferWithSellDirectPricing() to get net proceeds (what the seller takes home) and the sell-direct price breakdown:
const pricing = await client.getOfferWithSellDirectPricing( offer.opendoorOfferRequestId);
if (pricing.offerStatus === 'OFFERED' && pricing.offerData) { console.log('Net proceeds:', pricing.offerData.netProceeds); // cents console.log('Headline price:', pricing.offerData.headlinePriceCents); console.log('Opendoor fee:', pricing.offerData.opendoorFeeCents); console.log('Repair cost:', pricing.offerData.repairCostCents); console.log('Closing costs:', pricing.offerData.closingCostsCents); console.log('Mortgage balance:', pricing.offerData.mortgageBalance); console.log('Seller:', pricing.offerData.sellerName); console.log('Expires:', pricing.offerData.expirationAtDisplayDate);}If you only need Sell Direct, getOfferWithSellDirectPricing() is enough. For Sell Direct plus CNML (Sell With Upside) in one backend call, use the server SDK’s getOfferWithProductsPricing and proxy the result to the browser.
Prefilling questionnaire data
const detail = await client.getHomeDetail({ opendoorOfferRequestId: offer.opendoorOfferRequestId,});
// detail.answerPrefills: { "home.bedrooms": { value: 3, source: "MLS" }, ... }Error handling
import { ValidationError, APIError,} from '@opendoor/partner-sdk-client-js-core';
try { await client.createOffer({ address });} catch (err) { if (err instanceof ValidationError) { // Client-side validation failed (missing fields, etc.) console.log(err.field); // e.g., 'address.street1' } else if (err instanceof APIError) { // Server returned an error console.log(err.code); // 'API_ERROR' | 'TIMEOUT' | 'NETWORK_ERROR' console.log(err.statusCode); // HTTP status code }}Client SDK methods summary
| Method | Description |
|---|---|
getAddressSuggestions(query) | Search for addresses |
checkAddressUnit(address) | Check if address needs a unit number |
createOffer({ address, salesAssociateName?, salesAssociateEmail? }) | Create an offer request |
updateOffer(offerId, data) | Submit questionnaire answers |
getOffer(offerId) | Poll for offer status (full details) |
getOfferWithSellDirectPricing(offerId) | Get offer with net proceeds pricing |
getHomeDetail({ opendoorOfferRequestId }) | Load prefill data for questionnaire |
getHomebuilders() | List homebuilder partners for dropdown |
getAssessmentSlots({ offerId }) | Get inspection time slots |
Backend setup
Your backend needs to proxy these requests using the Node or Ruby server SDK. See Quick Start step 1 for Express and Rails examples, and the demo-rails sample for a full Rails BFF.