Skip to main content

Python wrapper for Dotloop API - Real estate transaction management and document handling

Project description

Dotloop Python API Wrapper

A comprehensive Python wrapper for the Dotloop API v2, providing easy access to real estate transaction management and document handling functionality.

Features

  • Complete API Coverage: Implements all major Dotloop API endpoints
  • Type Safety: Full type hints and Pydantic validation
  • Error Handling: Comprehensive exception handling with detailed error messages
  • Easy to Use: Intuitive client interface with method chaining
  • Well Documented: Extensive docstrings and examples
  • Testing: 100% test coverage with pytest

Installation

pip install dotloop-api

Quick Start

from dotloop import DotloopClient, TransactionType, LoopStatus

# Initialize the client
client = DotloopClient(api_key="your_api_key")

# Or use environment variable DOTLOOP_API_KEY
client = DotloopClient()

# Get account information
account = client.account.get_account()
print(f"Account: {account['data']['firstName']} {account['data']['lastName']}")

# List profiles
profiles = client.profile.list_profiles()
for profile in profiles['data']:
    print(f"Profile: {profile['name']}")

# Create a new loop
loop = client.loop_it.create_loop(
    name="123 Main St Property",
    transaction_type=TransactionType.PURCHASE_OFFER,
    status=LoopStatus.PRE_OFFER,
    profile_id=123,
    street_number="123",
    street_name="Main St",
    city="San Francisco",
    state="CA",
    zip_code="94105"
)

Authentication

The Dotloop API uses OAuth 2.0 Bearer tokens for authentication. You can obtain an access token through the OAuth flow and then use it with this wrapper.

Setting up Authentication

  1. Environment Variable (Recommended):

    export DOTLOOP_API_KEY="your_access_token"
    
  2. Direct Parameter:

    client = DotloopClient(api_key="your_access_token")
    

Available Endpoints

Account

  • client.account.get_account() - Get current account details

Profiles

  • client.profile.list_profiles() - List all profiles
  • client.profile.get_profile(profile_id) - Get profile by ID
  • client.profile.create_profile(...) - Create a new profile
  • client.profile.update_profile(profile_id, ...) - Update profile

Loops

  • client.loop.list_loops(profile_id, ...) - List loops for a profile
  • client.loop.get_loop(profile_id, loop_id) - Get loop by ID
  • client.loop.create_loop(profile_id, ...) - Create a new loop
  • client.loop.update_loop(profile_id, loop_id, ...) - Update loop

Loop-It (Simplified Loop Creation)

  • client.loop_it.create_loop(...) - Create loop with property and participant data

Contacts

  • client.contact.list_contacts() - List all contacts
  • client.contact.get_contact(contact_id) - Get contact by ID
  • client.contact.create_contact(...) - Create a new contact
  • client.contact.update_contact(contact_id, ...) - Update contact
  • client.contact.delete_contact(contact_id) - Delete contact

Webhooks

Dotloop webhook subscriptions are created against a (targetType, targetId) pair, and event types are target-specific. In practice, “subscribe to everything valid” usually means two subscriptions:

  • PROFILE target (account.defaultProfileId): loop + participant events
  • USER target (account.id): contact + profile membership events

Use ensure_default_subscriptions() to set this up idempotently:

from dotloop import DotloopClient

client = DotloopClient()

results = client.webhook.ensure_default_subscriptions(
    "https://example.com/dotloop-webhook",
    enabled=True,
    signing_key="super_secret_key",  # optional
    external_id="my_external_id",    # optional
)

for result in results:
    print(result.action, result.target_type, result.target_id, result.subscription_id)

Note: Dotloop rejects invalid eventTypes + targetType combinations. Use SUPPORTED_WEBHOOK_EVENT_TYPES_BY_TARGET (and/or the ensure helpers above) to avoid request validation errors.

Examples

Creating a Complete Real Estate Transaction

from dotloop import DotloopClient, TransactionType, LoopStatus, ParticipantRole

client = DotloopClient()

# Create a comprehensive loop with property and participants
loop = client.loop_it.create_loop(
    name="John Doe - 456 Oak Avenue",
    transaction_type=TransactionType.PURCHASE_OFFER,
    status=LoopStatus.PRE_OFFER,
    profile_id=123,
    
    # Property information
    street_number="456",
    street_name="Oak Avenue",
    city="Los Angeles",
    state="CA",
    zip_code="90210",
    
    # Participants
    participants=[
        {
            "fullName": "John Doe",
            "email": "john.doe@example.com",
            "role": ParticipantRole.BUYER.value
        },
        {
            "fullName": "Jane Smith",
            "email": "jane.smith@realty.com",
            "role": ParticipantRole.BUYING_AGENT.value
        },
        {
            "fullName": "Bob Johnson",
            "email": "bob.johnson@realty.com",
            "role": ParticipantRole.LISTING_AGENT.value
        }
    ],
    
    # Optional MLS information
    mls_property_id="ML123456",
    template_id=1001
)

print(f"Created loop: {loop['data']['loopUrl']}")

Managing Contacts

# Create a new contact
contact = client.contact.create_contact(
    first_name="Alice",
    last_name="Johnson",
    email="alice.johnson@example.com",
    phone="+1 (555) 123-4567",
    company="Johnson Realty"
)

# Update the contact
updated_contact = client.contact.update_contact(
    contact_id=contact['data']['id'],
    phone="+1 (555) 987-6543",
    company="Johnson Premium Realty"
)

# List all contacts
contacts = client.contact.list_contacts()
for contact in contacts['data']:
    print(f"{contact['firstName']} {contact['lastName']} - {contact.get('email', 'No email')}")

Working with Profiles

# Create a new profile
profile = client.profile.create_profile(
    name="My Real Estate Business",
    company="ABC Realty",
    phone="+1 (555) 123-4567",
    address="123 Business Ave",
    city="New York",
    state="NY",
    zip_code="10001"
)

# List loops for this profile
loops = client.loop.list_loops(
    profile_id=profile['data']['id'],
    batch_size=50,
    sort="updated:desc",
    include_details=True
)

Error Handling

The wrapper provides comprehensive error handling with specific exception types:

from dotloop import DotloopClient
from dotloop.exceptions import (
    AuthenticationError,
    NotFoundError,
    ValidationError,
    RateLimitError
)

client = DotloopClient()

try:
    account = client.account.get_account()
except AuthenticationError:
    print("Invalid or expired API token")
except NotFoundError:
    print("Resource not found")
except ValidationError as e:
    print(f"Invalid parameters: {e}")
except RateLimitError:
    print("Rate limit exceeded, please wait")

Enums and Constants

The wrapper provides enums for common values:

from dotloop import (
    TransactionType,
    LoopStatus,
    ParticipantRole,
    SortDirection
)

# Transaction types
TransactionType.PURCHASE_OFFER
TransactionType.LISTING_FOR_SALE
TransactionType.PURCHASED
TransactionType.SOLD

# Loop statuses
LoopStatus.PRE_OFFER
LoopStatus.UNDER_CONTRACT
LoopStatus.SOLD
LoopStatus.ARCHIVED

# Participant roles
ParticipantRole.BUYER
ParticipantRole.SELLER
ParticipantRole.LISTING_AGENT
ParticipantRole.BUYING_AGENT

Development

Setting up Development Environment

# Clone the repository
git clone https://github.com/your-org/dotloop-python.git
cd dotloop-python

# Create virtual environment
python -m venv venv
source venv/bin/activate  # On Windows: venv\Scripts\activate

# Install dependencies
pip install -r requirements-dev.txt

# Run tests
pytest

# Run tests with coverage
pytest --cov=dotloop --cov-report=html

Running Tests

# Run all tests
pytest

# Run with coverage
pytest --cov=dotloop

# Run specific test file
pytest tests/test_account.py

# Run with verbose output
pytest -v

API Reference

For detailed API documentation, see the official Dotloop API documentation.

Contributing

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

License

This project is licensed under the MIT License - see the LICENSE file for details.

Support

Changelog

v1.0.0

  • Initial release
  • Complete implementation of core Dotloop API endpoints
  • Account, Profile, Loop, Loop-It, and Contact management
  • Comprehensive error handling and type safety
  • 100% test coverage

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

dotloop-1.3.0.tar.gz (65.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

dotloop-1.3.0-py3-none-any.whl (42.6 kB view details)

Uploaded Python 3

File details

Details for the file dotloop-1.3.0.tar.gz.

File metadata

  • Download URL: dotloop-1.3.0.tar.gz
  • Upload date:
  • Size: 65.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for dotloop-1.3.0.tar.gz
Algorithm Hash digest
SHA256 93f1e438204c943ae49da718d380f44b11b3e3b2cf4487f3b56c6bfc0981504d
MD5 75a00a2e2a61368a508fad5bdd1c9ff8
BLAKE2b-256 3ff19922e32664c1a178185d2d60137354dd2f15787ef5fbc952db803eccd021

See more details on using hashes here.

File details

Details for the file dotloop-1.3.0-py3-none-any.whl.

File metadata

  • Download URL: dotloop-1.3.0-py3-none-any.whl
  • Upload date:
  • Size: 42.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for dotloop-1.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 8d83cc92f1f090dd31ab70e13b4f172878fb75873bd90808376ae5e571ca8161
MD5 fccfbd409fb38ff7b26611d21a02b1f4
BLAKE2b-256 41f5227bf233b9277db6f5803ba2868ddeda137a77b248346f691382c9133b31

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page