Leasey.AI

Leasey.AI API Documentation

The Leasey External API allows authorized partners to read reporting data and manage property listings programmatically. Built on REST principles โ€” standard HTTP methods, resource-based URLs, and JSON responses.

Version 2.0 Protocol HTTPS Format JSON

Last updated: June 01, 2026

What changed in v2.0

Version 2.0 is a full REST redesign. If you integrated with v1, update your requests according to the tables below.

Reports API

v1 (old)v2 (current)What changed
POST /reports + "report_type":"showings"GET /reports/showingsEach report type is its own URL. Use GET, not POST.
POST /reports + "report_type":"leads"GET /reports/leadsDedicated URL per resource.
POST /reports + "report_type":"properties"GET /reports/propertiesDedicated URL per resource.
password in JSON bodyX-Company-Password headerPasswords no longer go in request bodies for read ops.
company_id in JSON bodycompany_id query parameterFilters are query params on GET requests.

Properties API

v1 (old)v2 (current)What changed
POST /properties + "action":"create"POST /propertiesThe action field is removed. POST always means create.
PUT /properties + "property_id" in bodyPUT /properties/{property_id}Property ID moves to the URL path.

1. Getting Started

To use the Leasey External API you need three credentials:

CredentialDescriptionHow to obtain
API TokenApplication-level bearer tokenContact Leasey support
Company IDYour company's numeric identifierLeasey dashboard โ†’ Settings
Company PasswordYour company's API passwordLeasey dashboard โ†’ Settings โ†’ API
bash โ€” first request
curl -X GET "https://<base-url>/reports/showings?company_id=123" \
  -H "Authorization: Bearer <your-api-token>" \
  -H "X-Company-Password: <your-company-password>"

2. Authentication

Every request requires two levels of authentication.

Level 1 โ€” API Token

Authorization: Bearer <api_token>
โ„น๏ธ
The Bearer prefix is required. As a fallback you may pass the token as ?token=<api_token> for debugging only โ€” query strings appear in server logs and should not be used in production.

Level 2 โ€” Company Credentials

Request typecompany_idpassword
GET (reports, leads read)Query parameterX-Company-Password header
POST / PUT (property write)JSON body fieldJSON body field "password"
๐Ÿ”’
Using X-Company-Password on GET requests keeps the password out of server logs, CDN caches, and browser history.

Authentication errors

ScenarioStatusMessage
Token missing401Authentication token required
Token incorrect401Invalid authentication token
Token length mismatch401The provided token does not match the expected token โ€” verify you are copying the full token (expected length: 48 characters)
Password missing401X-Company-Password header is required
Password incorrect401Invalid company password
Company not found401Company {id} not found
No admin for company400No admin user found for company {id}. Contact support.

3. Environments & Base URL

Production:  https://uvciw58bnh.execute-api.ca-central-1.amazonaws.com/production
Staging:     https://hs52apwqwh.execute-api.ca-central-1.amazonaws.com/staging
๐Ÿ’ก
Run serverless info --stage production from reports-api-service/ to get the exact URL for your stage. All examples use {base_url} as a placeholder.

4. Reports API

GET
/reports/showings
Returns all property showing and viewing activity for a company.

Headers

HeaderRequiredDescription
AuthorizationYesBearer <api_token>
X-Company-PasswordYesYour company API password

Query Parameters

ParameterTypeRequiredDefaultDescription
company_idintegerYesโ€”Your company identifier
start_dateISO-8601NoJan 1 current yearFilter showings on or after โ€” format: YYYY-MM-DDTHH:MM:SS
end_dateISO-8601NoNoneFilter showings on or before
bash
curl -X GET "{base_url}/reports/showings?company_id=123&start_date=2024-11-01T00:00:00&end_date=2024-11-30T23:59:59" \
  -H "Authorization: Bearer <api_token>" \
  -H "X-Company-Password: <company_password>"

Response Fields

FieldTypeDescription
Property_NamestringBuilding or complex name
Unit_NamestringUnit identifier
Unit_AddressstringStreet address
Unit_IDintegerInternal unit ID
Property_IDintegerInternal property ID
Unit_TypestringProperty type
Unit_statusstringActive or Archived
Unit_publish_statusstringPublished or Unpublished
Vacancy_StatusstringVacant or Occupied
Rental_SpacestringEntire place, Partial place
Lease_TermstringFixed term, Month to month, etc.
Suite_NumberstringSuite / unit number
Rent_AmountnumberMonthly rent
Parking_StallsintegerNumber of parking stalls
BedroomsnumberNumber of bedrooms
BathroomsnumberNumber of bathrooms
Square_FootageintegerUnit size in sq ft
Max_OccupancyintegerMaximum occupants
FurnishingstringFurnished / Unfurnished / Partially furnished
CountrystringCountry
Days_listedintegerDays on market
StoragestringYes or No
Date_startdatetimeShowing start time
Date_enddatetimeShowing end time
Showing_StatusstringScheduled, Confirmed, Cancelledโ€ฆ
TeamMemberstringAssigned team member name
TeamMemberRolstringTeam member role
Lead_sourcestringOrigin of lead (Website, Zillowโ€ฆ)
Showing_Feedbackstring | nullPost-showing feedback
Person_IDinteger | nullLead's person ID
Application_IDinteger | nullApplication ID
Lead_Namestring | nullFull name
Lead_First_Namestring | nullFirst name
Lead_Last_Namestring | nullLast name
Lead_Emailstring | nullEmail address
Lead_Phonestring | nullPhone with dial code
Application_Statusstring | nullLead, Application In Progressโ€ฆ
Applicant_Ratinginteger | nullRating 0โ€“5
Tenancy_Application_Statusstring | nullDocument review status
Lead_Approval_Statusstring | nullApproval status
Lead_Notestring | nullInternal notes
GET
/reports/leads
Returns prospect and applicant data for a company.

Query Parameters

ParameterTypeRequiredDefaultDescription
company_idintegerYesโ€”Your company identifier
start_dateISO-8601NoJan 1 current yearFilter by lead creation date (on or after)
end_dateISO-8601NoNoneFilter by lead creation date (on or before)
bash
curl -X GET "{base_url}/reports/leads?company_id=123&start_date=2024-01-01T00:00:00" \
  -H "Authorization: Bearer <api_token>" \
  -H "X-Company-Password: <company_password>"

Response Fields

FieldTypeDescription
Application_IDintegerApplication identifier
Applicant_IDintegerApplicant record identifier
Person_IDintegerPerson identifier
Lead_NamestringFull name
Lead_First_NamestringFirst name
Lead_Last_NamestringLast name
Lead_EmailstringEmail address
Lead_PhonestringPhone with dial code
Application_StatusstringLead, Incomplete Application, Application Received, Application Under Review, Lease Signed, Archived
Applicant_RatingnumberRating 0โ€“5
Tenancy_Application_StatusstringDocument review status
Lead_Approval_StatusstringApproval status
Unit_NamestringProperty of interest
Property_NamestringBuilding name
Unit_AddressstringStreet address
Full_AddressstringConcatenated full address
Unit_CitystringCity
Unit_ProvincestringProvince / state
Unit_Publish_StatusstringPublished or Unpublished
Vacancy_StatusstringVacant or Occupied
Rent_AmountnumberMonthly rent
Rent_Payment_FrequencystringPayment frequency
Lease_TypestringFixed term, Month to month, etc.
BedroomsnumberBedrooms
BathroomsnumberBathrooms
FurnishingstringFurnished, Unfurnished, Partially furnished
Lead_SourcestringOrigin of lead
Lead_Notestring | nullInternal notes
Lead_Date_CreateddatetimeWhen lead was created
Application_Sent_Datedatetime | nullWhen application was sent
Application_Received_Datedatetime | nullWhen application was received back
GET
/reports/properties
Returns the full property portfolio. Always returns all properties โ€” date parameters are not applicable.

Query Parameters

ParameterTypeRequiredDescription
company_idintegerYesYour company identifier
bash
curl -X GET "{base_url}/reports/properties?company_id=123" \
  -H "Authorization: Bearer <api_token>" \
  -H "X-Company-Password: <company_password>"

Response Fields

FieldTypeDescription
Property_IDintegerUnique property identifier
Property_URL_SlugstringURL-safe slug
Property_NamestringBuilding name
Unit_NamestringUnit identifier
Unit_AddressstringStreet address
Unit_TypestringApartment, House, Townhouseโ€ฆ
Unit_StatusstringActive or Archived
Unit_Publish_StatusstringPublished or Unpublished
Rental_SpacestringEntire place, Partial place
Lease_TermstringFixed term, Month to monthโ€ฆ
Suite_NumberstringSuite / unit number
CitystringCity
Province_StatestringProvince or state
Postal_CodestringPostal / ZIP code
CountrystringCountry
Days_ListedintegerDays since marketing start
Marketing_Startdate | nullDate listed for marketing
Marketing_Enddate | nullDate taken off market (null = still active)
Date_Availabledate | nullMove-in available date
Rent_AmountnumberMonthly asking rent
Rent_Marketnumber | nullMarket rent (only if feature enabled)
Parking_StallsintegerNumber of parking stalls
BedroomsnumberBedrooms
BathroomsnumberBathrooms
Square_FootageintegerUnit size in sq ft
Max_OccupancyintegerMaximum occupants
FurnishingstringFurnished / Unfurnished / Partially furnished
StoragestringYes or No
Vacancy_StatusstringVacant or Occupied
Descriptionstring | nullProperty description
Latitudenumber | nullGPS latitude
Longitudenumber | nullGPS longitude
Lead_StatusstringNo Leads, 1-5 Leads, 6-10 Leads, 10+ Leads
Lead_CountintegerTotal lead count
Applicant_StatusstringBucketed label for applicant count
Applicant_CountintegerTotal applicant count
Lease_StatusstringNo Active Leases, 1 Active Lease, Multiple Leases
Lease_CountintegerActive lease count
Total_ApplicationsintegerAll-time applications
Showings_DisqualifiedintegerDisqualified showings
Showings_ScheduledintegerPending showings
Showings_ConfirmedintegerConfirmed showings
Showings_CancelledintegerCancelled showings
Showings_TotalintegerTotal showings
Contact_Namestring | nullAssigned contact person
Contact_EmailstringRouted inbox for this listing
Contact_Phonestring | nullContact phone
ImagesarrayImage URLs (up to 10, ordered)
Unit_AmenitiesarrayUnit-level amenity names
Unit_Amenities_CountintegerCount of unit amenities
Building_AmenitiesarrayBuilding-level amenity names
Building_Amenities_CountintegerCount of building amenities
Listing_URLstring | nullPublic listing page on Leasey

5. Properties API

POST
/properties
Creates a new property listing. Returns property_id โ€” store it for future updates.

Required Body Fields

FieldTypeDescription
company_idintegerYour company identifier
passwordstringYour company API password
property.namestringUnit or property name
property.addressstringStreet address
property.citystringCity name
property.province_statestringProvince or state (e.g. ON, BC)
property.countrystringCountry name
json โ€” example request body
{
  "company_id": 123,
  "password": "<company_password>",
  "property": {
    "name": "Unit 2B",
    "address": "456 Oak Ave",
    "city": "Toronto",
    "province_state": "ON",
    "country": "Canada",
    "postal_code": "M5V 2T6",
    "unit_type": "apartment",
    "rental_space": "entire place",
    "rent_amount": "2100",
    "rent_payment_frequency": "monthly",
    "renewal_frequency": "month to month",
    "bedrooms": 2,
    "bathrooms": 1,
    "size_sqft": 850,
    "furnishing": 2,
    "parking_stalls": 1,
    "date_available": "2025-02-01",
    "building_name": "Oak Ave Apartments",
    "features": [
      { "id": 1, "value": 1 },
      { "id": 13, "value": 1 }
    ],
    "building_amenities": [
      { "id": 4, "value": 1 }
    ],
    "restrictions": [1, 2],
    "marketplaces": [
      { "name": "zillow", "enabled": true }
    ]
  }
}

Response 201 Created

{
  "success": true,
  "message": "Property created successfully.",
  "property_id": "a3f7b2d1-e894-4c1a-b5f0-1234567890ab",
  "url_slug": "unit-2b",
  "building_id": 55,
  "floorplan_id": 12,
  "building_amenities": {
    "building_id": 55,
    "inserted": 1,
    "skipped": []
  },
  "team_members": {
    "assigned": [45, 101],
    "created": [
      {
        "team_member_id": 101,
        "user_id": 305,
        "person_id": 488,
        "email": "sara.white@mycompany.com",
        "already_existed": false
      }
    ],
    "skipped": []
  }
}
โš ๏ธ
Save the property_id. It is the only way to update this property later via PUT /properties/{property_id}.

Response Fields

FieldAlways presentDescription
property_idYesUUID of the created property. Store this โ€” required for future updates.
url_slugYesURL-safe slug generated from name + suite_unit
building_idOnly if building linkedID of the building the property was linked to
floorplan_idOnly if floorplan setID of the floorplan assigned
building_amenities.insertedOnly if building_amenities sentCount of amenity records inserted
building_amenities.skippedOnly if building_amenities sentArray of error strings for items that could not be saved
team_members.assignedOnly if team_members sentArray of team_member_id integers successfully assigned
team_members.createdOnly if team_members sentArray of objects for newly created team members
team_members.skippedOnly if team_members sentArray of error strings for items that could not be processed
PUT
/properties/{property_id}
Updates an existing property. Only fields in the property object are modified.
json โ€” example request body
{
  "company_id": 123,
  "password": "<company_password>",
  "property": {
    "rent_amount": "2200",
    "date_available": "2025-03-01"
  }
}
๐Ÿ”„
Partial update behavior: photos, features, building_amenities, restrictions replace all existing values when included. team_members is additive only โ€” existing assignments are never removed. marketplaces updates only the listed entries; others retain their current state.
GET
/properties/leads
Simplified, paginated lead list. Lighter than GET /reports/leads.

Query Parameters

ParameterTypeRequiredDefaultDescription
company_idintegerYesโ€”Your company identifier
passwordstringYesโ€”Your company API password
start_dateISO-8601NoNoneFilter leads created on or after
end_dateISO-8601NoNoneFilter leads created on or before
property_idUUIDNoNoneFilter by a specific property
limitintegerNo100 (max 500)Maximum records returned

6. Property Object Reference

All fields accepted in the property object for create and update requests.

Core Fields

FieldTypeRequiredDescription
namestringYesUnit or property name
addressstringYesStreet address
citystringYesCity name
province_statestringYesProvince or state code or full name (e.g. ON, Ontario)
countrystringYesCountry name
postal_codestringNoPostal or ZIP code
suite_unitstringNoSuite number. Used to generate the URL slug: {name}-{suite_unit}
descriptionstringNoProperty description text
date_availabledateNoMove-in available date (YYYY-MM-DD)

Pricing & Lease Fields

FieldTypeDefaultDescription
rent_amountstring or number0Monthly asking rent
security_depositnumber0Security deposit amount
rent_payment_frequencystringmonthlyPayment frequency โ€” see Enum Values
renewal_frequencystringmonth to monthLease renewal type โ€” see Enum Values

Physical Fields

FieldTypeDefaultDescription
unit_typestringapartmentProperty type โ€” see Enum Values
rental_spacestringentire placeRental space type โ€” see Enum Values
bedroomsnumber0Bedrooms (0.5 for bachelors)
bathroomsnumber0Bathrooms (e.g. 1, 1.5, 2)
size_sqftinteger0Unit size in square feet
max_occupancyintegerbedrooms ร— 2Maximum permitted occupants
parking_stallsintegernullNumber of parking stalls
furnishinginteger21 Furnished ยท 2 Unfurnished ยท 3 Partially furnished
storage_availableinteger21 Available ยท 2 Not available ยท 3 Available (extra cost)
rental_application_urlstring (URL)nullCustom external application URL
owner_user_idintegernullOverride the internal Leasey platform user assigned as property owner. Must be a users.id that belongs to your company. If invalid, silently falls back to the company's primary admin.
โš ๏ธ
use_custom_application_link is a company-level setting. Setting this to true routes all applicants across all properties in your company to the rental_application_url instead of the built-in Leasey form โ€” not just the property you are creating or updating.

Features (Unit Amenities)

The features array controls unit-level amenity and utility availability. Each entry has an id and a value: 1 = Included in rent ยท 2 = Available, not included ยท 3 = Not available. Only include features you want to set โ€” unspecified features default to not available.

"features": [
  { "id": 1,  "value": 1 },
  { "id": 13, "value": 1 },
  { "id": 20, "value": 2 }
]

Unit feature IDs โ€” complete list

IDFeatureCategory
1Air conditioningAmenity
2Balcony / patioAmenity
3BasementAmenity
4Bike storageAmenity
5ConciergeAmenity
6DishwasherAmenity
7Electrical car charging stationAmenity
8FireplaceAmenity
9Fitness center / gymAmenity
10FurnishedAmenity
11HeatingAmenity
12Hot tubAmenity
13In-suite washerAmenity
14ParkingAmenity
15SaunaAmenity
16Steam roomAmenity
17Storage lockerAmenity
18Swimming poolAmenity
19CableUtility
20ElectricityUtility
21InternetUtility
22Natural gasUtility
23WaterUtility
24Common areasAmenity
25Community laundryAmenity
26ElevatorAmenity
27On-site managerAmenity
28In-suite dryerAmenity
29Food waste disposalAmenity
30FreezerAmenity
31MicrowaveAmenity
32OvenAmenity
33RefrigeratorAmenity
34StoveAmenity
35Garbage disposalUtility
36Satellite TVUtility
37SewageUtility

Building Fields

FieldTypeDescription
building_namestringName of the building. If a building with this name already exists for your company, the property is linked to it; otherwise a new building is created.
building_idintegerLink to an existing building by ID (from prior create responses). Takes precedence over building_name.
number_of_unitsintegerTotal units in the building. Required to auto-create a building without an explicit building_id.

Building Amenities

The building_amenities array controls building-level amenities shared across all units. Each entry has an id and a value: 1 = Active/included ยท 2 = Available, not included ยท 3 = Not available. Providing this field replaces all existing building amenities. Requires building_id, building_name, or number_of_units to be set.

"building_amenities": [
  { "id": 4, "value": 1 },
  { "id": 11, "value": 1 }
]

Building amenity IDs โ€” complete list

IDBuilding Amenity
1Bike storage
2Concierge
3Electrical car charging station
4Fitness center / gym
5Hot tub
6Sauna
7Steam room
8Swimming pool
9Common areas
10Community laundry
11Elevator
12On-site manager

Owner Contact

Override the contact information shown on the listing.

FieldTypeDescription
first_namestringContact first name
last_namestringContact last name
emailstringContact email
phonestringPhone number (digits only, without dial code)
dial_codestringCountry dial code (e.g. "+1")
notify_emailbooleanSend email notifications to this contact
notify_smsbooleanSend SMS notifications to this contact

Team Members

Assign team members to the property. Three styles can be mixed in the same array:

json
"team_members": [
  { "team_member_id": 45 },
  { "email": "agent@mycompany.com" },
  {
    "email": "sara.white@mycompany.com",
    "first_name": "Sara",
    "last_name": "White",
    "role_id": 12,
    "is_admin": false,
    "timezone": "America/Vancouver"
  }
]
โ„น๏ธ
On PUT (update), team member assignments are additive only โ€” existing assignments are never removed. To remove a member, use the Leasey admin panel. If a new member creation fails, the error appears in team_members.skipped and the property creation itself is not rolled back.

Restrictions

Providing this field replaces all existing restrictions.

IDRestriction
1No dogs
2No smoking
3No short-term rentals
4No subletting
5No cats

Photos

Providing this field replaces all existing photos. Photos are displayed in the order provided; the first photo is used as the main listing image. URLs must start with https://.

"photos": [
  { "url": "https://cdn.example.com/unit2b-main.jpg" },
  { "url": "https://cdn.example.com/unit2b-kitchen.jpg" }
]

Marketplaces

Setting enabled: false unpublishes the listing. Only explicitly listed entries are affected; others retain their current state.

NamePlatformPaid
zillowZillowFree
zillowMultiZillow MultiFree
facebookFacebook MarketplaceFree
apartmentsStandardApartments.com StandardFree
apartmentsPremiumApartments.com PremiumFree
rentPandaRent PandaFree
rentalBeastRental BeastFree
realtorRealtor.caFree
rentFasterRent FasterFree
rentSeekerRent SeekerFree
rewREWFree
lqLQFree
listanzaListanzaFree
rentalSourceRental SourceFree
lespacLesPACFree
zumperPremiumZumper PremiumPaid
zumperStandardZumper StandardPaid
kijijiKijijiPaid
viewitView itPaid
livRentLiv RentPaid
craigslistCraigslistPaid
๐Ÿ’ณ
When you enable a paid marketplace, Leasey creates a billing item based on the daily rate configured for your company. Re-sending the same state is safe โ€” the API is idempotent and will not create duplicate billing entries.

9. Enum Value Reference

โš ๏ธ
Use only the exact values listed below. Sending an unlisted value (e.g. "condo", "studio", "shared room") will return a 422 validation error.

Unit Type

ValueDescription
apartmentApartment (default)
houseHouse
townhouseTownhouse
duplex/triplexDuplex or triplex
basement suiteBasement suite
room onlyRoom only
otherOther

Rental Space

ValueDescription
entire placeEntire place (default)
partial placePartial place

Rent Payment Frequency

ValueDescription
dailyDaily
weeklyWeekly
bi-weeklyBi-weekly
monthlyMonthly (default)
yearlyYearly
one-timeOne-time

Renewal Frequency

ValueDescription
month to monthMonth to month (default)
fixed termFixed term lease
fixed term, renewing month-to-monthFixed term that renews monthly

10. Response Format

Envelope

{
  "success": true,
  "report_type": "showings",
  "company_id": 123,
  "count": 45,
  "data": [ ... ]
}

Data types

TypeFormatExample
DatetimeYYYY-MM-DD HH:MM:SS2024-11-15 14:00:00
Date onlyYYYY-MM-DD2024-11-15
ISO datetime (leads)YYYY-MM-DDTHH:MM:SS2024-11-05T14:32:18
Monetary amountsfloat1850.00
Null fieldsJSON nullnull

11. Error Reference

StatusWhen it occurs
200Request processed successfully
201Property created successfully
400Missing required parameter, malformed JSON, invalid type
401Missing or invalid API token or company password
404Property not found or does not belong to your company
405Wrong HTTP method (e.g. POST on a GET-only endpoint)
422Valid JSON but field-level validation failed (e.g. invalid enum value)
500Database error or unexpected exception

Validation Error Format (422)

{
  "error": "Unprocessable Entity",
  "message": "Validation failed. See 'details' for field-level errors.",
  "details": [
    "\"name\" is required.",
    "\"unit_type\" invalid: \"studio\". Valid values: [\"apartment\", \"house\", ...]"
  ]
}

12. Code Examples

JavaScript (fetch)

javascript
const authHeaders = {
  "Authorization": `Bearer ${API_TOKEN}`,
  "X-Company-Password": COMPANY_PASSWORD,
};

// Get showings report
const params = new URLSearchParams({ company_id: COMPANY_ID, start_date: "2024-01-01T00:00:00" });
const res = await fetch(`${BASE_URL}/reports/showings?${params}`, { headers: authHeaders });
const showings = await res.json();

// Create a property
const createRes = await fetch(`${BASE_URL}/properties`, {
  method: "POST",
  headers: { ...authHeaders, "Content-Type": "application/json" },
  body: JSON.stringify({
    company_id: COMPANY_ID,
    password: COMPANY_PASSWORD,
    property: { name: "Unit 2B", address: "456 Oak Ave", city: "Toronto",
      province_state: "ON", country: "Canada", rent_amount: "2100", bedrooms: 2, bathrooms: 1 }
  }),
});
const { property_id } = await createRes.json(); // Save this!

Python

python
import requests

auth_headers = {
    "Authorization": f"Bearer {API_TOKEN}",
    "X-Company-Password": COMPANY_PASSWORD,
}

# Get leads report
response = requests.get(f"{BASE_URL}/reports/leads",
    headers=auth_headers,
    params={"company_id": COMPANY_ID, "start_date": "2024-01-01T00:00:00"})
leads = response.json()

# Create a property
response = requests.post(f"{BASE_URL}/properties",
    headers={**auth_headers, "Content-Type": "application/json"},
    json={
        "company_id": COMPANY_ID, "password": COMPANY_PASSWORD,
        "property": { "name": "Unit 2B", "address": "456 Oak Ave", "city": "Toronto",
            "province_state": "ON", "country": "Canada", "rent_amount": "2100" }
    })
property_id = response.json()["property_id"]

PHP

php
// Get properties report
$ch = curl_init($BASE_URL . "/reports/properties?company_id=" . $COMPANY_ID);
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer $API_TOKEN",
        "X-Company-Password: $COMPANY_PASSWORD",
    ],
]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);

// Create a property
$payload = json_encode(["company_id" => $COMPANY_ID, "password" => $COMPANY_PASSWORD,
    "property" => ["name" => "Unit 2B", "address" => "456 Oak Ave",
        "city" => "Toronto", "province_state" => "ON", "country" => "Canada"]]);
$ch = curl_init($BASE_URL . "/properties");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => $payload,
    CURLOPT_HTTPHEADER => ["Authorization: Bearer $API_TOKEN", "Content-Type: application/json"],
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);

13. Testing with Postman

Step 1 โ€” Get the endpoint URL

cd reports-api-service
serverless info --stage production

Look for the endpoints section. The URL will look like: https://uvciw58bnh.execute-api.ca-central-1.amazonaws.com/production

Step 2 โ€” Create an environment

VariableValue
base_urlhttps://uvciw58bnh.execute-api.ca-central-1.amazonaws.com/production
api_tokenYour API token
company_idYour company ID (integer, no quotes)
company_passwordYour company API password

Step 3 โ€” GET showings

  1. Method: GET ยท URL: {{base_url}}/reports/showings
  2. Params: company_id โ†’ {{company_id}} ยท start_date โ†’ 2024-01-01T00:00:00
  3. Headers: Authorization โ†’ Bearer {{api_token}}
  4. Headers: X-Company-Password โ†’ {{company_password}}
  5. Click Send

Step 4 โ€” POST property

  1. Method: POST ยท URL: {{base_url}}/properties
  2. Headers: Authorization โ†’ Bearer {{api_token}} ยท Content-Type โ†’ application/json
  3. Body โ†’ raw โ†’ JSON: paste your create property payload
  4. Click Send

14. Troubleshooting

401 โ€” Token length mismatch

If you see "The provided token does not match the expected token" with a debug field showing token_received_length: 32 and token_expected_length: 48, your token is being truncated. Verify you are copying the full 48-character token โ€” check for line breaks or truncation in your environment variable or config file.

401 โ€” Token not working

  • Verify Content-Type: application/json is set.
  • Ensure you are using the correct token for the right stage (dev and production tokens are different).
  • Check CloudWatch logs: serverless logs -f reportsApi --stage production --tail

401 โ€” Company password not working

Confirm the passwordapi field is set in Leasey dashboard โ†’ Settings โ†’ API Access.

400 โ€” company_id must be an integer

Send as a number: ?company_id=123 โœ…   ?company_id="123" โŒ. In Postman query params, type the value without quotes.

422 โ€” Invalid enum value

You sent a value not in the allowed enum list. Common mistakes: "condo", "studio", "loft", "shared room", "private room", "semi-monthly" โ€” none of these exist. Check the Enum Values section for the exact accepted values.

CORS errors in browser

The API allows all origins. CORS errors are almost always caused by a missing Authorization header triggering a 401 before CORS preflight headers are sent. Check auth headers first.

500 โ€” Internal Server Error

Check CloudWatch logs for the specific stack trace: serverless logs -f reportsApi --stage production --tail. Common causes: database connection timeout (Lambda cold start), invalid SQL from a malformed date parameter.


15. Best Practices

๐Ÿ”’ Security

  • Never expose credentials in client-side code
  • Use X-Company-Password header on GET requests
  • Store token in environment variables
  • Rotate your company password periodically

โšก Performance

  • Use date filters to limit large reports
  • Cache properties report for 5โ€“15 minutes
  • Design with a 60-second timeout on the client

๐Ÿ”„ Reliability

  • Exponential backoff for 5xx (2s, 4s, 8s)
  • Handle null fields โ€” many are nullable
  • Store property_id immediately after creation

๐Ÿ“‹ Data Consistency

  • Photos, features, restrictions replace on update
  • team_members on PUT is additive only
  • Dates must be ISO-8601: YYYY-MM-DDTHH:MM:SS

Support

๐Ÿ“ง
Email: helpdesk@leasey.ai  ยท  Response time: Within 24 business hours

When contacting support include your company_id, the full request (credentials removed), the exact error message, and the time it occurred (UTC).

Leasey External API v2.0 โ€” ยฉ 2024 Lease.ai. All rights reserved.