1. Overview
1.1. What’s New
- Webhooks
-
Dotloop Webhooks is currently in the Initial Release phase. Webhooks allow client users to subscribe to specific events that occur in dotloop. When an event occurs, dotloop will send an HTTPS POST payload to the webhook’s configured URL. Detailed information can be found in the Webhooks (Initial Release) section. API Documentation can be found in Webhook Subscriptions and Webhook Events sections.
- Loop-It Facade API
-
We now provide a simple 'Facade' API which allows client applications to create a loop and populate data into it via a single request, which includes inserting property information, adding loop participants into the contacts directory, and creating a loop from loop templates etc.
- New Authentication Scheme
-
We introduce a new authentication scheme (OAuth2) which gets client applications access to user accounts upon user’s consent.
- New Request / Response Schemas
-
All APIs and their corresponding request/response schemas have been refreshed/updated - we continue to support existing integrations using dotloop’s external API.
- Read/Write Access
-
We introduce a new set of APIs in addition to
GET
APIs already available in external API, so clients can now for the first time create and update new resources (e.g. create/update loops or profiles viaPOST
andPATCH
).
1.2. Authentication
Dotloop’s Public API Version 2 makes use of the OAuth 2.0 protocol for authentication and authorization and initially support scenarios for web server applications only (3-legged-OAuth).
By using this protocol, we allow dotloop users to control data access by third-party applications and provide users the ability to revoke previously granted access at a later time.

1.2.1. Client Registration
To register your application to integrate with this API, please request access at http://info.dotloop.com/developers. Upon registration, we will issue you a client id and client secret which are prerequisites in order to use the API.
1.2.2. Obtaining Access Token
In order to obtain an access token to access any resources on behalf of a dotloop user, client applications need to obtain an access token for each user provided the user gives his consent. Access tokens are short-living and expire usually after 12 hours, hence need to be refreshed once expired. For more information on the Oauth2 protocol, e.g. error codes please see RFC6749.
The first step towards acquisition of access and refresh tokens is to obtain an Authorization Code. The code will be issued after the user approved access to the client application to his account. To prompt the user to give his approval, redirect the user to (usually done in a popup window):
https://auth.dotloop.com/oauth/authorize?response_type=code&client_id=<client_id>&redirect_uri=<redirect_url>[&state=<state>&redirect_on_deny=(true|false)]
Parameters
Name | Type | Description |
---|---|---|
response_type |
string |
[required] only |
client_id |
string |
[required] client id (UUID issued when registring client application) |
redirect_uri |
string |
[required] URL the user agent gets redirected to with authorization code after user consent |
state |
string |
[optional] random string to protect against CSRF |
redirect_on_deny |
boolean |
[optional] defines whether the action behind deny button redirects to
|
Once the user approved the request of your client to access his account, we’ll issue a user agent redirect (302
with
Location
header) back to the URL provided (redirect_uri
param) with the authorization code added in the query, ie.
<redirect_url>?code=<code>
. Please not that the state
param is recommended and should be used to protect your site
against CSRF.
With the code
received in step 1, the client application can obtain an access token by making a request against the
/token
endpoint.
The /token endpoint requires an authentication header (HTTP Basic Authentication) sent by the client application.
|
Request
POST https://auth.dotloop.com/oauth/token?grant_type=authorization_code&code=<code>&redirect_uri=<redirect_url>&state=<state>
Header
Authorization: Basic <encode_base64(ClientID:ClientSecret)>
-
Example
-
ClientId:
69bcf590-71b7-41a4-a039-a1d290edca11
-
ClientSecret:
3415e381-bdc4-49b7-bde2-69b3c5cd6447
-
Resulting Header:
Authorization: Basic NjliY2Y1OTAtNzFiNy00MWE0LWEwMzktYTFkMjkwZWRjYTExOjM0MTVlMzgxLWJkYzQtNDliNy1iZGUyLTY5YjNjNWNkNjQ0Nw==
-
1.2.3. Refreshing Access Token after expiration
Access tokens have a short lifetime and need to be refreshed every 12 hours. The client application can either
pro-actively refresh tokens before they expire and lazily refresh them upon receiving an authentication error
(401 Unauthenticated
) when accessing the API.
POST https://auth.dotloop.com/oauth/token?grant_type=refresh_token&refresh_token=<refresh_token>
Response
Status: 200 OK
{
"access_token": "86609772-aa95-4071-ad7f-25ad2d0be295",
"token_type": "Bearer",
"refresh_token": "19bfda68-ca62-480c-9c62-2ba408458fc7",
"expires_in": 43199,
"scope": "account:read, profile:*, loop:*, contact:*, template:read"
}
When refreshing access tokens, any previously issued access token becomes invalid. If you manage tokens in a clustered environment, make sure to share/use and refresh the token once across your cluster instances to avoid race conditions during the token update when triggered from multiple instances concurrently. |
1.2.4. Access Revocation
A client or the actual user of the client may decide to disconnect from dotloop and revoke previously given permission to his dotloop account. To revoke access, the client application should call the following endpoint which will invalidate access and refresh token for future use.
POST https://auth.dotloop.com/oauth/token/revoke?token=<access_token>
1.3. Endpoint
All APIs listed below are available under a common base path:
All paths below are relative to this url, e.g. https://api-gateway.dotloop.com/public/v2/loop-it
2. Loop-It™
The Loop-It™ API makes it easy to create a new Loop and and populate various details into the loop, e.g. setup loop participant’s contact data into the contacts directory, pulls listing property data, authenticates the caller if an NRDS Id or MLS Agent Id is available to get access to form templates, etc.
Required scope: |
POST /loop-it?profile_id=<profile_id>
2.1. Parameters
Name | Type | Description |
---|---|---|
profile_id |
integer |
[optional] Id of the individual profile the loop will be created in; required in case the account has more than one profile |
transactionType |
string |
[required] Type of transaction (see addendum) |
status |
string |
[required] Status of the loop (see addendum) |
name |
string |
[required] Name of the loop, usually either property address line or lead name (max 200 chars) |
streetName |
string |
[optional] Street name |
streetNumber |
string |
[optional] Street number |
unit |
string |
[optional] Unit number |
city |
string |
[optional] City |
state |
string |
[optional] State |
zipCode |
string |
[optional] Zip code |
county |
string |
[optional] County |
country |
string |
[optional] Country |
participants.fullName |
string |
[optional] Participant’s full name |
participants.email |
string |
[optional] Participant’s email address |
participants.role |
string |
[optional] Participant’s role |
templateId |
integer |
[optional or required] Loop Template Id: (note: my be required by the user’s organization (parent profile) |
mlsPropertyId |
string |
[optional] MLS Property Id |
mlsId |
string |
[optional] MLS Id required to search listing |
mlsAgentId |
string |
[optional] MLS Agent Id |
nrdsId |
string |
[optional] NRDS Id |
Please be aware of that access to loops is currently restricted to INDIVIDUAL profiles only
|
2.2. Example
POST /loop-it?profile_id=4711
{
"name": "Brian Erwin",
"transactionType": "PURCHASE_OFFER",
"status": "PRE_OFFER",
"streetName": "Waterview Dr",
"streetNumber": "2100",
"unit": "12",
"city": "San Francisco",
"zipCode": "94114",
"state": "CA",
"country": "US",
"participants": [
{
"fullName": "Brian Erwin",
"email": "brianerwin@newkyhome.com",
"role": "BUYER"
},
{
"fullName": "Allen Agent",
"email": "allen.agent@gmail.com",
"role": "LISTING_AGENT"
},
{
"fullName": "Sean Seller",
"email": "sean.seller@yahoo.com",
"role": "SELLER"
}
],
"templateId": 1424,
"mlsPropertyId": "43FSB8",
"mlsId": "789",
"mlsAgentId": "123456789"
}
2.3. Response
The response contains a property loopUrl
, which can be used to redirect the user to the loop on dotloop.com.
Status: 201 Created
{
"data": {
"id": 34308,
"profileId": 4711,
"name": "Brian Erwin",
"transactionType": "PURCHASE_OFFER",
"status": "PRE_OFFER",
"created": "2017-05-30T21:42:17Z",
"updated": "2017-05-31T23:27:11Z",
"loopUrl": "https://www.dotloop.com/m/loop?viewId=34308"
}
}
2.4. Design Guidelines
In order to allow users to easily spot and interact with Dotloop’s Loop-It™ functionality in 3rd-party products, we advise to implement and visualize the feature as a Loop-It™ button.
As an example, the Loop-It™ Button could be rendered next to a property listing, which allows an agent to easily create a loop associated with the listed property. Upon response from the Loop-It™ API, which contains a perma-link to the created loop, the agent gets prompted by the 3rd party application, whether he wants to transition into dotloop to continue manage loop details or move the transaction forward.

4. Profiles
4.1. List all Profiles
List all profiles associated with the user.
GET /profile
Required scope: profile:read
|
4.1.2. Response
Status: 200 OK
{
"meta": {
"total": 3
},
"data": [
{
"id": 3,
"name": "My Profile",
"type": "INDIVIDUAL",
"company": "MyCompany",
"phone": "+0 (123) 456 7890",
"fax": "+0 (123) 456 7890",
"address": "1234 Wall St",
"city": "New York",
"state": "NY",
"zipCode": "10005",
"default": true,
"requiresTemplate": true
},
...
]
}
4.2. Get a Profile
Retrieve an individual profile by id.
Required scope: |
GET /profile/:profile_id
4.3. Create a Profile
Create a new profile.
Required scope: |
POST /profile
4.3.1. Parameters
Name | Type | Description |
---|---|---|
name |
string |
profile name |
company |
string |
company name |
phone |
string |
phone number |
address |
string |
address line |
city |
string |
city |
zipCode |
string |
zip code |
state |
string |
state |
country |
string |
country |
4.4. Update a Profile
Update an existing profile by id.
|
PATCH /profile/:profile_id
5. Loop Summaries
5.1. List all Loops
List all loops associated with a profile.
Required scope: |
GET /profile/:profile_id/loop[?batch_size=<batch_size>&batch_number=<batch_number>&sort=<sort>&filter=<filter>&include_details=true]
5.1.1. Parameters
Name | Type | Description |
---|---|---|
batch_size |
integer |
[optional] size of batch returned (default=20, max=100) |
batch_number |
integer |
[optional] batch/page number (default=1) |
sort |
string |
[optional] string which contains the sort category and optionally the sort direction (default ascending);
format: |
filter |
String |
[optional] format: |
include_details |
boolean |
[optional] flag to include loop details with each record returned; [true|false] (default: false) |
5.1.2. Response
Status: 200 OK
{
"meta": {
"total": 10
},
"data": [
{
"id": 34308,
"name": "Atturo Garay, 3059 Main, Chicago, IL 60614",
"status": "ARCHIVED",
"transactionType": "PURCHASE_OFFER",
"totalTaskCount": 5,
"completedTaskCount": 3,
"updated": "2017-05-30T21:42:17Z",
"created": "2017-05-17T01:18:37Z",
"loopUrl": "https://www.dootloop.com/m/loop?viewId=34308"
},
...
]
}
5.2. Get a Loop
Retrieve an individual loop by id.
Required scope: |
GET /profile/:profile_id/loop/:loop_id
5.2.2. Response
Status: 200 OK
{
"data": {
"id": 34308,
"name": "Atturo Garay, 3059 Main, Chicago, IL 60614",
"status": "ARCHIVED",
"transactionType": "PURCHASED",
"totalTaskCount": 5,
"completedTaskCount": 3,
"updated": "2017-05-30T21:42:17Z",
"created": "2017-05-17T01:18:37Z",
"loopUrl": "https://www.dootloop.com/m/loop?viewId=34308"
}
}
Status: 301 Moved Permanently
In some scenarios, two separate loops can be merged together, which can change the original loop ID. In those cases, attempting to access the original loop ID will produce a 301 response that points to the new loop. Any clients that persist loop IDs should account for this scenario and be able to update any references when a 301 is encountered.
See the support article for more information on the loop merging process: [Merge Loops](https://support.dotloop.com/s/article/Merge-Loops).
The 301 response will have a Location
header present with a path to redirect to the new Loop View.
Headers
Location: /public/v2/profile/3/loop/30004
To get the Loop use the value from the Location
header to do the next call.
When Autoredirect is enabled the second call will be done automatically and will get you a response as shown in 200 Response
.
5.3. Create a Loop
Create a new loop.
Required scope: |
POST /profile/:profile_id/loop
5.3.1. Parameters
Name | Type | Description |
---|---|---|
name |
string |
the name of the loop (max 200 chars) |
status |
string |
status of the loop |
transactionType |
string |
type of transaction |
5.3.2. Example
{
"name": "Atturo Garay, 3059 Main, Chicago, IL 60614",
"status": "PRE_LISTING",
"transactionType": "LISTING_FOR_SALE"
}
5.3.3. Response
Status: 201 Created
{
"data": {
"id": 34308,
"profileId": 23483,
"name": "Atturo Garay, 3059 Main, Chicago, IL 60614",
"transactionType": "LISTING_FOR_SALE",
"status": "PRE_LISTING",
"totalTaskCount": 5,
"completedTaskCount": 3,
"created": "2017-05-17T01:18:37Z",
"updated": "2017-05-17T01:18:37Z",
"loopUrl": "https://www.dootloop.com/m/loop?viewId=34308"
}
}
5.4. Update a Loop
Update an existing loop by id.
|
PATCH /profile/:profile_id/loop/:loop_id
5.4.1. Parameters
Name | Type | Description |
---|---|---|
name |
string |
the name of the loop (max 200 chars) |
status |
string |
status of the loop |
transactionType |
string |
type of transaction |
5.4.3. Response
Status: 200 OK
{
"data": {
"id": 34308,
"name": "Atturo Garay, 3059 Main, Chicago, IL 60614",
"transactionType": "LISTING_FOR_SALE",
"status": "SOLD",
"totalTaskCount": 5,
"completedTaskCount": 3,
"updated": "2017-05-30T21:42:17Z",
"created": "2017-05-17T01:18:37Z",
"loopUrl": "https://www.dootloop.com/m/loop?viewId=34308"
}
}
6. Loop Details
6.1. Get Loop Details
Retrieve loop details by id.
Required scope: |
GET /profile/:profile_id/loop/:loop_id/detail
6.1.1. Parameters
Details Section | Field | Type | Description |
---|---|---|---|
'Property Address' |
'Country' |
string |
|
'Property Address' |
'Street Number' |
string |
|
'Property Address' |
'Street Name' |
string |
|
'Property Address' |
'Unit Number' |
string |
|
'Property Address' |
'City' |
string |
|
'Property Address' |
'State/Prov' |
string |
|
'Property Address' |
'Zip/Postal Code' |
string |
|
'Property Address' |
'County' |
string |
|
'Property Address' |
'MLS Number' |
string |
|
'Property Address' |
'Parcel/Tax ID' |
string |
|
'Financials' |
'Purchase/Sale Price' |
string |
|
'Financials' |
'Sale Commission Rate' |
string |
|
'Financials' |
'Sale Commission Split % - Buy Side' |
string |
|
'Financials' |
'Sale Commission Split % - Sell Side' |
string |
|
'Financials' |
'Sale Commission Total' |
string |
|
'Financials' |
'Earnest Money Amount' |
string |
|
'Financials' |
'Earnest Money Held By' |
string |
|
'Financials' |
'Sale Commission Split $ - Buy Side' |
string |
|
'Financials' |
'Sale Commission Split $ - Sell Side' |
string |
|
'Contract Dates' |
'Contract Agreement Date' |
string |
date string, e.g. 01/31/2017 |
'Contract Dates' |
'Closing Date' |
string |
date string, e.g. 01/31/2017 |
'Offer Dates' |
'Inspection Date' |
string |
date string, e.g. 01/31/2017 |
'Offer Dates' |
'Offer Date' |
string |
date string, e.g. 01/31/2017 |
'Offer Dates' |
'Offer Expiration Date' |
string |
date string, e.g. 01/31/2017 |
'Offer Dates' |
'Occupancy Date' |
string |
date string, e.g. 01/31/2017 |
'Offer Dates' |
'Offer Date' |
string |
date string, e.g. 01/31/2017 |
'Contract Info' |
'Transaction Number' |
string |
|
'Contract Info' |
'Class' |
string |
|
'Contract Info' |
'Type' |
string |
|
'Referral' |
'Referral %' |
string |
|
'Referral' |
'Referral Source' |
string |
|
'Listing Information' |
'Expiration Date' |
string |
date string, e.g. 01/31/2017 |
'Listing Information' |
'Listing Date' |
string |
date string, e.g. 01/31/2017 |
'Listing Information' |
'Original Price' |
string |
|
'Listing Information' |
'Current Price' |
string |
|
'Listing Information' |
'1st Mortgage Balance' |
string |
|
'Listing Information' |
'2nd Mortgage Balance' |
string |
|
'Listing Information' |
'Other Liens' |
string |
|
'Listing Information' |
'Description of Other Liens' |
string |
|
'Listing Information' |
'Homeowner's Association' |
string |
|
'Listing Information' |
'Homeowner's Association Dues' |
string |
|
'Listing Information' |
'Total Encumbrances' |
string |
|
'Listing Information' |
'Property Includes' |
string |
|
'Listing Information' |
'Property Excludes' |
string |
|
'Listing Information' |
'Remarks' |
string |
|
'Geographic Description' |
'MLS Area' |
string |
|
'Geographic Description' |
'Legal Description' |
string |
|
'Geographic Description' |
'Map Grid' |
string |
|
'Geographic Description' |
'Subdivision' |
string |
|
'Geographic Description' |
'Lot' |
string |
|
'Geographic Description' |
'Deed Page' |
string |
|
'Geographic Description' |
'Deed Book' |
string |
|
'Geographic Description' |
'Section' |
string |
|
'Geographic Description' |
'Addition' |
string |
|
'Geographic Description' |
'Block' |
string |
|
'Property' |
'Year Built' |
string |
|
'Property' |
'Bedrooms' |
string |
|
'Property' |
'Square Footage' |
string |
|
'Property' |
'School District' |
string |
|
'Property' |
'Type' |
string |
|
'Property' |
'Bathrooms' |
string |
|
'Property' |
'Lot Size' |
string |
6.1.2. Response
Status: 200 OK
{
"data": {
"Property Address": {
"Country": "USA",
"Street Number": "333",
"Street Name": "Main St",
"Unit Number": "123",
"City": "San Francisco",
"State/Prov": "CA",
"Zip/Postal Code": "94105",
"County": "USA",
...
},
"Financials": {
"Sale Commission Rate": "3",
"Sale Commission Split % - Buy Side": "50",
"Sale Commission Split % - Sell Side": "50",
"Sale Commission Total": "10000",
"Sale Commission Split $ - Buy Side": "50",
"Sale Commission Split $ - Sell Side": "20000",
...
},
...
}
}
6.2. Update Loop Details
Update loop details by id.
|
PATCH /profile/:profile_id/loop/:loop_id/detail
6.2.1. Parameters
See Get Loop Details above.
6.2.3. Response
Status: 200 OK
{
"data": {
"Property Address": {
"Country": "USA",
"Street Number": "333",
"Street Name": "Main St",
"Unit Number": "123",
"City": "San Francisco",
"State/Prov": "CA",
"Zip/Postal Code": "94105",
"County": "USA",
...
},
"Financials": {
"Purchase/Sale Price": "342342",
"Sale Commission Rate": "3",
"Sale Commission Split % - Buy Side": "50",
"Sale Commission Split % - Sell Side": "50",
"Sale Commission Total": "10000",
"Sale Commission Split $ - Buy Side": "50",
"Sale Commission Split $ - Sell Side": "20000",
...
},
...
}
}
7. Loop Folders
7.1. List all Folders
List all folders in a loop
Required scope: |
GET /profile/:profile_id/loop/:loop_id/folder[?include_documents=<include_documents>]
7.2. Get a Folder
Retrieve an individual folder by id.
Required scope: |
GET /profile/:profile_id/loop/:loop_id/folder/:folder_id[?include_documents=<include_documents>]
7.3. Create a Folder
Create a new folder.
Required scope: |
POST /profile/:profile_id/loop/:loop_id/folder/
8. Loop Documents
8.1. List all Documents
List all documents in a loop
Required scope: |
GET /profile/:profile_id/loop/:loop_id/folder/:folder_id/document
8.2. Get a Document
Retrieve an individual document by document_id
Required scope: |
GET /profile/:profile_id/loop/:loop_id/folder/:folder_id/document/:document_id Accept: application/json
8.3. Upload a Document
Upload a individual document (binary) via multipart form post
Required scope: |
POST /profile/:profile_id/loop/:loop_id/folder/:folder_id/document/ content-type: multipart/form-data; boundary=<BOUNDARY> content-length: XXX --<BOUNDARY> Content-Disposition: form-data; name="file"; fileName="disclosures.pdf" Content-Type: application/pdf <binary data> --<BOUNDARY>--
9. Loop Participants
9.1. List all Loop Participants
List all loop participants in a loop
Required scope: |
GET /profile/:profile_id/loop/:loop_id/participant
9.1.2. Response
Status: 200 OK
{
"meta": {
"total": 3
},
"data": [
{
"id": 2355,
"fullName": "Brian Erwin",
"email": "brianerwin@newkyhome.com",
"role": "BUYER",
"Phone": "(555) 555-5555"
},
{
"id": 57567,
"fullName": "Allen Agent",
"email": "allen.agent@gmail.com",
"role": "LISTING_AGENT",
"Phone": "(555) 555-1234",
"Company Name": "Allen Realty"
},
{
"id": 24743,
"fullName": "Sean Seller",
"email": "sean.seller@yahoo.com",
"role": "SELLER",
"Street Name": "123",
"Street Number": "Main St.",
"City": "Cincinnati",
"Zip/Postal Code": "45123",
"Country": "USA",
"Cell Phone": "(555) 555-4444"
}
]
}
9.2. Get a Loop Participant
Retrieve loop participants details of an individual loop participant.
Required scope: |
GET /profile/:profile_id/loop/:loop_id/participant/:participant_id
9.3. Add a Loop Participant
Add a new loop participant
|
POST /profile/:profile_id/loop/:loop_id/participant
9.3.1. Parameters
Name | Type | Description |
---|---|---|
fullName |
string |
First and last name of the participant |
string |
participant email |
|
role |
string |
participant role |
'Street Name' |
string |
[optional] street number of participant’s address |
'Street Number' |
string |
[optional] street name of participant’s address |
'City' |
string |
[optional] city of participant’s address |
'State/Prov' |
string |
[optional] state/providence of participant’s address |
'Zip/Postal Code' |
string |
[optional] postal code of participant’s address |
'Unit Number' |
string |
[optional] unit # of participant’s address |
'Country' |
string |
[optional] country of participant’s address |
'Phone' |
string |
[optional] participant phone number |
'Cell Phone' |
string |
[optional] participant mobile number |
'Company Name' |
string |
[optional] participant company |
Additional role-specific fields can also be provided. See Built-in Contact/Loop Participant Roles for details.
9.3.2. Example
{
"fullName": "Brian Erwin",
"email": "brian@gmail.com",
"role": "BUYER",
"Street Name": "123",
"Street Number": "Main St.",
"City": "Cincinnati",
"Zip/Postal Code": "45123",
"Country": "USA",
"Phone": "(555) 555-5555",
"Cell Phone": "(555) 555-4444",
"Company Name": "Buyer's Company"
}
9.3.3. Response
Status: 201 Created
{
"data": {
"id": 2355,
"fullName": "Brian Erwin",
"email": "brianerwin@newkyhome.com",
"role": "BUYER",
"Street Name": "123",
"Street Number": "Main St.",
"City": "Cincinnati",
"Zip/Postal Code": "45123",
"Country": "USA",
"Phone": "(555) 555-5555",
"Cell Phone": "(555) 555-4444",
"Company Name": "Buyer's Company"
}
}
9.4. Update a Loop Participant
Update an existing participant
|
PATCH /profile/:profile_id/loop/:loop_id/participant/:participant_id
9.4.1. Parameters
Name | Type | Description |
---|---|---|
fullName |
string |
First and last name of the participant |
string |
participant email |
|
role |
string |
participant role |
'Street Name' |
string |
street number of participant’s address |
'Street Number' |
string |
street name of participant’s address |
'City' |
string |
city of participant’s address |
'State/Prov' |
string |
state/providence of participant’s address |
'Zip/Postal Code' |
string |
postal code of participant’s address |
'Unit Number' |
string |
unit # of participant’s address |
'Country' |
string |
country of participant’s address |
'Phone' |
string |
participant phone number |
'Cell Phone' |
string |
participant mobile number |
'Company Name' |
string |
participant company |
10. Loop Tasks
10.1. List all Loop Task Lists
List all task lists in a loop
Required scope: |
GET /profile/:profile_id/loop/:loop_id/tasklist/
10.2. Get a Loop Task List
Retrieve an individual task list.
Required scope: |
GET /profile/:profile_id/loop/:loop_id/tasklist/:task_list_id
10.3. List all Loop Task List Items
List all task items in a task list
Required scope: |
GET /profile/:profile_id/loop/:loop_id/tasklist/:task_list_id/task
11. Loop Activities
11.1. List all Loop Activities
List all activities for a loop
Required scope: |
GET /profile/:profile_id/loop/:loop_id/activity[?batch_size=<batch_size>&batch_number=<batch_number>]
11.1.1. Parameters
Name | Type | Description |
---|---|---|
batch_size |
integer |
size of batch returned (default=20, max=100) |
batch_number |
integer |
batch/page number (default=1) |
11.1.2. Response
Status: 200 OK
{
"meta": {
"total": -1
},
"data": [
{
"message": "User One viewed document Agency Disclosure Statement - Seller",
"date": "2017-01-09T13:10:14Z"
},
...
]
}
The meta/total count is currently returned as -1 , ie in order to know how many activities are present the caller
needs to paginate the the entire result.
|
12. Contacts
12.1. List all Contacts
List all contacts in the user account.
Required scope: |
GET /contact[?batch_size=<batch_size>&batch_number=<batch_number>&filter=<filter>]
12.1.1. Parameters
Name | Type | Description |
---|---|---|
batch_size |
integer |
size of batch returned (default=20, max=100) |
batch_number |
integer |
batch/page number (default=1) |
filter |
String |
format: |
12.1.2. Response
Status: 200 OK
{
"meta": {
"total": 10
},
"data": [
{
"id": 3603862,
"firstName": "Brian",
"lastName": "Erwin",
"email": "brianerwin@newkyhome.com",
"home": "(415) 8936 332",
"office": "(415) 1213 656",
"fax": "(415) 8655 686",
"address": "2100 Waterview Dr",
"city": "San Francisco",
"zipCode": "94114",
"state": "CA",
"country": "US",
"updated": "2017-04-20T03:48:30Z"
},
...
]
}
12.2. Get a Contact
Retrieve an individual contact by id.
Required scope: |
GET /contact/:contact_id
12.2.2. Response
Status: 200 OK
{
"data": {
"id": 3603862,
"firstName": "Brian",
"lastName": "Erwin",
"email": "brianerwin@newkyhome.com",
"home": "(415) 8936 332",
"office": "(415) 1213 656",
"fax": "(415) 8655 686",
"address": "2100 Waterview Dr",
"city": "San Francisco",
"zipCode": "94114",
"state": "CA",
"country": "US",
"updated": "2017-04-20T03:48:30Z"
}
}
12.3. Create a Contact
Create a new contact.
Required scope: |
POST /contact
12.3.1. Parameters
Name | Type | Description |
---|---|---|
firstName |
string |
first name |
lastName |
string |
last name |
string |
email address |
|
home |
string |
home phone number |
office |
string |
office phone number |
fax |
string |
fax number |
address |
string |
address line |
city |
string |
city |
zipCode |
string |
zip code |
state |
string |
state |
country |
string |
country |
12.3.2. Example
{
"firstName": "Brian",
"lastName": "Erwin",
"email": "brianerwin@newkyhome.com",
"home": "(415) 8936 332",
"office": "(415) 1213 656",
"fax": "(415) 8655 686",
"address": "2100 Waterview Dr",
"city": "San Francisco",
"zipCode": "94114",
"state": "CA",
"country": "US"
}
12.3.3. Response
Status: 201 Created
{
"data": {
"id": 3603862,
"firstName": "Brian",
"lastName": "Erwin",
"email": "brianerwin@newkyhome.com",
"home": "(415) 8936 332",
"office": "(415) 1213 656",
"fax": "(415) 8655 686",
"address": "2100 Waterview Dr",
"city": "San Francisco",
"zipCode": "94114",
"state": "CA",
"country": "US",
"updated": "2017-04-20T03:48:30Z"
}
}
12.4. Update a Contact
Update an existing contact by id.
|
PATCH /contact/:contact_id
12.4.1. Parameters
Name | Type | Description |
---|---|---|
firstName |
string |
first name |
lastName |
string |
last name |
string |
email address |
|
home |
string |
home phone number |
office |
string |
office phone number |
fax |
string |
fax number |
address |
string |
address |
city |
string |
city |
zipCode |
string |
zip code |
state |
string |
state |
country |
string |
country |
12.4.3. Response
Status: 200 OK
{
"data": {
"id": 3603862,
"firstName": "Brian",
"lastName": "Erwin",
"email": "brianerwin@newkyhome.com",
"home": "(415) 888 8888",
"office": "(415) 1213 656",
"fax": "(415) 8655 686",
"address": "2100 Waterview Dr",
"city": "San Francisco",
"zipCode": "94114",
"state": "CA",
"country": "US",
"updated": "2017-04-20T03:48:30Z"
}
}
13. Loop Templates
13.1. List all Loop Templates
List all loop templates in the profile
Required scope: |
GET /profile/:profile_id/loop-template
14. Webhooks (Initial Release)
14.1. Webhooks Overview
Webhooks is currently in the initial release phase and available to API clients by request. Please contact support to request access.
Webhooks is a Dotloop Public API feature. The Webhooks feature is managed by API client applications registered with the Dotloop Public API via /subscription
and /subscription/:subscription_id/event
endpoints. Integrating applications are able to configure webhooks individually per authorized dotloop user and their profiles, using the appropriate access tokens.
A dotloop user may have webhooks enabled for as many integrating applications as they have connected to their account. These webhook configurations have no interference or interaction with webhook configurations created by other integrating applications. |
Below are diagrams outlining an example using a USER CONTACT_CREATED subscription and event:
-
Creating a subscription
-
Receiving a webhook event because of the subscription
-
How client applications may want to fetch Public API data based on the event.
14.2. Subscriptions Overview
The following table defines the event types that can be subscribed to.
(See also Webhooks Event Targets and Corresponding Event Types under Types/Constants)
14.2.1. Event Types By Target Type Table
Target Type | Event Types |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Create Subscription Request
POST /public/v2/subscription HTTP/1.1 Content-Type: application/json Authorization: {{BEARER_TOKEN}} ... { "targetType": "PROFILE", "targetId": 789, "eventTypes": ["LOOP_CREATED", "LOOP_UPDATED"], "url": "https://foobar.com/callbacks/dotloopwebhook", "externalId": "your_external_id" "signingKey": "super_secret_key" }
ExternalId
The externalId
property is a configuration on the Subscription resource. This is intended to be a foreign key in the integrating system, and is included in the body of each webhook event as subscriptionExternalId
. See Receiving Webhook Events below.
Subscription Authorization
To receive profile events, the user-owner of the subscription must have access to the profile. If access to a profile is lost, the subscription will be disabled. This subscription will not be automatically re-enabled by dotloop if access is re-granted to the profile - client applications will need to re-enable via the Public API. Consider the following example:
Subscription Events
In the event a subscription is disabled or deleted, a "good-bye" event will be delivered to the webhook url configured for the subscription. Subscription events follow the same format as application events, but will contain the subscription configuration in the "event" field (see Event Request for details).
Subscription Event Type | Description |
---|---|
|
Event generated when a subscription is permanently deleted, typically by user removing api access ( see Delete a Subscription) |
|
Event generated when a subscription is suspended via |
14.3. Receiving Webhook Events
14.3.1. Event Request
POST https://fooBar.com/callbacks/dotloopWebhook Content-Type: application/json X-DOTLOOP-SIGNATURE: {{COMPUTED_SIGNATURE_HASH}} X-DOTLOOP-TIMESTAMP: 1691763097001 ... { "eventId": "3bc982f6-7029-40ae-81aa-62f93a5ea1a8", "createdOn": "2022-11-01T00:00:00Z", "subscriptionId": "7dbf7306-6015-48aa-8e9b-205363514d32", "subscriptionExternalId": "FOO_BAR_DB_ID", "profileId": "5678", "eventType": "LOOP_CREATED", "event": { "id": "154684513548" } }
Please note that the event
field contains a polymorphic object, consisting of ids. There will always be an id
field, this is the id of the eventType
target.
Event Type | Event |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
14.3.2. At-Least-Once Delivery
Dotloop will attempt to deliver each webhook event at least once. There may be times where you receive an event twice or multiple times, your application should be able to handle this.
14.3.3. Multiple Event Types
In some cases, a single action can trigger multiple events. For example, adding a loop participant to a loop can trigger both LOOP_UPDATED
and LOOP_PARTICIPANT_CREATED
events. Clients can expect to receive both events if they are subscribed to both. This is not the only example of this, so it is good practice to fetch the proper API Resource for each event received, see Webhook Events & Related API Resources.
14.3.4. Failure Handling
Dotloop will attempt to POST events immediately to the url defined in a subscription. If the response from the client server is anything but a 2xx success code, we will schedule the event for another delivery. These retries follow a back-off policy of 30s, 1min, 15min, 30min, 1hr, 2hr, 4hr, and 8hr, after which the event will be marked as failed to deliver. Failed events can still be viewed via api endpoint but dotloop will no longer attempt to resend them. There is currently no mechanism to redrive events.
Please note that if a subscription results in too many failed events in succession we may disable the subscription.
14.3.5. Webhook Request Timeout
Dotloop will wait for a response from the client server for 5 seconds. If no response is received, the https connection will be terminated and the request will be marked as failed, the event will be scheduled for another delivery if applicable.
A sample event with a timeout failure:
{ data: { id: '194d74f5-6bdf-415e-b0dc-bfae1aa75ae', subscriptionId: 'bc08a81e-e42c-4dfc-bc23-a560234f3b8e', createdOn: '2023-10-30T17:41:55.975Z', eventType: 'LOOP_CREATED', eventData: { id: '70558693' }, deliveryStatus: 'SCHEDULED', deliveryAttempts: 1, responseData: [ { responseCode: 0, responseBody: 'Timeout duration of 5000ms has been reached.', url: 'https://foobar.com/callbacks/dotloopwebhook', requestSentAt: '2023-11-27T21:28:01.512Z', durationMs: 5260 } ] } }
14.3.6. Verifying Webhook Events
Since webhook endpoints are accepting post requests from the internet, we implement two ways to verify the authenticity and integrity of events posted to a client’s endpoint.
Subscription Signing Key
When a client creates a subscription, a signingKey
can be provided (see the above request body for creating a subscription). All events posted from this subscription will have a header X-DOTLOOP-SIGNATURE: {{COMPUTED_SIGNATURE_HASH}}
. Client’s can use this hash to verify the body and timestamp of the event has not been tampered with or impersonated. More on this below.
Subscriptions without a signingKey
will not have this header.
Timestamp
Events always have a timestamp header, X-DOTLOOP-TIMESTAMP: 1691763097001
.
This header’s value is a timestamp in seconds since epoch. This is to prevent replay attacks, where an event can be "replayed" by someone multiple times, even with a "valid" signature header. Clients should use this timestamp to verify an event falls within their acceptable time range.
Computing The Signature
To compute the signature, you will need to compose a string where the timestamp and raw json body are concatenated, separated by a dot (.). As an example:
const signed_content = `${X-DOTLOOP-TIMESTAMP}.${body}`
Dotloop uses an HMAC with SHA-1 to sign its webhooks. Below is an example implementation:
import crypto from 'crypto';
const signed_content = `${X-DOTLOOP-TIMESTAMP}.${body}`;
const secret_bytes = Buffer.from("super_secrety_key", "utf8");
const hmac = crypto.createHmac("sha1", secret_bytes);
hmac.update(signed_content);
const hash = hmac.digest("hex");
console.log(hash);
14.3.7. Querying For Events
All events can be queried via the Dotloop Public Api. Refer to the Webhook Events API documentation below. Notice that the shape for received events and queried events from the API are quite different. Events returned via the API contain additional information about the most recent success, response, or failure of the event’s delivery attempt.
14.4. Webhook Events & Related API Resources
This is a list of all webhook events and the related API resources that can be fetched to get more information about the event.
Event Types | Resource(s) |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
none |
|
|
|
none |
|
none |
|
|
|
none |
|
none |
|
none |
15. Webhook Subscriptions
15.1. List all Subscriptions
List all subscriptions associated with the current user token.
SCOPE: Subscriptions are scoped to the API Client itself. Only the client that created the subscription can list the subscription. |
GET /subscription[?enabled=<enabled>&next_cursor=<next_cursor>]
15.1.1. Parameters
Name | Type | Description |
---|---|---|
enabled |
boolean |
[optional] flag to return only enabled subscriptions (default: false) |
next_cursor |
string |
[optional] fetch the next batch/page from this cursor |
15.1.2. Response
Status: 200 OK
{
"data": [{
"id": "4370bfdb-af96-430c-9871-90badf4d5608",
"targetType": "PROFILE",
"targetId": 789,
"externalId": "some_external_id",
"url": "https://foobar.com/callbacks/dotloopwebhook",
"eventTypes": ["LOOP_CREATED", "LOOP_UPDATED"],
"signingKey": "super_secret_key",
"enabled": true
},{
"id": "d5871d89-c901-4a8f-9b7b-f9f183624edc",
"targetType": "USER",
"targetId": 100,
"externalId": "user_id_in_db",
"url": "https://foobar.com/callbacks/dotloopwebhook",
"eventTypes": ["CONTACT_CREATED", "CONTACT_UPDATED"],
"signingKey": "super_secret_key",
"enabled": true
}],
"meta": {
"nextCursor: "base64_string"
}
}
15.2. Get a Subscription
Retrieve an individual subscription by id.
Scope: Subscriptions are scoped to the API Client itself. Only the client that created the subscription can get the subscription. |
GET /subscription/:subscription_id
15.2.2. Response
Status: 200 OK
{
"data": {
"id": "4370bfdb-af96-430c-9871-90badf4d5608",
"targetType": "PROFILE",
"targetId": 789,
"externalId": "your_external_id",
"url": "https://foobar.com/callbacks/dotloopwebhook",
"eventTypes": ["LOOP_CREATED", "LOOP_UPDATED"],
"signingKey": "super_secret_key",
"enabled": true
}
}
15.3. Create a Subscription
Create a new subscription.
|
POST /subscription
15.3.1. Parameters
Name | Type | Description |
---|---|---|
targetType |
string |
Type of target (USER or PROFILE) |
targetId |
number |
A dotloop user id or profile id depending on the targetType. |
eventTypes |
string list |
List of event types to deliver. Refer to Event Types By Target Type Table. |
url |
string |
The url you wish to have webhook events POST to. |
signingKey |
string |
[optional] Secret key to be used for generating event signature header. |
externalId |
string |
[optional] An identifier, useful to you, that will be included in the body of every webhook event. |
15.3.2. Example
{
"targetType": "USER",
"targetId": 7083432,
"eventTypes": ["CONTACT_CREATED", "CONTACT_UPDATED"],
"url": "https://foobar.com/callbacks/dotloopwebhook",
"signingKey": "super_secret_key",
"externalId": "user_id_in_db"
}
15.3.3. Response
Status: 200 OK
{
"data": {
"id": "ab0e1759-9419-4c51-ae03-aeb296f815ef",
"targetType": "USER",
"targetId": 7083432,
"eventTypes": ["CONTACT_CREATED", "CONTACT_UPDATED"],
"url": "https://foobar.com/callbacks/dotloopwebhook",
"externalId": "user_id_in_db",
"signingKey": "super_secret_key",
"enabled": true
}
}
15.4. Update a Subscription
Update an existing subscription by id.
|
PATCH /subscription/:subscription_id
15.4.1. Parameters
Name | Type | Description |
---|---|---|
eventTypes |
string list |
List of event types to deliver. Refer to Event Types By Target Type Table. |
url |
string |
The url you wish to have webhook events POST to. |
signingKey |
string |
Secret key to be used for generating event signature header. |
externalId |
string |
An identifier, useful to you, that will be included in the body of every webhook event. |
enabled |
boolean |
Enable or disable the subscription. |
15.4.3. Response
Status: 200 OK
{
"data": {
"id": "ab0e1759-9419-4c51-ae03-aeb296f815ef",
"targetType": "USER",
"targetId": 7083432,
"eventTypes": ["CONTACT_CREATED", "CONTACT_UPDATED"],
"url": "https://foobar.com/callbacks/dotloopwebhook",
"externalId": "user-guuid-abcd-123efg",
"signingKey": "super_secret_key",
"enabled": true
}
}
15.5. Delete a Subscription
Delete an existing subscription by id.
|
DELETE /subscription/:subscription_id
16. Webhook Events
16.1. List all Events
List all events associated with the subscription.
|
GET /subscription/:subscription_id/event[?delivery_status=<delivery_status>&next_cursor=<next_cursor>]
16.1.1. Query Parameters
Name | Type | Description |
---|---|---|
delivery_status |
string |
[optional] filter events by a delivery status, see Webhooks Event Delivery Statuses |
next_cursor |
string |
[optional] fetch the next batch/page from this cursor |
16.1.2. Response
Status: 200 OK
{
"data": [{
"id": "5f6667e3-4b65-4025-9d64-f8d695b3ebb5",
"subscriptionId": "4370bfdb-af96-430c-9871-90badf4d5608",
"createdOn": "2023-11-01T20:20:08.186Z",
"eventType": "LOOP_UPDATED",
"eventData": {
"id": 6611120
},
"deliveryStatus": "SUCCESS",
"deliveryAttempts": 1,
"responseData": [
{
"responseCode": 200,
"responseHeaders": {
"content-type": "application/json; charset=utf-8",
…
},
"responseBody": "Hello World",
"url": "https://foobar.com/callbacks/dotloopwebhook",
"requestSentAt": "2023-11-01T20:20:10.186Z",
"durationMs": 5260
}
]
},{
"id": "a22e5a45-f9db-4250-8157-c7e2a5d21ab9",
"subscriptionId": "4370bfdb-af96-430c-9871-90badf4d5608",
"createdOn": "2023-11-01T20:19:50.722Z",
"eventType": "LOOP_CREATED",
"eventData": {
"id": 6611120
},
"deliveryStatus": "SUCCESS",
"deliveryAttempts": 1,
"responseData": [
{
"responseCode": 200,
"responseHeaders": {
"content-type": "application/json; charset=utf-8",
…
},
"responseBody": "Hello World",
"url": "https://foobar.com/callbacks/dotloopwebhook",
"requestSentAt": "2023-11-01T20:19:55.722Z",
"durationMs": 5260
}
]
}],
"meta": {}
}
16.2. Get an Event
Retrieve an individual event by id.
|
GET /subscription/:subscription_id/event/:event_id
16.2.2. Response
Status: 200 OK
{
"data": {
"id": "5f6667e3-4b65-4025-9d64-f8d695b3ebb5",
"subscriptionId": "4370bfdb-af96-430c-9871-90badf4d5608",
"createdOn": "2023-11-01T20:20:08.186Z",
"eventType": "LOOP_UPDATED",
"eventData": {
"id": 6611120
},
"deliveryStatus": "SUCCESS",
"deliveryAttempts": 1,
"responseData": [
{
"responseCode": 200,
"responseHeaders": {
"content-type": "application/json; charset=utf-8",
…
},
"responseBody": "Hello World",
"url": "https://foobar.com/callbacks/dotloopwebhook",
"requestSentAt": "2023-11-01T20:19:55.722Z",
"durationMs": 5260
}
]
}
}
17. Addendum
17.1. Types / Constants
17.1.1. Built-in Contact/Loop Participant Roles
-
ADMIN
-
optional fields:
ID
,License #
-
-
APPRAISER
-
optional fields:
ID
,License #
-
-
BUYER_ATTORNEY
-
optional fields:
ID
,License #
-
-
BUYER
-
optional fields:
Marital Status
-
-
BUYING_AGENT
-
optional fields:
Fax
,ID
,License #
-
-
BUYING_BROKER
-
optional fields:
Fax
,ID
,License #
-
-
ESCROW_TITLE_REP
-
optional fields:
ID
,License #
-
-
HOME_IMPROVEMENT_SPECIALIST
-
optional fields:
ID
,License #
-
-
HOME_INSPECTOR
-
optional fields:
ID
,License #
-
-
HOME_SECURITY_PROVIDER
-
optional fields:
ID
,License #
-
-
HOME_WARRANTY_REP
-
optional fields:
ID
,License #
-
-
INSPECTOR
-
optional fields:
ID
,License #
-
-
INSURANCE_REP
-
optional fields:
ID
,License #
-
-
LANDLORD
-
optional fields:
ID
,License #
-
-
LISTING_AGENT
-
optional fields:
Fax
,ID
,License #
-
-
LISTING_BROKER
-
optional fields:
Fax
,ID
,License #
-
-
LOAN_OFFICER
-
optional fields:
ID
,License #
-
-
LOAN_PROCESSOR
-
optional fields:
ID
,License #
-
-
MANAGING_BROKER
-
optional fields:
ID
,License #
-
-
MOVING_STORAGE
-
optional fields:
ID
,License #
-
-
OTHER
-
optional fields:
ID
,License #
-
-
PROPERTY_MANAGER
-
optional fields:
ID
,License #
-
-
SELLER_ATTORNEY
-
optional fields:
ID
,License #
-
-
SELLER
-
optional fields:
Marital Status
-
-
TENANT_AGENT
-
optional fields:
ID
,License #
-
-
TENANT
-
optional fields:
ID
,License #
,Marital Status
-
-
TRANSACTION_COORDINATOR
-
optional fields:
ID
,License #
-
-
UTILITIES_PROVIDER
-
optional fields:
ID
,License #
-
Custom roles are not listed here |
17.1.3. Loop Transactions corresponding Statuses
-
PURCHASE_OFFER
-
PRE_OFFER
-
UNDER_CONTRACT
-
SOLD
-
ARCHIVED
-
-
LISTING_FOR_SALE
-
PRE_LISTING
-
PRIVATE_LISTING
-
ACTIVE_LISTING
-
UNDER_CONTRACT
-
SOLD
-
ARCHIVED
-
-
LISTING_FOR_LEASE
-
PRE_LISTING
-
PRIVATE_LISTING
-
ACTIVE_LISTING
-
UNDER_CONTRACT
-
LEASED
-
ARCHIVED
-
-
LEASE_OFFER
-
PRE_OFFER
-
UNDER_CONTRACT
-
LEASED
-
ARCHIVED
-
-
REAL_ESTATE_OTHER
-
NEW
-
IN_PROGRESS
-
DONE
-
ARCHIVED
-
-
OTHER
-
NEW
-
IN_PROGRESS
-
DONE
-
ARCHIVED
-
17.1.4. Webhooks Event Targets and Corresponding Event Types
-
USER
-
CONTACT_CREATED
-
CONTACT_UPDATED
-
CONTACT_DELETED
-
PROFILE_UPDATED
-
USER_PROFILE_ACTIVATED
-
USER_PROFILE_DEACTIVATED
-
USER_ADDED_TO_PROFILE
-
USER_REMOVED_FROM_PROFILE
-
-
PROFILE
-
LOOP_CREATED
-
LOOP_UPDATED
-
LOOP_MERGED
-
LOOP_PARTICIPANT_CREATED
-
LOOP_PARTICIPANT_UPDATED
-
LOOP_PARTICIPANT_DELETED
-
17.1.5. Webhooks Event Delivery Statuses
-
PENDING
-
description: The event has been created and is waiting to be scheduled.
-
-
PENDING_RETRY
-
description: The previous delivery attempt did not result in a 2xx response, retry is waiting to be scheduled.
-
-
SCHEDULED
-
description: The event is scheduled to be delivered immediately or at its next interval, see back-off policy in Failure Handling.
-
-
SUCCESS
-
description: The event was successfully delivered.
-
-
FAILED
-
description: Delivery attempts were exhausted, no further attempts will be executed.
-
-
DISABLED
-
description: The subscription for this event has been disabled, no delivery attempt will be executed.
-
18. Client Errors
18.1. HTTP status codes
This API follows the common HTTP status code semantics. List of possible Client errors:
Error Code | Description |
---|---|
400 Bad Request |
The request is invalid, e.g. request payload can’t be parsed |
401 Unauthorized |
The access token is invalid or expired. Obtain a new token using the refresh token and
insert into request header: |
403 Forbidden |
The request is denied, e.g. you don’t have the privileges to access or create the resource |
404 Not Found |
The requested resource does not exist |
422 Unprocessable Entity |
The syntax of the request is correct but semantically erroneous |
429 Too Many Requests |
The caller exceeded the rate limit |
19. Changelog
-
05/20/2025
-
Added support for new Webhook event types Webhooks Event Targets and Corresponding Event Types
-
-
08/14/2024
-
Added support for 301 Redirect for Loop Merges, Get a Loop
-
-
05/20/2024
-
Added support for webhook Subscription Events and documentation
-
-
10/30/2023
-
Introduce Webhooks (Initial Release) as an "Initial Release" public api feature to create, list and update subscriptions, and list events.
-
Introduced api documentation for Webhook Subscriptions and Webhook Events
-
Added constants for Webhooks Event Delivery Statuses
-
Added constants for Webhooks Event Targets and Corresponding Event Types
-
-
09/06/2018
-
Added support to filter for multiple transaction types, e.g.
transaction_status=PRE_OFFER|PRE_LISTING
Loop API
-
-
08/08/2018
-
Adding support for updating custom loop template fields in Loop Detials API
-
-
04/20/2018
-
Include document metadata in Folder API via "include_documents" parameter
-
role
is now a required field when creating Loop Participants
-
-
03/13/2018
-
Added additional data fields to participants api
-
-
11/01/2017
-
Fix issue where opening downloaded documents via the API triggered a print dialog to appear
-
-
10/04/2017
-
Fix issue where
updated
field for loops was not updated upon document uploads
-
-
09/20/2017
-
Global Templates are now applied at loop creation time
-
-
08/24/2017
-
Fix issue where
updated
field for loops was not updated upon task changes -
Add
updated
timestamp for document entities
-
-
07/26/2017
-
Added support to filter for multiple transaction types, e.g.
transaction_type=PURCHASE_OFFER|LISTING_FOR_SALE
-
Introducing Folder API to create, list and rename folders
-
Introducing Document API to upload and download documents
-
FIX:
404 Not Found
was returned in some users whose default profile id is not set correctly.
-
-
07/12/2017
-
Introduce Activity API which retrieve all loop activities
-
New Filter syntax with param
?filter=…
(we continue to supportupdated_min
parameter which is now deprecated) Examples: Contact API, Loop API -
2 new Loop filters: a)
created_min
to list all loops created after a specific time, b)transaction_type
to list all loops of the specified transaction type -
New sort field: Sort Loops by
created
timestamp -
Include Loop details in summaries via
?include_details=true
-
-
06/28/2017
-
FIX: templates can now also be read with
loop:write
scope as they might be required to create a loop -
FIX: Loop-It™ does not fail if the a participant is added with the same email as the account email (ignored)
-
-
06/16/2017
-
Allow redirect to the
redirect_uri
via new&redirect_on_deny=[true|false]
param, which allows client applications to control the experience if user denies access, see here. -
FIX: Show correct total counts (in meta section) in
GET /loop
responses -
FIX: Fix
403 Forbidden
issue affecting some users
-
-
05/31/2017
-
Loops and Contacts returned with
created
timestamp (in addition toupdated
field) -
Custom contact or loop participant roles are supported now
-
-
05/17/2017
-
Loop list supports new query param
updated_min
to select loops updated since a timestamp -
requiresTemplate
flag on profile which defines whether a template id is required to create a loop -
FIX: transaction type in the request takes precedence over loop template transaction type now
-
-
05/10/2017
-
Loop Summaries contains
loopUrl
property now -
Contacts API supports
company
androle
property now -
Loop list can be sorted now (e.g.
…&sort=purchase_price:desc
)
-
-
04/19/2017
-
Added the ability to add, update and delete Loop Participants
-
Paginate thru loop and contact lists via new params
batch_size
andbatch_number
-
Contacts API changes
-
contact items now have a last
updated
timestamp -
introduce
updated_min
query param to select contacts updated since a timestamp
-
-
-
04/05/2017
-
Introducing Loop Tasks APIs
-
-
03/23/2017
-
Introduced
deactivated
flag for profiles -
Loop-it™ returns now mobile-ready URLs
-
Loop task count (total/completed) returned in loop APIs
-
-
03/08/2017
-
Introduced
default
flag for profiles -
Added new
GET /account
API (token requiresaccount:read
scope to be able to access this api)
-
20. FAQ
Are there more than one access or refresh token valid for a particular client/user combination at a given time?
No, there can be only 1 token valid at a time. This also means if you refresh an access token, any previously issued access token for that user will be invalidated.
Yes, otherwise all instances within your cluster may start racing to refresh the access token.
Yes. We rate limit client requests in order to protect our service against abuse or DOS attacks. Today we allow each client application to make up to 100 requests per minute for a user. Once client exceed this limit, they’ll receive a 429 Error response and need to wait till the rate limit gets reset. Clients can track their limits by evaluating the following response headers which indicate the actual limit, the remaining calls and when the limit gets reset (ms), e.g.:
X-RateLimit-Limit: 100 X-RateLimit-Remaining: 34 X-RateLimit-Reset: 32000
There could be multiple reasons for a 403 error returned by the API:
-
You may be attempting to access a non-
INDIVIDUAL
profile (e.g.OFFICE
orCOMPANY
profiles) -
You may be using the wrong token when accessing a profile. Tokens are issued on behalf of a user, so you can only access resources which are the user has permission to access
-
The scope of you client may be incorrect hence the client is not authorized to make the API request. Example: The application is trying to update a loop but the application has only
loop:read
scope (required:loop:write
orloop:*
)