Transaction Workflows¶
Complete guide to managing real estate transactions with the ReZEN API, from creation to closing.
🚨 CRITICAL REQUIREMENTS - Read This First!¶
API Requirements That Must Be Followed
Based on extensive testing, these requirements are MANDATORY for successful API calls. Failing to follow them will result in "Bad request: Invalid request" errors.
🏠 Location Updates¶
❌ THIS FAILS:
# Basic address fields alone will FAIL
location_data = {
"street": "123 Main St",
"city": "Salt Lake City",
"state": "UTAH",
"zip": "84101"
}
# API returns: "Bad request: Invalid request"
✅ THIS WORKS:
# Additional property details are REQUIRED
location_data = {
"street": "123 Main St",
"city": "Salt Lake City",
"state": "UTAH",
"zip": "84101",
"county": "Salt Lake", # REQUIRED
"yearBuilt": 2020, # REQUIRED
"mlsNumber": "MLS123456" # REQUIRED
}
💰 Price/Date Updates¶
❌ THIS FAILS:
# Basic price fields alone will FAIL
price_data = {
"dealType": "COMPENSATING",
"propertyType": "RESIDENTIAL",
"salePrice": {"amount": 500000, "currency": "USD"},
"representationType": "BUYER"
}
# API returns: "Bad request: Invalid request"
✅ THIS WORKS:
# BOTH commission objects are REQUIRED
price_data = {
"dealType": "COMPENSATING",
"propertyType": "RESIDENTIAL",
"salePrice": {"amount": 500000, "currency": "USD"},
"representationType": "BUYER",
"listingCommission": { # REQUIRED
"commissionPercent": 3.0,
"percentEnabled": True,
"negativeOrEmpty": False
},
"saleCommission": { # REQUIRED
"commissionPercent": 3.0,
"percentEnabled": True,
"negativeOrEmpty": False
}
}
👥 Co-Agent Roles¶
✅ WORKING ROLES: - "REAL"
- Always works - "BUYERS_AGENT"
- Works with location data - "SELLERS_AGENT"
- Works with location data
❌ NON-WORKING ROLES: - "LISTING_AGENT"
- Always fails
🏢 Owner Agent Requirements¶
Owner agents require a specific sequence and additional fields:
- Create transaction
- Add location info (with additional fields) - REQUIRED FIRST
- Add price/date info (with commission objects) - REQUIRED SECOND
- Add buyers/sellers - REQUIRED THIRD
- THEN add owner agent (with valid
officeId
andteamId
)
🏗️ Transaction Lifecycle Overview¶
Real estate transactions in ReZEN follow a structured lifecycle:
graph LR
A[Create Builder] --> B[Add Participants]
B --> C[Set Property Info]
C --> D[Configure Finances]
D --> E[Submit Transaction]
E --> F[Manage Live Transaction]
F --> G[Process Payments]
G --> H[Generate Documents]
H --> I[Close Transaction]
👥 Handling Multiple Teams¶
Many users belong to multiple teams in ReZEN. This section shows how to handle team selection when creating transactions.
Team Selection in ReZEN
When adding an owner agent to a transaction, you must specify: - Agent ID: Your user ID (automatically retrieved) - Office ID: Your office ID (automatically retrieved) - Team ID: Which team to use (requires selection if multiple)
🔍 Discovering Available Teams¶
First, check what teams and offices you have access to:
from rezen import RezenClient
def check_user_teams():
"""Check what teams the current user belongs to."""
client = RezenClient()
# Get comprehensive team and office information
teams_info = client.transaction_builder.get_user_teams_and_offices()
print(f"Agent ID: {teams_info['agent_id']}")
print(f"Office ID: {teams_info['office_id']}")
print(f"Has multiple teams: {teams_info['has_multiple_teams']}")
if teams_info["has_multiple_teams"]:
print(f"\n🏢 You belong to {len(teams_info['teams'])} teams:")
for team in teams_info["teams"]:
is_default = team["id"] == teams_info["default_team"]["id"]
marker = " ⭐ (DEFAULT)" if is_default else ""
print(f" - {team['name']}")
print(f" Role: {team['role']}")
print(f" ID: {team['id']}{marker}")
print(f"\n🎯 Smart default team: {teams_info['default_team']['name']}")
print(" (Prefers LEADER role over ADMIN)")
else:
print(f"\n🏢 Single team: {teams_info['default_team']['name']}")
return teams_info
# Example output:
# Agent ID: 12345678-1234-1234-1234-123456789012
# Office ID: 87654321-4321-4321-4321-210987654321
# Has multiple teams: True
#
# 🏢 You belong to 2 teams:
# - The Perry Group Standard Team
# Role: LEADER
# ID: team-uuid-1 ⭐ (DEFAULT)
# - The Perry Group Marketing Group
# Role: ADMIN
# ID: team-uuid-2
#
# 🎯 Smart default team: The Perry Group Standard Team
# (Prefers LEADER role over ADMIN)
🚀 Three Approaches to Owner Agent Setup¶
Choose the approach that best fits your needs:
Use the default team with smart selection logic:
def setup_owner_agent_automatic():
"""Use automatic team selection (prefers LEADER role)."""
client = RezenClient()
# Create and setup transaction
transaction_id = create_basic_transaction()
# Set current user as owner agent with default team
result = client.transaction_builder.set_current_user_as_owner_agent(
transaction_id,
role="BUYERS_AGENT" # or "SELLERS_AGENT"
)
print("✅ Owner agent set with default team")
return result
# This method:
# - Uses smart default logic (LEADER > ADMIN > first team)
# - Shows warning if you have multiple teams
# - Handles all ID resolution automatically
Check teams first, then choose explicitly:
def setup_owner_agent_with_choice():
"""Check teams and choose which one to use."""
client = RezenClient()
# Create transaction
transaction_id = create_basic_transaction()
# Discover available teams
teams_info = client.transaction_builder.get_user_teams_and_offices()
if teams_info["has_multiple_teams"]:
print("Available teams:")
for i, team in enumerate(teams_info["teams"]):
role = team["role"]
name = team["name"]
is_default = team["id"] == teams_info["default_team"]["id"]
marker = " (DEFAULT)" if is_default else ""
print(f" {i+1}. {name} - Role: {role}{marker}")
# For demo, use default team
# In real app, you might prompt user for choice
selected_team_id = teams_info["default_team"]["id"]
print(f"Using: {teams_info['default_team']['name']}")
else:
selected_team_id = teams_info["default_team"]["id"]
print(f"Using single team: {teams_info['default_team']['name']}")
# Set owner agent with specific team
result = client.transaction_builder.set_current_user_as_owner_agent_with_team(
transaction_id,
role="BUYERS_AGENT",
team_id=selected_team_id
)
print("✅ Owner agent set with chosen team")
return result
Always specify the team ID directly:
def setup_owner_agent_explicit():
"""Always specify team explicitly for full control."""
client = RezenClient()
# Create transaction
transaction_id = create_basic_transaction()
# Get available teams
teams_info = client.transaction_builder.get_user_teams_and_offices()
# Select team by business logic (e.g., always use LEADER team)
leader_teams = [t for t in teams_info["teams"] if t["role"] == "LEADER"]
if leader_teams:
selected_team_id = leader_teams[0]["id"]
print(f"Using LEADER team: {leader_teams[0]['name']}")
else:
# Fallback to default
selected_team_id = teams_info["default_team"]["id"]
print(f"No LEADER team, using default: {teams_info['default_team']['name']}")
# Set owner agent with explicit team
result = client.transaction_builder.set_current_user_as_owner_agent_with_team(
transaction_id,
role="BUYERS_AGENT",
team_id=selected_team_id
)
print("✅ Owner agent set with explicit team selection")
return result
✅ Complete Working Example¶
Here's a full example that handles multiple teams automatically:
def create_transaction_with_owner_agent():
"""Create complete transaction with owner agent handling multiple teams."""
client = RezenClient()
try:
# Step 1: Create transaction
transaction_id = client.transaction_builder.create_transaction_builder()
print(f"Created transaction: {transaction_id}")
# Step 2: Add location with ALL required fields
location_data = {
"street": "123 Demo Leader Lane",
"city": "Salt Lake City",
"state": "UTAH",
"zip": "84101",
"county": "Salt Lake", # REQUIRED
"yearBuilt": 2020, # REQUIRED
"mlsNumber": "MLS-456789" # REQUIRED
}
client.transaction_builder.update_location_info(transaction_id, location_data)
print("✅ Added location")
# Step 3: Add price/date with commission objects
price_data = {
"dealType": "COMPENSATING",
"propertyType": "RESIDENTIAL",
"salePrice": {"amount": 750000, "currency": "USD"},
"representationType": "BUYER",
"listingCommission": { # REQUIRED
"commissionPercent": 3.0,
"percentEnabled": True,
"negativeOrEmpty": False
},
"saleCommission": { # REQUIRED
"commissionPercent": 3.0,
"percentEnabled": True,
"negativeOrEmpty": False
}
}
client.transaction_builder.update_price_and_date_info(transaction_id, price_data)
print("✅ Added price/date")
# Step 4: Add buyer
buyer_data = {
"firstName": "Demo",
"lastName": "Buyer",
"email": "demo.buyer@example.com",
"phoneNumber": "(555) 123-4567"
}
client.transaction_builder.add_buyer(transaction_id, buyer_data)
print("✅ Added buyer")
# Step 5: Set owner agent (handles multiple teams automatically)
result = client.transaction_builder.set_current_user_as_owner_agent(
transaction_id,
role="BUYERS_AGENT" # Must match representationType: BUYER -> BUYERS_AGENT
)
print("✅ Set owner agent with automatic team handling")
# Optional: Add co-agent
co_agent_info = {
"agentId": "bd465129-b224-43e3-b92f-524ea5f53783",
"role": "REAL",
"receivesInvoice": False
}
client.transaction_builder.add_co_agent(transaction_id, co_agent_info)
print("✅ Added co-agent")
return transaction_id
except Exception as e:
print(f"❌ Error: {e}")
return None
# Usage
transaction_id = create_transaction_with_owner_agent()
if transaction_id:
print(f"🎉 Successfully created transaction: {transaction_id}")
🚀 Complete Transaction Workflow¶
Phase 1: Transaction Creation¶
from rezen import RezenClient
from rezen.exceptions import RezenError
def create_basic_transaction():
"""Create a basic transaction with correct field names."""
client = RezenClient()
try:
# Step 1: Create transaction builder
response = client.transaction_builder.create_transaction_builder()
transaction_id = str(response) # Extract ID as string
print(f"✅ Created transaction: {transaction_id}")
# Step 2: Add property information
# ⚠️ CRITICAL: Basic address fields alone will FAIL!
# The API requires additional property details for successful location updates
location_data = {
"street": "123 Main Street", # Use 'street' not 'address'
"city": "Salt Lake City",
"state": "UTAH", # Must be ALL CAPS
"zip": "84101", # Use 'zip' not 'zipCode'
"county": "Salt Lake", # REQUIRED - API fails without this
"yearBuilt": 2020, # REQUIRED - API fails without this
"mlsNumber": "MLS123456" # REQUIRED - API fails without this
}
client.transaction_builder.update_location_info(
transaction_id, location_data
)
print("✅ Added property location (with required additional fields)")
# Step 3: Add price/date information
# ⚠️ CRITICAL: Both commission objects are REQUIRED!
# Basic price fields alone will FAIL with "Bad request: Invalid request"
price_date_data = {
"dealType": "COMPENSATING",
"propertyType": "RESIDENTIAL",
"salePrice": { # Must be object with amount/currency
"amount": 500000,
"currency": "USD"
},
"representationType": "BUYER",
"listingCommission": { # REQUIRED - cannot be omitted
"commissionPercent": 3.0,
"percentEnabled": True,
"negativeOrEmpty": False
},
"saleCommission": { # REQUIRED - cannot be omitted
"commissionPercent": 3.0,
"percentEnabled": True,
"negativeOrEmpty": False
}
}
client.transaction_builder.update_price_and_date_info(
transaction_id, price_date_data
)
print("✅ Added pricing (with required commission objects)")
return transaction_id
except RezenError as e:
print(f"❌ Transaction creation failed: {e}")
return None
from datetime import datetime, timedelta
def create_complete_transaction_with_coagent():
"""Create a complete transaction with all details including co-agent."""
client = RezenClient()
try:
# Create transaction builder
response = client.transaction_builder.create_transaction_builder("TRANSACTION")
transaction_id = str(response)
print(f"✅ Created transaction: {transaction_id}")
# Add location (Required field names: street, zip, state in ALL CAPS)
location_data = {
"street": "2158 E Wilson Ave",
"city": "Salt Lake City",
"state": "UTAH", # ALL CAPS required
"zip": "84108", # 'zip' not 'zipCode'
"county": "Salt Lake",
"yearBuilt": 2020,
"mlsNumber": "MLS123456"
}
client.transaction_builder.update_location_info(transaction_id, location_data)
print("✅ Added property location")
# Add price/date info
closing_date = (datetime.now() + timedelta(days=45)).strftime("%Y-%m-%d")
price_data = {
"dealType": "COMPENSATING",
"propertyType": "RESIDENTIAL",
"salePrice": { # Must be object with amount/currency
"amount": 565000,
"currency": "USD"
},
"listingCommission": {
"commissionPercent": 3.0,
"percentEnabled": True,
"negativeOrEmpty": False
},
"saleCommission": {
"commissionPercent": 3.0,
"percentEnabled": True,
"negativeOrEmpty": False
},
"acceptanceDate": datetime.now().strftime("%Y-%m-%d"),
"closingDate": closing_date,
"earnestMoney": 15000,
"downPayment": 113000,
"loanAmount": 452000,
"representationType": "BUYER"
}
client.transaction_builder.update_price_and_date_info(transaction_id, price_data)
print("✅ Added pricing and dates")
# Add buyer (use camelCase for names)
buyer_data = {
"firstName": "John", # camelCase required
"lastName": "Doe",
"email": "john.doe@example.com",
"phoneNumber": "(801) 555-1234" # camelCase required
}
client.transaction_builder.add_buyer(transaction_id, buyer_data)
print("✅ Added buyer")
# Add seller
seller_data = {
"firstName": "Jane",
"lastName": "Smith",
"email": "jane.smith@example.com",
"phoneNumber": "(801) 555-5678"
}
client.transaction_builder.add_seller(transaction_id, seller_data)
print("✅ Added seller")
# Add co-agent (can be added at any time)
co_agent_info = {
"agentId": "bd465129-b224-43e3-b92f-524ea5f53783",
"role": "REAL",
"receivesInvoice": False,
"opCityReferral": False,
"optedInForEcp": False
}
client.transaction_builder.add_co_agent(transaction_id, co_agent_info)
print("✅ Added co-agent")
# Add title company
title_info = {
"company": "Premier Title Company",
"firstName": "Sarah",
"lastName": "Johnson",
"email": "sarah@premiertitle.com",
"phoneNumber": "(801) 555-9999"
}
client.transaction_builder.update_title_info(transaction_id, title_info)
print("✅ Added title company")
# Add mortgage info
mortgage_info = {
"lenderName": "First National Bank",
"lenderContact": "Mike Banker",
"lenderPhone": "(801) 555-2468",
"lenderEmail": "mike@firstnational.com"
}
client.transaction_builder.update_mortgage_info(transaction_id, mortgage_info)
print("✅ Added mortgage information")
return transaction_id
except RezenError as e:
print(f"❌ Transaction creation failed: {e}")
print(f" Error type: {type(e).__name__}")
return None
def create_advanced_transaction():
"""Create a transaction with complete configuration."""
client = RezenClient()
try:
# Create transaction builder
response = client.transaction_builder.create_transaction_builder(
builder_type="TRANSACTION"
)
transaction_id = str(response) # Extract ID as string
# Add detailed property information
location_data = {
"street": "456 Oak Avenue", # Use 'street' not 'address'
"city": "Springfield",
"state": "CALIFORNIA", # ALL CAPS
"zip": "90210", # Use 'zip' not 'zipCode'
"county": "Los Angeles",
"subdivision": "Oak Grove Estates",
"unit": "Unit 2B"
}
client.transaction_builder.update_location_info(
transaction_id, location_data
)
# Set pricing and dates
price_data = {
"dealType": "COMPENSATING",
"propertyType": "RESIDENTIAL",
"salePrice": { # Must be object
"amount": 750000,
"currency": "USD"
},
"earnestMoney": 15000, # Use camelCase
"downPayment": 150000,
"loanAmount": 600000,
"acceptanceDate": "2024-02-01", # Use camelCase
"closingDate": "2024-03-15"
}
client.transaction_builder.update_price_and_date_info(
transaction_id, price_data
)
return transaction_id
except RezenError as e:
print(f"❌ Advanced transaction setup failed: {e}")
return None
Phase 2: Participant Management¶
def add_core_participants(transaction_id: str):
"""Add buyer, seller, and agents to the transaction."""
client = RezenClient()
try:
# Add buyer (use camelCase for field names)
buyer_data = {
"firstName": "John", # Use camelCase
"lastName": "Doe", # Use camelCase
"email": "john.doe@email.com",
"phoneNumber": "(555) 123-4567" # Use camelCase
}
client.transaction_builder.add_buyer(transaction_id, buyer_data)
print("✅ Added buyer")
# Add seller
seller_data = {
"firstName": "Jane",
"lastName": "Smith",
"email": "jane.smith@email.com",
"phoneNumber": "(555) 987-6543"
}
client.transaction_builder.add_seller(transaction_id, seller_data)
print("✅ Added seller")
# Add co-agent (can be added at any time)
co_agent_data = {
"agentId": "bd465129-b224-43e3-b92f-524ea5f53783",
"role": "REAL", # Will appear as BUYERS_AGENT/SELLERS_AGENT based on representationType
"receivesInvoice": False
}
client.transaction_builder.add_co_agent(transaction_id, co_agent_data)
print("✅ Added co-agent")
return True
except RezenError as e:
print(f"❌ Failed to add participants: {e}")
return False
def add_service_providers(transaction_id: str):
"""Add all necessary service providers."""
client = RezenClient()
service_providers = [
{
"type": "INSPECTOR",
"firstName": "Mike", # Use camelCase
"lastName": "Inspector", # Use camelCase
"company": "Quality Inspections Inc",
"phoneNumber": "(555) INSPECT", # Use camelCase
"email": "mike@qualityinspections.com"
},
{
"type": "LENDER",
"firstName": "Sarah",
"lastName": "Banker",
"company": "First National Bank",
"phoneNumber": "(555) 555-LOAN",
"email": "sarah@firstnational.com"
},
{
"type": "APPRAISER",
"firstName": "Bob",
"lastName": "Appraiser",
"company": "Accurate Appraisals",
"phoneNumber": "(555) 555-VALU",
"email": "bob@accurateappraisals.com"
}
]
try:
for provider in service_providers:
client.transaction_builder.add_participant(
transaction_id, provider
)
print(f"✅ Added {provider['type'].lower()}")
return True
except RezenError as e:
print(f"❌ Failed to add service providers: {e}")
return False
Phase 3: Financial Configuration¶
def setup_commission_splits(transaction_id: str):
"""Configure commission splits between agents."""
client = RezenClient()
try:
# Set commission payer
payer_data = {
"payer_type": "SELLER",
"commission_rate": 6.0,
"flat_fee": False
}
client.transaction_builder.add_commission_payer(
transaction_id, payer_data
)
print("✅ Set commission payer")
# Configure commission splits
commission_splits = [
{
"agent_id": "listing-agent-uuid",
"split_percentage": 50.0,
"commission_amount": 22500 # 3% of $750k
},
{
"agent_id": "buyer-agent-uuid",
"split_percentage": 50.0,
"commission_amount": 22500 # 3% of $750k
}
]
client.transaction_builder.update_commission_splits(
transaction_id, commission_splits
)
print("✅ Configured commission splits")
return True
except RezenError as e:
print(f"❌ Commission setup failed: {e}")
return False
def setup_title_company(transaction_id: str):
"""Add title company information."""
client = RezenClient()
try:
title_data = {
"title_company": "Premier Title Co",
"title_contact": "Sarah Johnson",
"title_phone": "+1-555-789-0123",
"title_email": "sarah@premiertitle.com",
"title_address": "789 Title Lane, Title City, TC 54321",
"policy_number": "PT-2024-001234"
}
client.transaction_builder.update_title_info(
transaction_id, title_data
)
print("✅ Added title company information")
return True
except RezenError as e:
print(f"❌ Title setup failed: {e}")
return False
Phase 4: Transaction Submission & Management¶
def submit_and_manage_transaction(transaction_id: str):
"""Submit transaction and begin management phase."""
client = RezenClient()
try:
# Submit the transaction
submit_response = client.transaction_builder.submit_transaction(
transaction_id
)
print("✅ Transaction submitted successfully")
# Get the live transaction ID
live_transaction_id = submit_response.get('live_transaction_id', transaction_id)
# Switch to transactions API for ongoing management
transaction = client.transactions.get_transaction(live_transaction_id)
print(f"✅ Retrieved live transaction: {transaction['status']}")
return live_transaction_id
except RezenError as e:
print(f"❌ Transaction submission failed: {e}")
return None
def monitor_transaction_progress(transaction_id: str):
"""Monitor and update transaction progress."""
client = RezenClient()
try:
# Get current transaction status
transaction = client.transactions.get_transaction(transaction_id)
print(f"📊 Transaction Status: {transaction['status']}")
print(f"🏠 Property: {transaction.get('property', {}).get('address', 'N/A')}")
print(f"💰 Purchase Price: ${transaction.get('purchase_price', 0):,}")
# Check for pending tasks or issues
if transaction.get('status') == 'PENDING_INSPECTION':
print("⏰ Waiting for inspection to be completed")
elif transaction.get('status') == 'PENDING_APPRAISAL':
print("⏰ Waiting for appraisal results")
elif transaction.get('status') == 'PENDING_FINANCING':
print("⏰ Waiting for loan approval")
return transaction
except RezenError as e:
print(f"❌ Failed to retrieve transaction: {e}")
return None
💰 Payment Processing¶
Earnest Money & Deposits¶
def process_earnest_money(transaction_id: str, amount: float):
"""Process earnest money deposit."""
client = RezenClient()
try:
# Get payment information
payment_info = client.transactions.get_payment_info(
transaction_id, "buyer-agent-uuid"
)
# Process the earnest money
# Note: This would typically integrate with actual payment processing
print(f"💰 Processing earnest money: ${amount:,}")
print(f"📋 Payment details: {payment_info}")
return True
except RezenError as e:
print(f"❌ Payment processing failed: {e}")
return False
Money Transfers¶
def track_money_transfers(transaction_id: str):
"""Track all money transfers for the transaction."""
client = RezenClient()
try:
transfers = client.transactions.get_money_transfers(transaction_id)
print("💸 Money Transfers:")
for transfer in transfers:
print(f" - ${transfer.get('amount', 0):,} - {transfer.get('status', 'Unknown')}")
print(f" Type: {transfer.get('type', 'N/A')}")
print(f" Date: {transfer.get('date', 'N/A')}")
return transfers
except RezenError as e:
print(f"❌ Failed to get money transfers: {e}")
return []
📄 Document Generation¶
Transaction Summary¶
def generate_transaction_summary(transaction_id: str):
"""Generate and save transaction summary PDF."""
client = RezenClient()
try:
# Generate PDF
pdf_response = client.transactions.get_transaction_summary_pdf(transaction_id)
# Save to file (example)
filename = f"transaction_summary_{transaction_id}.pdf"
with open(filename, 'wb') as f:
f.write(pdf_response.content)
print(f"📄 Transaction summary saved as: {filename}")
return filename
except RezenError as e:
print(f"❌ Failed to generate summary: {e}")
return None
🏁 Complete Workflow Example¶
def complete_transaction_workflow():
"""Execute a complete transaction workflow from start to finish."""
print("🚀 Starting complete transaction workflow...")
# Phase 1: Create transaction
transaction_id = create_advanced_transaction()
if not transaction_id:
return False
# Phase 2: Add participants
if not add_core_participants(transaction_id):
return False
if not add_service_providers(transaction_id):
return False
# Phase 3: Configure finances
if not setup_commission_splits(transaction_id):
return False
if not setup_title_company(transaction_id):
return False
# Phase 4: Submit and manage
live_transaction_id = submit_and_manage_transaction(transaction_id)
if not live_transaction_id:
return False
# Phase 5: Monitor progress
transaction = monitor_transaction_progress(live_transaction_id)
if not transaction:
return False
# Phase 6: Process payments
process_earnest_money(live_transaction_id, 15000)
# Phase 7: Generate documents
generate_transaction_summary(live_transaction_id)
print("🎉 Transaction workflow completed successfully!")
return True
# Run the complete workflow
if __name__ == "__main__":
complete_transaction_workflow()
🛠️ Best Practices¶
Error Handling¶
Robust Error Handling
from rezen.exceptions import ValidationError, NotFoundError
def safe_transaction_operation(transaction_id: str):
try:
# Transaction operation
result = client.transactions.get_transaction(transaction_id)
return {"success": True, "data": result}
except ValidationError as e:
return {
"success": False,
"error": "Invalid data",
"details": e.invalid_fields
}
except NotFoundError:
return {
"success": False,
"error": "Transaction not found"
}
except RezenError as e:
return {
"success": False,
"error": str(e)
}
Data Validation¶
Always Validate Input
def validate_transaction_data(data: dict) -> bool:
"""Validate transaction data before submission."""
required_fields = ['address', 'city', 'state', 'zipCode']
for field in required_fields:
if not data.get(field):
print(f"❌ Missing required field: {field}")
return False
if data.get('purchase_price', 0) <= 0:
print("❌ Purchase price must be greater than 0")
return False
return True
Progress Tracking¶
Track Transaction Progress
def track_transaction_milestones(transaction_id: str):
"""Track key milestones in the transaction."""
milestones = {
'CREATED': '✅ Transaction created',
'PARTICIPANTS_ADDED': '✅ All participants added',
'UNDER_CONTRACT': '✅ Under contract',
'INSPECTION_COMPLETE': '✅ Inspection completed',
'APPRAISAL_COMPLETE': '✅ Appraisal completed',
'FINANCING_APPROVED': '✅ Financing approved',
'CLOSING_SCHEDULED': '✅ Closing scheduled',
'CLOSED': '🎉 Transaction closed'
}
transaction = client.transactions.get_transaction(transaction_id)
current_status = transaction.get('status')
print(f"Current Status: {milestones.get(current_status, current_status)}")
🔗 Related Documentation¶
- Transaction Builder API - Detailed API reference
- Transactions API - Live transaction management
- Error Handling - Comprehensive error handling
- Examples - More code examples and tutorials
⚠️ Important Field Name Requirements¶
Field Naming Conventions¶
The ReZEN API has specific field name requirements that must be followed exactly to avoid validation errors:
Critical Field Names
Field Type | ❌ WRONG | ✅ CORRECT | Notes |
---|---|---|---|
Location | |||
Street | address | street | Primary street address |
ZIP Code | zipCode , zip_code | zip | Must use 'zip' only |
State | state: "ca" | state: "CALIFORNIA" | MUST BE ALL CAPS |
Contact Info | |||
First Name | first_name , firstname | firstName | camelCase required |
Last Name | last_name , lastname | lastName | camelCase required |
Phone | phone , phone_number | phoneNumber | camelCase required |
Financial | |||
Sale Price | salePrice: 500000 | salePrice: {amount: 500000, currency: "USD"} | Must be object |
Earnest Money | earnest_money | earnestMoney | camelCase required |
Down Payment | down_payment | downPayment | camelCase required |
Loan Amount | loan_amount | loanAmount | camelCase required |
Dates | |||
Contract Date | contract_date | contractDate | camelCase, format: YYYY-MM-DD |
Closing Date | closing_date | closingDate | camelCase, format: YYYY-MM-DD |
Acceptance Date | acceptance_date | acceptanceDate | camelCase, format: YYYY-MM-DD |
Common Gotchas¶
Watch Out For These Issues
-
State Names Must Be ALL CAPS
-
Sale Price Must Be An Object
-
Co-Agents Can Be Added Anytime
# Co-agents don't require specific sequence co_agent_info = { "agentId": "agent-uuid-here", "role": "REAL", # May display as BUYERS_AGENT/SELLERS_AGENT "receivesInvoice": False } # Can be added immediately after transaction creation client.transaction_builder.add_co_agent(transaction_id, co_agent_info)
-
Owner Agents Require Specific Sequence
Field Validation Helper¶
Use This Helper Function
def validate_and_fix_field_names(data: dict) -> dict:
"""Helper to convert common field name mistakes."""
field_mappings = {
# Location fields
"address": "street",
"zipCode": "zip",
"zip_code": "zip",
# Name fields
"first_name": "firstName",
"last_name": "lastName",
"phone": "phoneNumber",
"phone_number": "phoneNumber",
# Financial fields
"earnest_money": "earnestMoney",
"down_payment": "downPayment",
"loan_amount": "loanAmount",
# Date fields
"contract_date": "contractDate",
"closing_date": "closingDate",
"acceptance_date": "acceptanceDate"
}
# Create new dict with corrected field names
corrected = {}
for key, value in data.items():
new_key = field_mappings.get(key, key)
corrected[new_key] = value
# Fix state to uppercase if present
if "state" in corrected and isinstance(corrected["state"], str):
corrected["state"] = corrected["state"].upper()
return corrected
🔧 Troubleshooting & API Testing Results¶
This section documents comprehensive testing results to help you avoid common API errors and understand which methods work reliably.
✅ Confirmed Working Methods¶
Core Operations¶
- ✅
create_transaction_builder()
- Always works - ✅
get_transaction_builder()
- Always works - ✅
delete_transaction_builder()
- Presumed working - ✅
create_listing_builder()
- Always works
Location Operations¶
- ✅
update_location_info()
- Only with additional fields - Required:
county
,yearBuilt
,mlsNumber
(beyond basic address) - Field names:
street
(notaddress
),zip
(notzipCode
) - State format: ALL CAPS (e.g., "UTAH", "CALIFORNIA")
Price/Date Operations¶
- ✅
update_price_and_date_info()
- Only with both commission objects - Required: BOTH
listingCommission
ANDsaleCommission
- salePrice format:
{"amount": 500000, "currency": "USD"}
(not simple number)
Participant Operations¶
- ✅
add_buyer()
- Works on transactions with location data - ✅
add_seller()
- Works on transactions with location data - ✅
add_co_agent()
with specific roles: - ✅
role: "REAL"
- Always works - ✅
role: "BUYERS_AGENT"
- Works with location data - ✅
role: "SELLERS_AGENT"
- Works with location data
❌ Methods That Don't Work¶
Owner Agent Operations¶
- ❌
update_owner_agent_info()
- Requires validofficeId
andteamId
- Error: "Missing required field 'officeId'" or "Missing required field 'teamId'"
- Sequence: Must follow exact order (location → price → participants → owner)
Limited Co-Agent Roles¶
- ❌
add_co_agent()
withrole: "LISTING_AGENT"
- Always fails
Service Provider Operations¶
- ❌ Most service provider methods - Dependencies not fully tested
⚠️ Common Error Patterns¶
"Bad request: Invalid request"¶
This generic error typically means:
- Missing additional fields in location updates
- Missing commission objects in price/date updates
- Invalid co-agent role (e.g., "LISTING_AGENT")
- Wrong sequence for owner agent operations
"Missing required field 'X'"¶
- officeId/teamId: Owner agent operations need valid UUIDs
- Commission fields: Price/date updates need both commission objects
InvalidFieldNameError¶
- Using
address
instead ofstreet
- Using
zipCode
instead ofzip
- Using snake_case instead of camelCase
InvalidFieldValueError¶
- State not in ALL CAPS
- salePrice as number instead of object
- Invalid representationType values
🛠️ Debugging Checklist¶
When transaction operations fail, check:
For Location Updates: - [ ] Included county
field - [ ] Included yearBuilt
field (number) - [ ] Included mlsNumber
field - [ ] Used street
not address
- [ ] Used zip
not zipCode
- [ ] State is ALL CAPS
For Price/Date Updates: - [ ] Included listingCommission
object - [ ] Included saleCommission
object - [ ] salePrice is object: {"amount": X, "currency": "USD"}
- [ ] Used camelCase for date fields
For Co-Agents: - [ ] Using working role: "REAL", "BUYERS_AGENT", or "SELLERS_AGENT" - [ ] Not using "LISTING_AGENT" role - [ ] Valid agent UUID
For Owner Agents: - [ ] Transaction has location data - [ ] Transaction has price/date data
- [ ] Transaction has participants (buyer/seller) - [ ] Have valid officeId
UUID - [ ] Have valid teamId
UUID - [ ] Role matches representationType
📋 Tested Working Sequence¶
This sequence is confirmed to work for creating complete transactions:
# 1. Create transaction
transaction_id = client.transaction_builder.create_transaction_builder()
# 2. Add location (with additional required fields)
location_data = {
"street": "123 Main Street",
"city": "Salt Lake City",
"state": "UTAH",
"zip": "84101",
"county": "Salt Lake", # REQUIRED
"yearBuilt": 2020, # REQUIRED
"mlsNumber": "MLS123456" # REQUIRED
}
client.transaction_builder.update_location_info(transaction_id, location_data)
# 3. Add price/date (with both commission objects)
price_data = {
"dealType": "COMPENSATING",
"propertyType": "RESIDENTIAL",
"salePrice": {"amount": 550000, "currency": "USD"},
"representationType": "BUYER",
"listingCommission": { # REQUIRED
"commissionPercent": 3.0,
"percentEnabled": True,
"negativeOrEmpty": False
},
"saleCommission": { # REQUIRED
"commissionPercent": 3.0,
"percentEnabled": True,
"negativeOrEmpty": False
}
}
client.transaction_builder.update_price_and_date_info(transaction_id, price_data)
# 4. Add participants
client.transaction_builder.add_buyer(transaction_id, {
"firstName": "John",
"lastName": "Buyer",
"email": "john@example.com",
"phoneNumber": "(801) 555-1234"
})
client.transaction_builder.add_seller(transaction_id, {
"firstName": "Jane",
"lastName": "Seller",
"email": "jane@example.com",
"phoneNumber": "(801) 555-5678"
})
# 5. Add co-agents (working roles)
client.transaction_builder.add_co_agent(transaction_id, {
"agentId": "bd465129-b224-43e3-b92f-524ea5f53783",
"role": "REAL",
"receivesInvoice": False
})
# Result: Complete working transaction with all components
This sequence has been tested and confirmed to work without errors.
🏢 Handling Multiple Teams & Offices¶
Real-world scenario: Many agents belong to multiple teams and offices. When creating transactions, you need to specify which team/office to use for the transaction.
🔍 Discovering Available Teams¶
Use get_user_teams_and_offices()
to see all available options:
from typing import Dict, Any
from rezen import RezenClient
client: RezenClient = RezenClient()
# Get user's available teams and offices
teams_info: Dict[str, Any] = client.transaction_builder.get_user_teams_and_offices()
print(f"Total teams: {teams_info['totalTeams']}")
print(f"Has multiple teams: {teams_info['hasMultipleTeams']}")
print(f"Default team: {teams_info['defaultTeam']}")
# List all available teams
for team in teams_info['teams']:
print(f"Team: {team['teamName']}")
print(f" ID: {team['teamId']}")
print(f" Type: {team['teamType']}")
print(f" Roles: {team['roles']}")
🎯 Owner Agent Team Selection¶
You have 3 options for setting owner agents:
Best for: Users with single team or when default is acceptable
Best for: Users with multiple teams who need control
# First, discover available teams
teams = client.transaction_builder.get_user_teams_and_offices()
if teams["hasMultipleTeams"]:
# Choose specific team (e.g., first team)
selected_team_id = teams["teams"][0]["teamId"]
# Set owner agent with explicit team
result = client.transaction_builder.set_current_user_as_owner_agent_with_team(
builder_id,
role="BUYERS_AGENT",
team_id=selected_team_id
)
print(f"✅ Used team: {teams['teams'][0]['teamName']}")
else:
# Use convenience method for single team
result = client.transaction_builder.set_current_user_as_owner_agent(
builder_id, role="BUYERS_AGENT"
)
Best for: Applications with user interface
def choose_team_interactively() -> str:
teams = client.transaction_builder.get_user_teams_and_offices()
if not teams["hasMultipleTeams"]:
return teams["defaultTeam"]
print("Available teams:")
for i, team in enumerate(teams["teams"]):
print(f" {i+1}. {team['teamName']} ({team['teamType']})")
while True:
try:
choice = int(input("Select team (1-{}): ".format(len(teams["teams"]))))
if 1 <= choice <= len(teams["teams"]):
return teams["teams"][choice-1]["teamId"]
except ValueError:
pass
print("Invalid choice. Try again.")
# Use interactive selection
selected_team = choose_team_interactively()
result = client.transaction_builder.set_current_user_as_owner_agent_with_team(
builder_id,
role="BUYERS_AGENT",
team_id=selected_team
)
🛡️ Error Handling¶
Handle team-related errors gracefully:
from rezen.exceptions import ValidationError
try:
teams = client.transaction_builder.get_user_teams_and_offices()
if teams["hasMultipleTeams"]:
# User choice logic here...
selected_team = "user-selected-team-id"
result = client.transaction_builder.set_current_user_as_owner_agent_with_team(
builder_id,
role="BUYERS_AGENT",
team_id=selected_team
)
else:
result = client.transaction_builder.set_current_user_as_owner_agent(
builder_id, role="BUYERS_AGENT"
)
except ValidationError as e:
if "not a member of team" in str(e):
print("❌ Invalid team selected. Please choose from available teams.")
elif "must belong to at least one team" in str(e):
print("❌ User has no team memberships. Contact administrator.")
else:
print(f"❌ Validation error: {e}")
💡 Best Practices¶
Team Selection Guidelines
- Always check for multiple teams before setting owner agents
- Provide clear team information to users (team name, type, roles)
- Use default team logic for convenience when appropriate
- Handle team validation errors gracefully in production
- Consider team types - some teams may be for specific purposes
Office ID Requirements
Current implementation requires users to have an officeId
in their profile. If team-specific offices become available in the API, this may need enhancement.