Introduction
ListSMRT will be building an API integration with client’s existing systems, enabling them to use output generated from ListSMRT to their system. With this API, clients can:
Retrieve and update data from their CMS to ListSMRT
Using ListSMRT to generate content details for their listings
Push it back to their CMS system instantly.
General Flow
The client will need to add a new button into their CMS that is basically just a URL (
https://api-app.listsmrt.com/api/v1/client/authorize/?api_key=meowmeowmeow) pointing to Listsmrt based on our provided format.When the user clicks the URL, it will redirect them to Listsmrt platform.
Users will follow the usual flow (log in if they haven't) and generate the content.
The webhook is then triggered and sends the data to the client system.
The webhook will be triggered when the user manually clicks the button "Send data to CRM" to confirm.
Developers on the client side will need to implement their solution for receiving our webhook data and storing it into their system.
Preparation Steps
To seamlessly integrate with ListSMRT's Generation Page and automate the property listing generation process, follow these steps:
Generate API Key
Please contact admin to generate an API Key. This key will serve as the authentication token for accessing ListSMRT's API.
API Key: DjEzRb8PkMvFY5YXgQhaW6dTySKcV5tmpepuV630Kn4xe5TRfFxNYkv0HnFkh4hH
Whitelist Domains
The user admin must whitelist the domain(s) that will be making API requests to ListSMRT (Please contact admin to whitelist the domain(s)). This ensures secure communication between the client's CMS system and ListSMRT.
Domain: https://example.com
Configure Webhook
The user admin should set up a webhook URL and a secret key. This URL will be used to receive data generated by ListSMRT (Please contact admin to set up a webhook URL and a secret key). The secret key adds an extra layer of security to the webhook communication.
Webhook URL: https://api-example.com/property-notification-handler
Secret Key: Ba2R1DUHZy7avEvM7ty2zLYX
Integration Steps
Follow these steps to seamlessly integrate ListSMRT's Generation Page:
Implement Button Element HTML
The client needs to implement an HTML button element that redirects to the authorization page:
https://api-app.listsmrt.com/api/v1/client/authorize/?api_key=DjEzRb8PkMvFY5YXgQhaW6dTySKcV5tmpepuV630Kn4xe5TRfFxNYkv0HnFkh4hH&address=SydneyNSW,Australia&type=House&bed=2&bath=1&garage=1&parking=1&metadata=propertyId:1,userId:1
The URL requires the apiKey query parameter along with other relevant parameters to pre-populate values on ListSMRT's Generation Page.
ListSMRT's system will verify the API key, whitelist domains, and user login status before granting authorization.
Note: Users have the flexibility to include additional metadata parameters (e.g., property ID, user ID) to cater to their specific requirements. This metadata will be attached to the payload sent via the webhook.
The following are the parameters that the user needs to include in the URL's details
| Params | Description | Value |
|---|---|---|
| api_key | Required. An API key generated by the user admin | Type: String |
| highlight | Optional. A brief highlight or title for the property listing | Type: String |
| campaign_type | Optional. Specifies the type of campaign for the property | Type: Enum Options: Residential Sale, Residential Lease, Commercial Sale, Commercial Lease |
| address | Optional. The location address of the property | Type: String |
| type | Optional. The type of property | Type: Enum Options: Property Types Example: Apartment/Unit |
| land_size | Optional. The size of the land associated with the property | Type: String |
| strata_rates | Optional. The strata fees or rates associated with the property | Type: String |
| land_rates | Optional. The rates or taxes for the land | Type: String |
| water_rates | Optional. The water usage rates or fees for the property | Type: String |
| weekly_rent | Optional. The weekly rent amount for the property | Type: String |
| bed | Optional. The number of bedrooms in the property | Type: Number 0 - 10 Default: 0 |
| bath | Optional. The number of bathrooms in the property | Type: Number 0 - 10 Default: 0 |
| garage | Optional. The number of garage spaces available | Type: Number 0 - 10 Default: 0 |
| parking | Optional. The number of parking spaces available | Type: Number 0 - 10 Default: 0 |
| land_status | Optional. The status of the land | Type: Enum Options: New Construction, Established Property |
| feature_details_id | Optional. Specific feature details associated with the property | Type: String Options: Feature List *Feature detail IDs separated by commas Example: 1,3,5 |
| perfect_for | Optional. The target audience or groups that the property | Type: String Options: First Home Buyers, Singles, Upsizers, Young Couples, Downsizers, Families, Investors, Retirees *Perfect for separated by commas Example: Singles,Upsizers |
| tone_of_voice | Optional. Specifies the preferred tone of voice for the property description | Type: Enum Default: Neutral Options: Formal, Luxurious, Professional, Neutral, Friendly, Calm, Excited, Informal, Jovial |
| language_input | Optional. The language used as input | Type: Enum Default: en Options: en, es, fr, de, zh, zhtw, ja, ko, ru, ar, it, nl, pt, sv, da, no, fi, he, hi, id, tr, el, th, pl, hu, ro |
| language_output | Optional. The desired language for the output | Type: Enum Default: en Options: en, es, fr, de, zh, zhtw, ja, ko, ru, ar, it, nl, pt, sv, da, no, fi, he, hi, id, tr, el, th, pl, hu, ro |
| metadata | Optional. The additional parameters from user requirements | Type: String Format: key1:value1,key2:value2 |
Property Types
The specific property types can vary depending on the campaign type. In this documentation, we will outline the property types associated with two common campaign types: Residential Sale/Lease and Commercial Sale/Lease.
1. Residential Property Types:
| Property Type |
|---|
| House |
| Apartment/Unit |
| Townhouse |
| Villa |
| Land |
| Retirement Living |
| Acreage |
| Semi/Duplex |
| Semi |
| Duplex |
2. Commercial Property Types:
| Property Type |
|---|
| Offices |
| Serviced Offices |
| Shop & Retail |
| Factory |
| Warehouse |
| Industrial |
| Land |
| Development Site |
| Hotel |
| Motel |
| Leisure |
| Medical |
| Consulting |
| Commercial Farming & Rural |
| Showroom & large Format Retail |
| Other |
Features
Below is a list of features available in the ListSMRT system:
| Feature Detail ID | Feature Name |
|---|---|
| 1 | Ensuite |
| 2 | Dishwasher |
| 3 | Study |
| 4 | Built in wardrobes |
| 5 | Alarm system |
| 6 | Broadband |
| 7 | Floorboards |
| 8 | Gym |
| 9 | Rumpus room |
| 10 | Workshop |
| 11 | Indoor spa |
| 12 | Fireplace |
| 13 | Intercom |
| 14 | Pay TV |
| 15 | Air conditioning |
| 16 | Solar panels |
| 17 | Heating |
| 18 | High energy efficiency |
| 19 | Water tank |
| 20 | Solar hot water |
| 21 | Gas |
| 22 | Swimming pool |
| 23 | Garage |
| 24 | Balcony |
| 25 | Outdoor area |
| 26 | Undercover parking |
| 27 | Shed |
| 28 | Fully fenced |
| 29 | Outdoor spa |
| 30 | Pet friendly |
| 31 | Courtyard |
Generate Content
Upon successful authorization, users will be directed to the "Generate" page.
The page will be automatically populated based on parameters sent by the client's Content Management System (CMS).
Users can review the populated information and make any necessary adjustments.
Clicking the "Generate" button triggers the content generation process.
After clicking, users will be redirected to the "Result Generate" page.
Trigger Notification
Example of how the HTTP notification will be sent from ListSMRT side:
// This example is built with Node.js
const axios = require('axios');
const url = 'https://api-example.com/property-notification-handler/';
const headers = {
'User-Agent': 'ListSMRT',
'ListSMRT-Signature': 't=1693446511,v1=15af584462edbb5052d302600bb2f832be45f608a5479a9209e324e4f7458233',
'Content-type': 'application/json',
};
const data = {
"created": "2023-09-25T06:38:50.180Z",
"type": "property.generate-content",
"data": {
"bed": 3,
"bath": 4,
"type": "Townhouse",
"garage": 2,
"address": "111 Harrington Street",
"parking": 3,
"highlight": "Strategic location",
"land_size": "",
"land_rates": "",
"land_status": "",
"perfect_for": null,
"tenure_type": "",
"water_rates": "",
"weekly_rent": "",
"strata_rates": "",
"the_location": true,
"campaign_type": "Sale",
"land_area_max": "",
"land_area_min": "",
"tone_of_voice": "Neutral",
"floor_area_max": "",
"floor_area_min": "",
"language_input": "en",
"language_output": "en",
"feature_details_id": "1,7,15,19",
"nabers_energy_rating": "",
"metadata": {
"requestId": "mUvZ3voNOQwzcmuJjnR2GXccd8looWlo",
"secretKey": "IvXfgldQYokWHcgM7b9E664NeemPAylj"
},
},
"result": "Check out this spacious 3 bed, 4 bath townhouse with a strategic location! Don't miss your chance to own it. #ForSale #Townhouse"
}
axios.post(url, data, { headers })
.then((response) => {
console.log('Response:', response.data);
})
.catch((error) => {
console.error('Error:', error);
});
curl --request POST \
--url https://api-example.com/property-notification-handler \
--header 'Content-Type: application/json' \
--header 'ListSMRT-Signature: t=1693446511,v1=15af584462edbb5052d302600bb2f832be45f608a5479a9209e324e4f7458233' \
--header 'User-Agent: ListSMRT' \
--data '{
"created": "2023-09-25T06:38:50.180Z",
"type": "property.generate-content",
"data": {
"bed": 3,
"bath": 4,
"type": "Townhouse",
"garage": 2,
"address": "111 Harrington Street",
"parking": 3,
"highlight": "Strategic location",
"land_size": "",
"land_rates": "",
"land_status": "",
"perfect_for": null,
"tenure_type": "",
"water_rates": "",
"weekly_rent": "",
"strata_rates": "",
"campaign_type": "Sale",
"land_area_max": "",
"land_area_min": "",
"tone_of_voice": "Neutral",
"floor_area_max": "",
"floor_area_min": "",
"language_input": "en",
"language_output": "en",
"feature_details_id": "1,7,15,19",
"nabers_energy_rating": "",
"metadata": {
"requestId": "mUvZ3voNOQwzcmuJjnR2GXccd8looWlo",
"secretKey": "IvXfgldQYokWHcgM7b9E664NeemPAylj"
},
},
"result": "Check out this spacious 3 bed, 4 bath townhouse with a strategic location! Don'\''t miss your chance to own it. #ForSale #Townhouse"
}'
Once the content has been generated, users have the option to manually trigger a notification.
The notification sends the generated content's data to the preconfigured URL in the CMS.
The payload includes any metadata provided by the user.
Webhook
To ensure a secure and reliable data exchange between client's CMS system and ListSMRT, follow these guidelines for setting up and managing the webhook integration:
URL and Security
ListSMRT's system requires a client's webhook to use HTTPS for secure data transmission and POST method.
When ListSMRT sends data to the client's webhook, the system must respond with an HTTP status code 200 OK to acknowledge successful receipt of the data.
Signature
For enhanced security, each webhook payload includes a signature.
The signature is a unique token that ListSMRT generates for each payload, which the client's CMS system can use to verify the authenticity of the incoming request.
Before processing the payload, the client CMS system should calculate the signature using the same algorithm provided by ListSMRT and compare it with the signature included in the payload.
Validate Signature
Example of how to validate signature:
// This example is built with Node.js
const crypto = require("crypto");
const express = require("express");
const bodyParser = require("body-parser");
const app = express();
// Middleware to parse JSON payloads
app.use(bodyParser.json());
// Your endpoint's signing secret
const API_KEY =
"DjEzRb8PkMvFY5YXgQhaW6dTySKcV5tmpepuV630Kn4xe5TRfFxNYkv0HnFkh4hH";
// Your custom middleware to verify the signature
app.use((req, res, next) => {
const header = req.get("ListSMRT-Signature");
const elements = header.split(",");
let timestamp;
let receivedSignature;
// Step 1: Extract timestamp and signatures from the header
for (const element of elements) {
const [prefix, value] = element.split("=");
if (prefix === "t") {
timestamp = value;
}
if (prefix === "v1") {
receivedSignature = value;
}
}
// Step 2: Prepare the signed_payload string
const payload = JSON.stringify(req.body);
const signedPayload = `${timestamp}.${payload}`;
// Step 3: Determine the expected signature
const hmac = crypto.createHmac("sha256", API_KEY);
const expectedSignature = hmac.update(signedPayload).digest("hex");
// Step 4: Compare the signatures
let isValid = false;
if (
crypto.timingSafeEqual(
Buffer.from(expectedSignature),
Buffer.from(receivedSignature)
)
) {
const currentTimestamp = Math.floor(Date.now() / 1000);
if (Math.abs(currentTimestamp - timestamp) < 300) {
// 5 minutes tolerance
isValid = true;
}
}
if (isValid) {
next();
} else {
res.status(401).send("Invalid signature");
}
});
app.post("/property-notification-handler", (req, res) => {
res.send("Payload verified");
});
app.listen(3000, () => {
console.log("Server running on http://localhost:3000");
});
Example of how to validate signature from ListSMRT:
Step 1: Extract the timestamp and signatures from the header
- Begin by splitting the header using the comma (,) character as the separator. This operation generates a list of individual elements. Proceed to split each element further using the equals (=) character as the separator, resulting in pairs of prefixes and values. The prefix "t" signifies the timestamp, while the prefix "v1" denotes the signature.
Step 2: Prepare the signed_payload string
- Formulate the signed_payload string by combining the following components:
- The timestamp, presented as a string.
- The period (.) character.
- The JSON payload, which corresponds to the content of the request body.
Step 3: Determine the expected signature
- Calculate an HMAC (Hash-Based Message Authentication Code) using the SHA256 hashing algorithm. Utilize the signing secret associated with the endpoint as the cryptographic key, and take the signed_payload string as the input message.
Step 4: Compare the signatures
Match the signature(s) found within the header against the anticipated signature. In case of an exact match, calculate the time disparity between the current timestamp and the received timestamp. Subsequently, assess whether this disparity falls within an acceptable range defined by your tolerance level.
To thwart timing-based attacks, utilize a constant-time string comparison method to equate the anticipated signature with each of the received signature.