# 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
```bash
pip install dotloop-api
```
## Quick Start
```python
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):
```bash
export DOTLOOP_API_KEY="your_access_token"
```
2. **Direct Parameter**:
```python
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
## Examples
### Creating a Complete Real Estate Transaction
```python
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
```python
# 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
```python
# 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:
```python
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:
```python
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
```bash
# 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
```bash
# 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](https://dotloop.github.io/public-api/).
## 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](LICENSE) file for details.
## Support
- **Documentation**: [API Documentation](https://dotloop.github.io/public-api/)
- **Issues**: [GitHub Issues](https://github.com/your-org/dotloop-python/issues)
- **Email**: dev@theperry.group
## 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
Raw data
{
"_id": null,
"home_page": null,
"name": "dotloop",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": "The Perry Group <dev@theperry.group>",
"keywords": "real-estate, api, dotloop, transactions, documents, real-estate-api, property-management, real-estate-transactions",
"author": null,
"author_email": "The Perry Group <dev@theperry.group>",
"download_url": "https://files.pythonhosted.org/packages/39/36/7474b3918b55fbee06b7a215b8689f8bfb43d0add569d753fab8718c5bbc/dotloop-1.1.2.tar.gz",
"platform": null,
"description": "# Dotloop Python API Wrapper\n\nA comprehensive Python wrapper for the Dotloop API v2, providing easy access to real estate transaction management and document handling functionality.\n\n## Features\n\n- **Complete API Coverage**: Implements all major Dotloop API endpoints\n- **Type Safety**: Full type hints and Pydantic validation\n- **Error Handling**: Comprehensive exception handling with detailed error messages\n- **Easy to Use**: Intuitive client interface with method chaining\n- **Well Documented**: Extensive docstrings and examples\n- **Testing**: 100% test coverage with pytest\n\n## Installation\n\n```bash\npip install dotloop-api\n```\n\n## Quick Start\n\n```python\nfrom dotloop import DotloopClient, TransactionType, LoopStatus\n\n# Initialize the client\nclient = DotloopClient(api_key=\"your_api_key\")\n\n# Or use environment variable DOTLOOP_API_KEY\nclient = DotloopClient()\n\n# Get account information\naccount = client.account.get_account()\nprint(f\"Account: {account['data']['firstName']} {account['data']['lastName']}\")\n\n# List profiles\nprofiles = client.profile.list_profiles()\nfor profile in profiles['data']:\n print(f\"Profile: {profile['name']}\")\n\n# Create a new loop\nloop = client.loop_it.create_loop(\n name=\"123 Main St Property\",\n transaction_type=TransactionType.PURCHASE_OFFER,\n status=LoopStatus.PRE_OFFER,\n profile_id=123,\n street_number=\"123\",\n street_name=\"Main St\",\n city=\"San Francisco\",\n state=\"CA\",\n zip_code=\"94105\"\n)\n```\n\n## Authentication\n\nThe 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.\n\n### Setting up Authentication\n\n1. **Environment Variable** (Recommended):\n ```bash\n export DOTLOOP_API_KEY=\"your_access_token\"\n ```\n\n2. **Direct Parameter**:\n ```python\n client = DotloopClient(api_key=\"your_access_token\")\n ```\n\n## Available Endpoints\n\n### Account\n- `client.account.get_account()` - Get current account details\n\n### Profiles\n- `client.profile.list_profiles()` - List all profiles\n- `client.profile.get_profile(profile_id)` - Get profile by ID\n- `client.profile.create_profile(...)` - Create a new profile\n- `client.profile.update_profile(profile_id, ...)` - Update profile\n\n### Loops\n- `client.loop.list_loops(profile_id, ...)` - List loops for a profile\n- `client.loop.get_loop(profile_id, loop_id)` - Get loop by ID\n- `client.loop.create_loop(profile_id, ...)` - Create a new loop\n- `client.loop.update_loop(profile_id, loop_id, ...)` - Update loop\n\n### Loop-It (Simplified Loop Creation)\n- `client.loop_it.create_loop(...)` - Create loop with property and participant data\n\n### Contacts\n- `client.contact.list_contacts()` - List all contacts\n- `client.contact.get_contact(contact_id)` - Get contact by ID\n- `client.contact.create_contact(...)` - Create a new contact\n- `client.contact.update_contact(contact_id, ...)` - Update contact\n- `client.contact.delete_contact(contact_id)` - Delete contact\n\n## Examples\n\n### Creating a Complete Real Estate Transaction\n\n```python\nfrom dotloop import DotloopClient, TransactionType, LoopStatus, ParticipantRole\n\nclient = DotloopClient()\n\n# Create a comprehensive loop with property and participants\nloop = client.loop_it.create_loop(\n name=\"John Doe - 456 Oak Avenue\",\n transaction_type=TransactionType.PURCHASE_OFFER,\n status=LoopStatus.PRE_OFFER,\n profile_id=123,\n \n # Property information\n street_number=\"456\",\n street_name=\"Oak Avenue\",\n city=\"Los Angeles\",\n state=\"CA\",\n zip_code=\"90210\",\n \n # Participants\n participants=[\n {\n \"fullName\": \"John Doe\",\n \"email\": \"john.doe@example.com\",\n \"role\": ParticipantRole.BUYER.value\n },\n {\n \"fullName\": \"Jane Smith\",\n \"email\": \"jane.smith@realty.com\",\n \"role\": ParticipantRole.BUYING_AGENT.value\n },\n {\n \"fullName\": \"Bob Johnson\",\n \"email\": \"bob.johnson@realty.com\",\n \"role\": ParticipantRole.LISTING_AGENT.value\n }\n ],\n \n # Optional MLS information\n mls_property_id=\"ML123456\",\n template_id=1001\n)\n\nprint(f\"Created loop: {loop['data']['loopUrl']}\")\n```\n\n### Managing Contacts\n\n```python\n# Create a new contact\ncontact = client.contact.create_contact(\n first_name=\"Alice\",\n last_name=\"Johnson\",\n email=\"alice.johnson@example.com\",\n phone=\"+1 (555) 123-4567\",\n company=\"Johnson Realty\"\n)\n\n# Update the contact\nupdated_contact = client.contact.update_contact(\n contact_id=contact['data']['id'],\n phone=\"+1 (555) 987-6543\",\n company=\"Johnson Premium Realty\"\n)\n\n# List all contacts\ncontacts = client.contact.list_contacts()\nfor contact in contacts['data']:\n print(f\"{contact['firstName']} {contact['lastName']} - {contact.get('email', 'No email')}\")\n```\n\n### Working with Profiles\n\n```python\n# Create a new profile\nprofile = client.profile.create_profile(\n name=\"My Real Estate Business\",\n company=\"ABC Realty\",\n phone=\"+1 (555) 123-4567\",\n address=\"123 Business Ave\",\n city=\"New York\",\n state=\"NY\",\n zip_code=\"10001\"\n)\n\n# List loops for this profile\nloops = client.loop.list_loops(\n profile_id=profile['data']['id'],\n batch_size=50,\n sort=\"updated:desc\",\n include_details=True\n)\n```\n\n## Error Handling\n\nThe wrapper provides comprehensive error handling with specific exception types:\n\n```python\nfrom dotloop import DotloopClient\nfrom dotloop.exceptions import (\n AuthenticationError,\n NotFoundError,\n ValidationError,\n RateLimitError\n)\n\nclient = DotloopClient()\n\ntry:\n account = client.account.get_account()\nexcept AuthenticationError:\n print(\"Invalid or expired API token\")\nexcept NotFoundError:\n print(\"Resource not found\")\nexcept ValidationError as e:\n print(f\"Invalid parameters: {e}\")\nexcept RateLimitError:\n print(\"Rate limit exceeded, please wait\")\n```\n\n## Enums and Constants\n\nThe wrapper provides enums for common values:\n\n```python\nfrom dotloop import (\n TransactionType,\n LoopStatus,\n ParticipantRole,\n SortDirection\n)\n\n# Transaction types\nTransactionType.PURCHASE_OFFER\nTransactionType.LISTING_FOR_SALE\nTransactionType.PURCHASED\nTransactionType.SOLD\n\n# Loop statuses\nLoopStatus.PRE_OFFER\nLoopStatus.UNDER_CONTRACT\nLoopStatus.SOLD\nLoopStatus.ARCHIVED\n\n# Participant roles\nParticipantRole.BUYER\nParticipantRole.SELLER\nParticipantRole.LISTING_AGENT\nParticipantRole.BUYING_AGENT\n```\n\n## Development\n\n### Setting up Development Environment\n\n```bash\n# Clone the repository\ngit clone https://github.com/your-org/dotloop-python.git\ncd dotloop-python\n\n# Create virtual environment\npython -m venv venv\nsource venv/bin/activate # On Windows: venv\\Scripts\\activate\n\n# Install dependencies\npip install -r requirements-dev.txt\n\n# Run tests\npytest\n\n# Run tests with coverage\npytest --cov=dotloop --cov-report=html\n```\n\n### Running Tests\n\n```bash\n# Run all tests\npytest\n\n# Run with coverage\npytest --cov=dotloop\n\n# Run specific test file\npytest tests/test_account.py\n\n# Run with verbose output\npytest -v\n```\n\n## API Reference\n\nFor detailed API documentation, see the [official Dotloop API documentation](https://dotloop.github.io/public-api/).\n\n## Contributing\n\n1. Fork the repository\n2. Create a feature branch (`git checkout -b feature/amazing-feature`)\n3. Commit your changes (`git commit -m 'Add amazing feature'`)\n4. Push to the branch (`git push origin feature/amazing-feature`)\n5. Open a Pull Request\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n\n## Support\n\n- **Documentation**: [API Documentation](https://dotloop.github.io/public-api/)\n- **Issues**: [GitHub Issues](https://github.com/your-org/dotloop-python/issues)\n- **Email**: dev@theperry.group\n\n## Changelog\n\n### v1.0.0\n- Initial release\n- Complete implementation of core Dotloop API endpoints\n- Account, Profile, Loop, Loop-It, and Contact management\n- Comprehensive error handling and type safety\n- 100% test coverage \n",
"bugtrack_url": null,
"license": null,
"summary": "Python wrapper for Dotloop API - Real estate transaction management and document handling",
"version": "1.1.2",
"project_urls": {
"Bug Tracker": "https://github.com/theperrygroup/dotloop/issues",
"Changelog": "https://github.com/theperrygroup/dotloop/blob/main/CHANGELOG.md",
"Documentation": "https://dotloop.readthedocs.io",
"Homepage": "https://github.com/theperrygroup/dotloop",
"Repository": "https://github.com/theperrygroup/dotloop.git"
},
"split_keywords": [
"real-estate",
" api",
" dotloop",
" transactions",
" documents",
" real-estate-api",
" property-management",
" real-estate-transactions"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "d7436bd5dfe3f7b86cb351167c4137406f919cc494e498d0e61b474a1d6eacec",
"md5": "fab36609d8853b46e54f2783701908a3",
"sha256": "8e3886ca37a6aa923384c143d57c25ccb701758275f543a23ecc9be7047ce442"
},
"downloads": -1,
"filename": "dotloop-1.1.2-py3-none-any.whl",
"has_sig": false,
"md5_digest": "fab36609d8853b46e54f2783701908a3",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 36840,
"upload_time": "2025-08-11T00:43:03",
"upload_time_iso_8601": "2025-08-11T00:43:03.827570Z",
"url": "https://files.pythonhosted.org/packages/d7/43/6bd5dfe3f7b86cb351167c4137406f919cc494e498d0e61b474a1d6eacec/dotloop-1.1.2-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "39367474b3918b55fbee06b7a215b8689f8bfb43d0add569d753fab8718c5bbc",
"md5": "7ca5c55a0e67e2201f55dde10b463c7c",
"sha256": "dd99fc904ca96f7404aaab244e50647b526e448d641073a0921f7aa5089ed8fb"
},
"downloads": -1,
"filename": "dotloop-1.1.2.tar.gz",
"has_sig": false,
"md5_digest": "7ca5c55a0e67e2201f55dde10b463c7c",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8",
"size": 37767,
"upload_time": "2025-08-11T00:43:05",
"upload_time_iso_8601": "2025-08-11T00:43:05.817007Z",
"url": "https://files.pythonhosted.org/packages/39/36/7474b3918b55fbee06b7a215b8689f8bfb43d0add569d753fab8718c5bbc/dotloop-1.1.2.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-08-11 00:43:05",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "theperrygroup",
"github_project": "dotloop",
"github_not_found": true,
"lcname": "dotloop"
}