Introduction
Welcome to the HOALife API Documentation. HOALife's API is intended to be consumed by programmers to consume or generate data with the HOALife system.
While this documentation is comprehensive, access to some endpoints may not be included in all Customer Agreements. Contact sales@hoalife.com to learn more.
The API provides scoped Read, Write, Update, and Destroy actions for most resources.
JSON is the only currently supported response format, following the JSON:API spec. All responses will have the application/vnd.api+json content type. Any POST or PUT requests must specify the application/vnd.api+json Content-Type header.
API Clients
This API is completely accessible without an API Client, however, we do provide some API Clients to allow you to leverage this API without worrying about implementation details.
Ruby
The hoalife Ruby gem will seamlessly take care of this API's authentication, signature verification, rate limiting, pagination, and error handling.
$ gem install hoalife
View the README for usage details and examples.
Authentication
HOALife supports two kinds of API credentials:
- Service Account API Keys (
sk_...) are created for an account and are intended for automated integrations that run without a user's involvement. They do not have a fixed expiration. - User Access API Keys (
cli_...) are issued to a signed-in user through the OAuth flow below. Each key is scoped to one selected account and the access level the user approved.
Both credential types can use the Authorization: Token ... header. Service Account API Keys also support the Basic Authentication and URL parameter methods documented below. User Access API Keys require the Token header.
Service Account API Keys via HTTP Header
To authorize, use this code:
# With shell, you can just pass the correct header with each request
curl https://api.hoalife.com/api/ \
-H "Authorization: Token sk_SUPERSECRET"
Make sure to replace
sk_SUPERSECRETwith your API key.
HOALife expects for the API key to be included in all API requests to the server in a header that looks like the following:
Authorization: Token sk_SUPERSECRET
User Access API Keys (OAuth)
User Access API Keys are issued through OAuth Authorization Code with PKCE. The HOALife CLI is a public client: it does not have a client secret. It starts the flow over HTTPS, opens the returned authorization URL in a browser, and listens on a local loopback callback for the authorization code. The resulting credential belongs to the user, is limited to the selected account and approved access level, and expires unless refreshed.
1. Create an authorization request
Generate a cryptographically random state and PKCE code_verifier. Use an unpadded Base64URL-encoded verifier between 43 and 128 characters long. Calculate the S256 challenge as the unpadded Base64URL encoding of SHA256(code_verifier). Keep the state and verifier in the CLI process; do not put either in the browser URL.
Choose an available port and use this exact loopback callback format for both requests:
http://127.0.0.1:{port}/oauth/callback
For example, request an authorization URL with cURL:
curl --fail-with-body https://api.hoalife.com/oauth/authorization-requests \
-H "Content-Type: application/json" \
-d '{"client_id":"hoalife-cli","redirect_uri":"http://127.0.0.1:49152/oauth/callback","state":"STATE_FROM_CLI","code_challenge":"S256_CHALLENGE_FROM_CLI"}'
The response contains an authorization_url. Open it in the user's browser. The user signs in if needed, selects an account if they have access to more than one eligible account, and approves the requested access. When CLI access is enabled on a root account, eligible leaf accounts beneath it can be selected. A user with one eligible account skips account selection. Account eligibility depends on the user's highest eligible membership role; architectural committee members and voters cannot authorize CLI access when they are the highest eligible role. Account administrators can grant read-only, read/write, or read/write/delete access; other eligible membership roles can grant read-only access only.
2. Receive the authorization response
After approval, HOALife redirects the browser to the loopback callback with a short-lived authorization code and the original state. The CLI must compare the returned state with the value it generated before proceeding. If the user cancels, the callback contains error=access_denied and the state instead of a code. The CLI should handle that response and close its local listener.
Authorization codes expire after five minutes and can be exchanged only once. The authorization request itself expires after ten minutes if it is not completed.
3. Exchange the code for a credential
Send the code, original redirect URI, and verifier to the token endpoint:
curl --fail-with-body https://api.hoalife.com/oauth/token \
-H "Content-Type: application/json" \
-d '{"client_id":"hoalife-cli","redirect_uri":"http://127.0.0.1:49152/oauth/callback","code":"CODE_FROM_CALLBACK","code_verifier":"VERIFIER_FROM_CLI"}'
The response contains an access_token, a refresh_token, a signing_secret, and token_type of Token. The access token is a User Access API Key. The signing secret is used to verify the API's X-Signature HMAC header. The credential is associated with the user and selected account, with the membership recorded when access came through one. API requests are scoped to the selected account. Its permissions are limited to the selected grant: read grants allow read actions; write grants allow read, create, and update actions; delete grants additionally allow destroy actions. These values are returned only once, so store them securely and never log them.
The access token expires in 24 hours (expires_in). A refresh token expires if it is not used within seven days (refresh_token_expires_in); each successful refresh rotates it and starts a new seven-day period. The grant has a one-year absolute lifetime (grant_expires_in). Refresh the pair by sending the current refresh token and client ID:
curl --fail-with-body https://api.hoalife.com/oauth/token \
-H "Content-Type: application/json" \
-d '{"grant_type":"refresh_token","client_id":"hoalife-cli","refresh_token":"REFRESH_TOKEN_FROM_PREVIOUS_RESPONSE"}'
The response contains a replacement access token, refresh token, and the same signing secret. Store the replacement tokens securely, discard the previous pair, and serialize refresh attempts. Refresh tokens are single-use; reusing a consumed token revokes the grant. If a refresh token has expired or the grant has reached its one-year lifetime, complete browser authorization again.
Requests to /api/info and /api/schema do not count against an API credential's request limit.
After the token exchange succeeds, the CLI may redirect the browser to /authorization-issued.html on the same HOALife host. This static page confirms authorization is complete and asks the user to return to the CLI; it does not receive the authorization code or access token.
4. Make API requests
Send the credential in the Authorization header:
curl https://api.hoalife.com/api/accounts \
-H "Authorization: Token cli_SUPERSECRET"
User Access API Keys are accepted with the Token header. They are not accepted with HTTP Basic authentication or the api_key URL parameter. Service Account API Keys can use the authentication methods in the section above.
To check which account and permissions a credential represents, request /api/info with the same header. The response includes the account ID and name, the credential type, its permissions, and (for User Access API Keys) its access level and access_token_expires_at. The API currently returns credential_type: "cli" for User Access API Keys and credential_type: "service_account" for Service Account API Keys.
curl https://api.hoalife.com/api/info \
-H "Authorization: Token cli_SUPERSECRET"
Revoke access
To revoke a grant, send the current refresh token and client ID to POST /oauth/revoke:
curl --fail-with-body https://api.hoalife.com/oauth/revoke \
-H "Content-Type: application/json" \
-d '{"client_id":"hoalife-cli","token":"REFRESH_TOKEN"}'
The endpoint returns success for unknown tokens and tokens belonging to another client. Revoking the refresh token invalidates the entire grant, including the current access token, even when that access token has expired. DELETE /api/logout remains available when a valid access token is present. A User Access API Key stops working if the user no longer has the access required by its grant. The CLI should discard its local copy when the user signs out or revokes access.
User Access API Keys do not grant webhook access. Webhooks are reserved for Service Account API Keys and cannot be created or managed with OAuth-issued credentials.
Service Account API Keys via Basic Authentication
To authorize, use this code:
# cURL will encode the username and password in the header for you. YMMV with other libraries.
curl https://sk_SUPERSECRET:sk_SUPERSECRET@api.hoalife.com/api/
Make sure to replace
sk_SUPERSECRETwith your API key.
When using Basic Authentication, enter your API key for both the username and password. cURL will Base64 and encode these for you, sending the Authorization header as follows:
Authorization: Basic c2tfU1VQRVJTRUNSRVQ6c2tfU1VQRVJTRUNSRVQ=
Service Account API Keys via URL Parameter
To authorize, use this code:
# With shell, you can just pass the correct header with each request
curl https://api.hoalife.com/api/?api_key=sk_SUPERSECRET
Make sure to replace
sk_SUPERSECRETwith your API key.
Passing your API key via URL parameter is also possible. This is helpful when testing requests via a web browser.
Rate Limit
To ensure a consistent and reliable service, this API limits the number of requests per API key within a given time frame. The default limit is 5 requests per second; requests to /api/info and /api/schema are excluded. The rate limit is enforced as a GCRA "Leaky Bucket". Higher volume rate limits are available per the Customer Agreement. Contact sales@hoalife.com to learn more.
Requests subject to rate limits include HTTP headers to help you stay within the allowance. Exceeding these limits will result in a 429. Continued requests after a 429 may result in requests from your IP being blocked.
| HTTP Header Name | Description |
|---|---|
| X-RateLimit-Limit | The maximum number of requests per period |
| X-RateLimit-Period | The period, in seconds, that the limit is enforced |
| X-RateLimit-Used | The number of consumed requests within the period |
| X-RateLimit-Remaining | The number of remaining requests within the period |
Accommodating the rate limit could be as simple as this pseudo-code sleep 2sec if X-RateLimit-Remaining < 1.
API Schema
Retrieve the machine-readable attribute contracts with an authenticated request:
curl https://api.hoalife.com/api/schema \
-H "Authorization: Token sk_SUPERSECRET"
HTTP Request
GET https://api.hoalife.com/api/schema
Service Account API Keys and active User Access API Keys can retrieve the schema.
No resource permissions are required. Missing, invalid, expired, or revoked
credentials receive 401 Unauthorized. Requests are exempt from credential rate
limits and usage counting, including when the credential has exhausted its
allowance. Successful responses use application/vnd.api+json and include the
usual X-Signature header.
Response
The response has data.id: "v1", data.type: "api_schema", and these attributes:
| Attribute | Meaning |
|---|---|
api_version |
API version, currently 1. |
schema_version |
Schema response format version, currently 1. |
resources |
Resource names mapped to their available read, create, and update attribute schemas. |
Each operation is a JSON Schema Draft 2020-12
object with $schema, type, properties, and required. It describes the
attributes inside data.attributes, including nested arrays and objects. For
example, data.attributes.resources.acc_request_votes.create lists request_id
and user_id as required and limits status to "requested", "", or null.
The read schema instead describes the returned vote and its possible statuses.
Nullable fields include "null" in their type. Conditional response fields have
x-conditional: true; conditional input requirements are explained in field
descriptions. Defaults are included only for creation.
The catalog is the same for every valid credential and contains the public
serializer contracts used by this reference. Resource names match documentation
sections, rather than endpoint paths. It does not describe the complete JSON:API
envelope, relationship inputs, special actions, or permissions. Use the endpoint
sections for those details and /api/info for your credential's permissions.
Verify Authenticity
Paranoid? No problem. All responses provided by this API are signed via HMAC SHA256.
A signature is created by generating an HMAC of the raw response body with a SHA256 digest, using the shared Signing Secret. You'll find this signature in the HTTP header X-Signature.
def verified?
digest = OpenSSL::Digest.new("sha256")
OpenSSL::HMAC.hexdigest(digest, "ss_SUPERSECRET", response.body) == response.headers["X-Signature"]
end
Make sure to replace
ss_SUPERSECRETwith your provided Signing Secret.
Querying, Ordering, and Pagination
Querying
Most attributes of a resource are query-able so that you can filter resource collections based on an attribute. Include the attribute as a query parameter in the URL external_id=EXT42.
By default, exact matching will be used. If you'd like to craft an approximate query, you can specify the operator as part of the parameter value.
| Operator | Example | SQL equivalent |
|---|---|---|
| default | foo=4 |
foo = 4 |
gt |
foo=gt,4 |
foo > 4 |
gte |
foo=gte,4 |
foo >= 4 |
lt |
foo=lt,4 |
foo < 4 |
lte |
foo=lte,4 |
foo <= 4 |
ilike |
bar=ilike,%25BAZ%25 |
bar ILIKE %BAZ% |
!= |
foo=!=,4 |
foo != 4 |
Ordering
You can specify the order of a resource collection by passing the order URL query parameter order=foo.
By default, the resource will be ordered by the created_at attribute in ascending order. You can specify the direction of the order clause by specifying asc or desc as part of the parameter value.
| Operator | Example | SQL equivalent |
|---|---|---|
| default | order=foo |
ORDER BY foo ASC |
asc |
order=foo&order_dir=asc |
ORDER BY foo ASC |
desc |
order=foo&order_dir=desc |
ORDER BY foo DESC |
Pagination
Recourse collections are paginated, with 25 resources per page. Traversing pages can be done via the page=X URL query parameter.
There are a number of helpful attributes in a collection response to help you traverse pages. If you have specified order or query attributes, they will be reflected in the pagination links.
"meta": {
"total": 167,
"total_pages": 7
},
"links": {
"self": "/api/accounts/1/properties?page=1",
"next": "/api/accounts/1/properties?page=2",
"prev": "/api/accounts/1/properties"
}
Accounts
Accounts represent both Associations which are managed, and Management companies.
Get All Accounts
curl "https://api.hoalife.com/api/accounts" \
-H "Authorization: Token sk_SUPERSECRET"
The above command returns JSON structured like this:
{
"data": [
{
"id": "1",
"type": "account",
"attributes": {
"id": 1,
"parent_id": null,
"name": "XYZ HOA Management",
"repeat_violation_strategy": "six_month_rolling_window",
"escalation_threshold": 3,
"violation_lookback_period": "two_months",
"mailing_street_1": "123 HOALife Way",
"mailing_street_2": null,
"mailing_city": "Sun Valley",
"mailing_state": "ID",
"mailing_postal_code": "83313",
"organizational": true,
"external_id": null,
"created_at": "2019-07-30T21:16:44.624Z",
"updated_at": "2019-08-01T14:49:17.016Z"
},
"relationships": {}
},
{
"id": "2",
"type": "account",
"attributes": {
"id": 2,
"parent_id": 1,
"name": "Goodview HOA",
"repeat_violation_strategy": "six_month_rolling_window",
"escalation_threshold": null,
"violation_lookback_period": null,
"mailing_street_1": "123 Goodview Dr",
"mailing_street_2": null,
"mailing_city": "Ketchum",
"mailing_state": "ID",
"mailing_postal_code": "83340",
"organizational": false,
"external_id": null,
"created_at": "2019-07-30T21:16:44.640Z",
"updated_at": "2019-07-31T15:44:01.644Z"
},
"relationships": {}
},
{
"id": "3",
"type": "account",
"attributes": {
"id": 3,
"parent_id": 1,
"name": "Sleepyville HOA",
"repeat_violation_strategy": "six_month_rolling_window",
"escalation_threshold": null,
"violation_lookback_period": null,
"mailing_street_1": "123 Sleepy Ct",
"mailing_street_2": null,
"mailing_city": "Sun Valley",
"mailing_state": "ID",
"mailing_postal_code": "83353",
"organizational": false,
"external_id": null,
"created_at": "2019-08-01T14:37:15.287Z",
"updated_at": "2019-08-01T14:37:15.287Z"
},
"relationships": {}
}
],
"meta": {
"current_page": 1,
"total": 3,
"total_pages": 1
},
"links": {
"self": "/api/accounts?page=1",
"next": "/api/accounts",
"prev": "/api/accounts"
}
}
This endpoint retrieves all Accounts accessible by the API key.
HTTP Request
GET https://api.hoalife.com/api/accounts
Attributes
| Attribute | Type | Presence | Nullable | Description |
|---|---|---|---|---|
id |
Integer | Always | No | Unique identifier of an Account |
name |
String | Always | No | Name of an Account |
organizational |
Boolean | Always | Yes | Indicates if the Account is organizational (Management Company) or not (Association) |
created_at |
String | Always | No | Date and time the record was created. Format: date-time. |
updated_at |
String | Always | No | Date and time the record was last updated. Format: date-time. |
repeat_violation_strategy |
String | Always | Yes | Strategy for calculating repeat Violations Allowed values: "zero", "consecutive", "one_month_rolling_window", "two_month_rolling_window", "three_month_rolling_window", "four_month_rolling_window", "five_month_rolling_window", "six_month_rolling_window", "seven_month_rolling_window", "eight_month_rolling_window", "nine_month_rolling_window", "ten_month_rolling_window", "eleven_month_rolling_window", "one_year_rolling_window", "eighteen_month_rolling_window", "two_year_rolling_window", "one_hundred_year_rolling_window", "closing", "calendar_year". |
external_id |
String | Always | Yes | A unique identifier from an external system used to map the record |
parent_id |
Integer | Always | Yes | Unique identifier of parent organizational Account Create accepts this ID when data.relationships.parent is omitted; the controller resolves the parent. |
parent_external_id |
String | Always | Yes | External identifier of the parent account. |
violation_lookback_period |
Integer | Always | Yes | Number of days to warn about prior Violations Allowed values: 0, 1, 30, 60, 90, 120, 150, 180, 270, 365, 730, 130000. |
escalation_threshold |
Integer | Always | Yes | Number of repeat Violations which trigger the creation of an Escalation |
mailing_street_2 |
String | Always | Yes | Mailing street address used on violation notices Returns the effective mailing setting, including inherited values. |
mailing_name |
String | Always | Yes | Mailing recipient name. Returns the effective mailing setting, including inherited values. |
mailing_street_1 |
String | Always | Yes | Mailing street address used on violation notices Returns the effective mailing setting, including inherited values. |
mailing_city |
String | Always | Yes | Mailing city address used on violation notices Returns the effective mailing setting, including inherited values. |
mailing_state |
String | Always | Yes | Mailing state address used on violation notices Returns the effective mailing setting, including inherited values. |
mailing_postal_code |
String | Always | Yes | Mailing postal code address used on violation notices Returns the effective mailing setting, including inherited values. |
Nested presence applies when its parent object is present.
Query Parameters
| Parameter | Query By | Order By |
|---|---|---|
| id | true | true |
| parent_id | false | false |
| name | true | true |
| escalation_threshold | true | true |
| mailing_street_1 | true | true |
| mailing_street_2 | true | true |
| mailing_city | true | true |
| mailing_state | true | true |
| mailing_postal_code | true | true |
| repeat_violation_strategy | false | false |
| violation_lookback_period | false | false |
| external_id | true | false |
| organizational | false | false |
Create Account
curl "https://api.hoalife.com/api/accounts" \
-X POST \
-H "Authorization: Token sk_SUPERSECRET" \
-H "Content-Type: application/vnd.api+json" \
-d '
{
"data": {
"type": "account",
"attributes": {
"name": "Foo Bar22",
"external_id": "123",
"organizational": false
},
"relationships": {
"parent": {
"data": {
"type": "account",
"id": 5
}
}
}
}
}
'
The above command returns JSON structured like this:
{
"data": {
"id": "731",
"type": "account",
"attributes": {
"id": 42,
"name": "Foo Bar",
"organizational": false,
"created_at": "2019-08-12T23:53:04.537Z",
"updated_at": "2019-08-12T23:53:04.537Z",
"repeat_violation_strategy": "six_month_rolling_window",
"external_id": "123",
"parent_id": 5,
"violation_lookback_period": 0,
"escalation_threshold": null,
"mailing_street_2": null,
"mailing_street_1": null,
"mailing_city": null,
"mailing_state": null,
"mailing_postal_code": null
},
"relationships": {}
}
}
This endpoint creates a new Account.
HTTP Request
POST https://api.hoalife.com/api/accounts
Account Writable Attributes
| Attribute | Type | Required | Nullable | Description |
|---|---|---|---|---|
name |
String | Yes | No | Name of an Account |
organizational |
Boolean | No | Yes | Indicates if the Account is organizational (Management Company) or not (Association) |
repeat_violation_strategy |
String or Integer | No | Yes | Strategy for calculating repeat Violations Allowed values: "zero", "consecutive", "one_month_rolling_window", "two_month_rolling_window", "three_month_rolling_window", "four_month_rolling_window", "five_month_rolling_window", "six_month_rolling_window", "seven_month_rolling_window", "eight_month_rolling_window", "nine_month_rolling_window", "ten_month_rolling_window", "eleven_month_rolling_window", "one_year_rolling_window", "eighteen_month_rolling_window", "two_year_rolling_window", "one_hundred_year_rolling_window", "closing", "calendar_year", -1, 0, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 1100, 1200, 1800, 2400, 120000, 130000, 140000. |
external_id |
String | No | Yes | A unique identifier from an external system used to map the record |
skip_onboarding |
Boolean | No | No | Skip account onboarding processing. |
parent_id |
String or Integer | No | No | Unique identifier of parent organizational Account Create accepts this ID when data.relationships.parent is omitted; the controller resolves the parent. |
violation_lookback_period |
String or Integer | No | Yes | Number of days to warn about prior Violations Allowed values: "last_inspection", "since_last_inspection", "one_month", "two_months", "three_months", "four_months", "five_months", "six_months", "nine_months", "one_year", "two_year", "until_closed", 0, 1, 30, 60, 90, 120, 150, 180, 270, 365, 730, 130000. |
escalation_threshold |
String or Integer | No | Yes | Number of repeat Violations which trigger the creation of an Escalation |
mailing_street_2 |
String | No | Yes | Mailing street address used on violation notices Returns the effective mailing setting, including inherited values. |
mailing_name |
String | No | Yes | Mailing recipient name. Returns the effective mailing setting, including inherited values. |
mailing_street_1 |
String | No | Yes | Mailing street address used on violation notices Returns the effective mailing setting, including inherited values. |
mailing_city |
String | No | Yes | Mailing city address used on violation notices Returns the effective mailing setting, including inherited values. |
mailing_state |
String | No | Yes | Mailing state address used on violation notices Returns the effective mailing setting, including inherited values. |
mailing_postal_code |
String | No | Yes | Mailing postal code address used on violation notices Returns the effective mailing setting, including inherited values. |
Required refers to unconditional presence. See descriptions for conditional requirements. Nested requirements apply when the parent object is supplied.
Update Account
curl "https://api.hoalife.com/api/accounts/42" \
-X PUT \
-H "Authorization: Token sk_SUPERSECRET" \
-H "Content-Type: application/vnd.api+json" \
-d '
{
"data": {
"type": "account",
"attributes": {
"name": "Foo Bar22"
}
}
}
'
The above command will return updated JSON, just like for the Create action with a successful status code of
201 CREATED.
This endpoint updates an existing Account.
HTTP Request
PUT https://api.hoalife.com/api/accounts/:account_id
URL Parameters
| Parameter | Type | Description |
|---|---|---|
| :account_id | Integer | Unique identifier of the Account the request is being made for |
Editable Attributes
| Attribute | Type | Required | Nullable | Description |
|---|---|---|---|---|
name |
String | No | No | Name of an Account |
organizational |
Boolean | No | Yes | Indicates if the Account is organizational (Management Company) or not (Association) |
repeat_violation_strategy |
String or Integer | No | Yes | Strategy for calculating repeat Violations Allowed values: "zero", "consecutive", "one_month_rolling_window", "two_month_rolling_window", "three_month_rolling_window", "four_month_rolling_window", "five_month_rolling_window", "six_month_rolling_window", "seven_month_rolling_window", "eight_month_rolling_window", "nine_month_rolling_window", "ten_month_rolling_window", "eleven_month_rolling_window", "one_year_rolling_window", "eighteen_month_rolling_window", "two_year_rolling_window", "one_hundred_year_rolling_window", "closing", "calendar_year", -1, 0, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 1100, 1200, 1800, 2400, 120000, 130000, 140000. |
external_id |
String | No | Yes | A unique identifier from an external system used to map the record |
skip_onboarding |
Boolean | No | No | Skip account onboarding processing. |
violation_lookback_period |
String or Integer | No | Yes | Number of days to warn about prior Violations Allowed values: "last_inspection", "since_last_inspection", "one_month", "two_months", "three_months", "four_months", "five_months", "six_months", "nine_months", "one_year", "two_year", "until_closed", 0, 1, 30, 60, 90, 120, 150, 180, 270, 365, 730, 130000. |
escalation_threshold |
String or Integer | No | Yes | Number of repeat Violations which trigger the creation of an Escalation |
mailing_street_2 |
String | No | Yes | Mailing street address used on violation notices Returns the effective mailing setting, including inherited values. |
mailing_name |
String | No | Yes | Mailing recipient name. Returns the effective mailing setting, including inherited values. |
mailing_street_1 |
String | No | Yes | Mailing street address used on violation notices Returns the effective mailing setting, including inherited values. |
mailing_city |
String | No | Yes | Mailing city address used on violation notices Returns the effective mailing setting, including inherited values. |
mailing_state |
String | No | Yes | Mailing state address used on violation notices Returns the effective mailing setting, including inherited values. |
mailing_postal_code |
String | No | Yes | Mailing postal code address used on violation notices Returns the effective mailing setting, including inherited values. |
Required refers to unconditional presence. See descriptions for conditional requirements. Nested requirements apply when the parent object is supplied.
Destroy Account
curl "https://api.hoalife.com/api/accounts/42" \
-X DELETE \
-H "Authorization: Token sk_SUPERSECRET" \
-H "Content-Type: application/vnd.api+json"
The above command will return an empty response successful response with a successful status code of
202 Accepted.
This endpoint destroys an existing Account. Because Account objects have many dependent resources, the actual deletion of the data is performed in a background process. This means that the API may continue to return data for the Account even after you've requested a it be deleted.
HTTP Request
DELETE https://api.hoalife.com/api/accounts/:account_id
URL Parameters
| Parameter | Type | Description |
|---|---|---|
| :account_id | Integer | Unique identifier of the Account the request is being made for |
Invoices
Invoices represent finalized charges associated with properties.
Get All Invoices for an Account
curl "https://api.hoalife.com/api/accounts/123/invoices" \
-H "Authorization: Token sk_SUPERSECRET"
The above command returns JSON structured like this:
{
"data": [
{
"id": "987",
"type": "invoice",
"attributes": {
"id": 987,
"account_id": 123,
"property_id": 456,
"status": "open",
"transaction_date": "2026-09-01",
"due_date": "2026-10-01",
"document_number": "INV-987",
"total_cents": 12500,
"balance_cents": 12500,
"created_at": "2026-09-01T12:00:00.000Z",
"updated_at": "2026-09-01T12:00:00.000Z"
},
"relationships": {}
}
],
"meta": {
"current_page": 1,
"total": 1,
"total_pages": 1
},
"links": {
"self": "/api/accounts/123/invoices?page=1",
"next": "/api/accounts/123/invoices",
"prev": "/api/accounts/123/invoices"
}
}
Returns finalized invoices synced with the accounting provider for the account identified in the path. Invoices without an external provider ID have not synced back yet and are excluded, even if they are finalized locally. If no finalized, synced invoices are available, the endpoint returns an empty collection. The account must have an active accounting integration. The API key must have read permission for invoices, and the path account must be within the API key's account scope.
If no accounting integration is available to the account, the API returns 409 Conflict:
{
"data": {
"id": "556629c9-c2ad-494b-82d4-53d9639f7174",
"type": "error",
"attributes": {
"id": "556629c9-c2ad-494b-82d4-53d9639f7174",
"title": "Accounting integration required",
"status": 409,
"detail": "Invoice listing requires an accounting integration for this account"
}
}
}
HTTP Request
GET https://api.hoalife.com/api/accounts/:account_id/invoices
URL Parameters
| Parameter | Type | Description |
|---|---|---|
| :account_id | Integer | Unique identifier of an Account within the API key's scope |
Attributes
| Attribute | Type | Presence | Nullable | Description |
|---|---|---|---|---|
id |
Integer | Always | No | Unique identifier of the Invoice |
account_id |
Integer | Always | Yes | Unique identifier of the Account the invoice belongs to |
property_id |
Integer | Always | No | Property to invoice. It must belong to the account in the request path and have an external customer ID in the accounting integration (a QuickBooks customer ID for QuickBooks Online). |
transaction_date |
String | Always | Yes | Invoice transaction date in ISO 8601 date format (YYYY-MM-DD) Format: date. |
due_date |
String | Always | Yes | Invoice due date in ISO 8601 date format (YYYY-MM-DD) Format: date. |
created_at |
String | Always | No | Date and time the invoice record was created Format: date-time. |
updated_at |
String | Always | No | Date and time the invoice record was last updated Format: date-time. |
total_cents |
Integer | Always | No | Total amount of the invoice in cents |
balance_cents |
Integer | Always | No | Remaining amount due in cents |
status |
String | Always | No | Provider-neutral invoice status: open for generated or sent invoices, or paid for paid invoices Allowed values: "paid", "open". |
document_number |
String | Always | Yes | Invoice's document number |
Nested presence applies when its parent object is present.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
| property_id | Integer | Return invoices for this property. The property must belong to the account in the path. |
| page | Integer | Page number to return. Collections contain 25 invoices per page. |
The response uses the standard collection pagination metadata and links. Follow the links.next URL to retrieve the next page. An account outside the API key's scope or a property that does not belong to the path account is not accessible through this endpoint.
Create Invoice
Create an invoice for a property in the account identified by account_id. The invoice is created asynchronously by the account's accounting integration.
curl "https://api.hoalife.com/api/accounts/123/invoices" \
-X POST \
-H "Authorization: Token sk_SUPERSECRET" \
-H "Content-Type: application/vnd.api+json" \
-d '
{
"data": {
"type": "invoice",
"attributes": {
"property_id": 456,
"description": "Monthly assessment",
"quantity": 1,
"amount_cents": 12500,
"item_type_id": "accounting-item-789",
"transaction_date": "2025-01-15",
"due_date": "2025-02-15",
"private_note": "Created through the API",
"customer_memo": "Thank you"
}
}
}
'
The API queues invoice creation with the accounting integration and returns immediately. No invoice resource is included in the response. Refresh the account's invoice list to find the invoice after creation completes. The API does not provide a job-status endpoint.
HTTP Request
POST https://api.hoalife.com/api/accounts/:account_id/invoices
Writable Attributes
| Attribute | Type | Required | Nullable | Description |
|---|---|---|---|---|
property_id |
String or Integer | Yes | No | Property to invoice. It must belong to the account in the request path and have an external customer ID in the accounting integration (a QuickBooks customer ID for QuickBooks Online). |
transaction_date |
String | Yes | No | Invoice transaction date in ISO 8601 date format (YYYY-MM-DD) Format: date. |
due_date |
String | Yes | No | Invoice due date in ISO 8601 date format (YYYY-MM-DD) Format: date. |
description |
String | Yes | No | Description of the invoice line item |
quantity |
Integer or String | No | No | Positive quantity for the invoice line item Default: 1. |
amount_cents |
Integer or String | Yes | No | Positive unit amount for the invoice line item, in cents (for example, 12500 represents $125.00) |
item_type_id |
String or Integer | Yes | No | Opaque identifier for a valid invoice item type returned by the account's invoice item types endpoint |
private_note |
String | No | Yes | Note visible to the account but not the customer |
customer_memo |
String | No | Yes | Memo visible to the customer |
Required refers to unconditional presence. See descriptions for conditional requirements. Nested requirements apply when the parent object is supplied.
The API key must have create permission for invoices. The :account_id must be within the API key's account scope, and property_id must identify a property belonging to that account. The property must also be linked to a customer in the accounting integration through its external customer ID; for QuickBooks Online, this is the QuickBooks customer ID. item_type_id is an opaque string; clients should obtain valid values from GET /api/accounts/:account_id/invoice_item_types rather than infer provider-specific identifiers.
Email delivery is not part of invoice creation through this endpoint.
Responses
202 Accepted
The invoice creation request was accepted for asynchronous processing. The response body is empty. The created invoice becomes available through GET /api/accounts/:account_id/invoices after processing completes.
400 Bad Request
The request is invalid. Required fields must be present; quantity and amount_cents must be positive, and amount_cents must be an integer expressed in cents; the dates must be valid ISO 8601 dates; and the property must have an external customer ID configured for the accounting integration. Validation failures use the standard API error envelope described in Errors.
{
"data": {
"id": "error-id",
"type": "error",
"attributes": {
"id": "error-id",
"title": "Invalid data",
"status": 400,
"detail": {
"amount_cents": ["must be an integer"],
"due_date": ["is invalid"],
"property_id": ["is not linked to an accounting customer"]
}
}
}
}
409 Conflict
The account or property does not have an accounting integration available for invoice creation. The response uses the standard API error envelope described in Errors.
{
"data": {
"id": "error-id",
"type": "error",
"attributes": {
"id": "error-id",
"title": "Accounting integration unavailable",
"status": 409,
"detail": "Invoice creation requires an accounting integration for this account."
}
}
}
Invoice Item Types
Invoice item types identify the products or services available when creating an invoice. The API returns the item types supplied by the invoice provider available to the requested Account. The resource is provider-neutral; its id is an opaque string and should be passed back unchanged when creating an invoice.
Get Invoice Item Types
curl "https://api.hoalife.com/api/accounts/2/invoice_item_types" \
-H "Authorization: Token sk_SUPERSECRET"
The above command returns JSON structured like this:
{
"data": [
{
"id": "7",
"type": "invoice_item_type",
"attributes": {
"id": "7",
"name": "Management Fee",
"fully_qualified_name": "Services:Management Fee",
"unit_price_cents": 12500
},
"relationships": {}
},
{
"id": "18",
"type": "invoice_item_type",
"attributes": {
"id": "18",
"name": "Late Fee",
"fully_qualified_name": "Fees:Late Fee",
"unit_price_cents": 2500
},
"relationships": {}
}
]
}
Returns invoice item types available from the invoice provider for the requested Account. The Account must be within the API key's scope. The API key must have read permission for invoices.
If no accounting integration is available to the Account, the API returns 409 Conflict:
{
"data": {
"id": "556629c9-c2ad-494b-82d4-53d9639f7174",
"type": "error",
"attributes": {
"id": "556629c9-c2ad-494b-82d4-53d9639f7174",
"title": "Accounting integration required",
"status": 409,
"detail": "No accounting integration is available for this account"
}
}
}
HTTP Request
GET https://api.hoalife.com/api/accounts/:account_id/invoice_item_types
Attributes
| Attribute | Type | Presence | Nullable | Description |
|---|---|---|---|---|
id |
String | Always | No | Opaque identifier for the invoice provider's item type. Pass it unchanged when creating an invoice. |
name |
String | Always | Yes | Item type name. |
fully_qualified_name |
String | Always | Yes | Item type name including its parent categories, when provided by the invoice provider. |
unit_price_cents |
Integer | Always | Yes | Default unit price supplied by the invoice provider, expressed as a number of cents. |
Nested presence applies when its parent object is present.
Users
Users with roles on the credential's Account, its descendants, or its ancestors
that provide inherited access.
Get All Users
curl "https://api.hoalife.com/api/users" \
-H "Authorization: Token sk_SUPERSECRET"
The above command returns JSON structured like this:
{
"data": [
{
"id": "1",
"type": "user",
"attributes": {
"id": 1,
"email": "admin@example.com",
"first_name": "Bob",
"last_name": "Admin",
"timezone": "Mountain Time (US & Canada)",
"authorizations": [
{
"id": 444,
"account_id": 1,
"role": "administrator"
}
]
}
},
{
"id": "2",
"type": "user",
"attributes": {
"id": 2,
"email": "sub-account-manager@example.com",
"first_name": "Jane",
"last_name": "SubAccountManger",
"timezone": "Pacific Time (US & Canada)",
"authorizations": [
{
"id": 555,
"account_id": 2,
"role": "manager"
},
{
"id": 556,
"account_id": 3,
"role": "manager"
}
]
}
},
{
"id": "3",
"type": "user",
"attributes": {
"id": 3,
"email": "inspector@example.com",
"first_name": "Sally",
"last_name": "Inspector",
"timezone": "Eastern Time (US & Canada)",
"authorizations": [
{
"id": 777,
"account_id": 4,
"role": "inspector"
}
]
}
}
],
"meta": {
"current_page": 1,
"total": 3,
"total_pages": 1
},
"links": {
"self": "/api/users?page=1",
"next": "/api/users",
"prev": "/api/users"
}
}
This endpoint retrieves all Users accessible by the credential, including users
whose roles are inherited from parent or other ancestor accounts. Each user is
returned once, with authorizations for the credential's account, its descendants,
and its ancestors. Each authorization retains the ID of the account where the
role was granted. Roles only on sibling or unrelated accounts are excluded.
Filter Users and their returned authorizations by the account where a role was
granted using the account_id query parameter, or by role using role. For
example, filtering by a parent account's ID returns users with roles granted on
that parent; filtering by a child account's ID selects roles granted directly
on that child.
HTTP Request
GET https://api.hoalife.com/api/users
Attributes
| Attribute | Type | Presence | Nullable | Description |
|---|---|---|---|---|
id |
Integer | Always | No | Unique identifier of the User |
email |
String | Always | No | Email address of the User |
first_name |
String | Always | No | User's first name |
last_name |
String | Always | No | User's last name |
timezone |
String | Always | No | String representing a user's timezone |
authorizations |
Array[Object] | Conditional | No | Roles granted on the credential's account, descendant accounts, and ancestor accounts providing inherited access. Each authorization identifies the account where the role was granted. Filtered by account_id and role when supplied. |
authorizations[] |
Object | Each item | No | — |
authorizations[].id |
Integer | Always | No | — |
authorizations[].role |
String | Always | No | Allowed values: "administrator", "manager", "staff", "inspector", "board_member", "read_only", "architectural_committee_member", "architectural_committee_voter". |
authorizations[].account_id |
Integer | Always | No | — |
Nested presence applies when its parent object is present.
Query Parameters
| Parameter | Query By | Order By |
|---|---|---|
| id | true | true |
| true | true | |
| first_name | true | true |
| last_name | true | true |
| account_id | true | false |
| role | true | false |
User Authorizations
User Authorizations to the given Account and any child accounts.
Get All User Authorizations
curl "https://api.hoalife.com/api/users/authorizations" \
-H "Authorization: Token sk_SUPERSECRET"
The above command returns JSON structured like this:
{
"data": [
{
"id": "111",
"type": "authorization",
"attributes": {
"id": 111,
"role": "staff",
"account_id": 1,
"user": {
"id": 1,
"email": "bob@example.com",
"first_name": "Bob",
"last_name": "Smith",
"timezone": "Mountain Time (US & Canada)"
}
},
"relationships": {}
},
{
"id": "222",
"type": "authorization",
"attributes": {
"id": 222,
"role": "read_only",
"account_id": 1,
"user": {
"id": 2,
"email": "boardmember@example.com",
"first_name": "Board",
"last_name": "Member",
"timezone": "Mountain Time (US & Canada)"
}
},
"relationships": {}
},
],
"meta": {
"current_page": 1,
"total": 2,
"total_pages": 1
},
"links": {
"self": "/api/users/authorizations?page=1",
"next": "/api/users/authorizations",
"prev": "/api/users/authorizations"
}
}
This endpoint retrieves all User Authorizations accessible by the API key. If the API key has access to multiple Accounts, all User Authorizations for all those accounts will be returned. Scope User Authorizations to a specific Account by adding an account_id query parameter.
HTTP Request
GET https://api.hoalife.com/api/users/authorizations
Attributes
| Attribute | Type | Presence | Nullable | Description |
|---|---|---|---|---|
id |
Integer | Always | No | Unique identifier of the User Authorization |
role |
String | Always | No | The authorization level for the User Allowed values: "administrator", "manager", "staff", "inspector", "board_member", "read_only", "architectural_committee_member", "architectural_committee_voter". |
account_id |
Integer | Always | No | Unique identifier of the Account the Authorization is for |
user |
Object | Always | No | Information about the User the Authorization is for Create uses this input when no user relationship is supplied; email can match an existing user. |
user.id |
Integer | Always | No | — |
user.email |
String | Always | No | User's email address. |
user.first_name |
String | Always | No | User's first name. |
user.last_name |
String | Always | No | User's last name. |
user.timezone |
String | Always | No | — |
account_external_id |
String | Always | Yes | External identifier of the associated account. |
Nested presence applies when its parent object is present.
Query Parameters
| Parameter | Query By | Order By |
|---|---|---|
| id | true | true |
| account_id | true | true |
| role | true | true |
| true | false | |
| first_name | true | false |
| last_name | true | false |
Create User Authorization
When creating a User Authorization, passing the user's information is recommended. If a User does not exist for a given email address, a user will be created. If the User does exist, the record will be looked up by the email attribute.
Alternatively, if the User already exists and you know the User's id, you can pass the id via a relationship. The former method is preferred to avoid an additional API request.
Only one Authorization per User per Account is possible. Per Account hierarchy branch User Authorizations are encouraged to scope a User's access. Multiple User Authorizations throughout the same Account hierarchy branch is possible, but not recommended.
Available roles are administrator, manager, staff, inspector, and read_only.
curl "https://api.hoalife.com/api/users/authorizations" \
-X POST \
-H "Authorization: Token sk_SUPERSECRET" \
-H "Content-Type: application/vnd.api+json" \
-d '
{
"data": {
"type": "authorization",
"attributes": {
"role": "read_only",
"send_invitation_email": false,
"user": {
"first_name": "Bob",
"last_name": "Smith",
"email": "bob@example.com"
}
},
"relationships": {
"account": {
"data": {
"type": "account",
"id": 5
}
}
}
}
}
'
# OR
curl "https://api.hoalife.com/api/users/authorizations" \
-X POST \
-H "Authorization: Token sk_SUPERSECRET" \
-H "Content-Type: application/vnd.api+json" \
-d '
{
"data": {
"type": "authorization",
"attributes": {
"role": "read_only",
"send_invitation_email": false
},
"relationships": {
"account": {
"data": {
"type": "account",
"id": 5
}
},
"user": {
"data": {
"type": "user",
"id": 2
}
},
}
}
}
'
The above command returns JSON structured like this:
{
"data": {
"id": "3333",
"type": "authorization",
"attributes": {
"id": 3333,
"role": "read_only",
"account_id": 5,
"user": {
"id": 1,
"email": "bob@example.com",
"first_name": "Bob",
"last_name": "Smith",
"timezone": "Mountain Time (US & Canada)"
}
}
},
"relationships": {}
}
}
This endpoint creates a User Authorization.
HTTP Request
POST https://api.hoalife.com/api/users/authorizations
User Authorization Writable Attributes
| Attribute | Type | Required | Nullable | Description |
|---|---|---|---|---|
role |
String | Yes | No | The authorization level for the User Allowed values: "administrator", "manager", "staff", "inspector", "board_member", "read_only", "architectural_committee_member", "architectural_committee_voter". |
send_invitation_email |
Boolean | No | No | Whether to send the user an invitation email. |
user |
Object | No | No | Information about the User the Authorization is for Create uses this input when no user relationship is supplied; email can match an existing user. |
user.first_name |
String | No | No | User's first name; needed when creating a new user. |
user.last_name |
String | No | No | User's last name; needed when creating a new user. |
user.email |
String | Yes | No | User's email address; can identify an existing user. |
user.disable_marketing_communications |
Boolean | No | No | — |
user.disable_all_system_notifications |
Boolean | No | No | — |
Required refers to unconditional presence. See descriptions for conditional requirements. Nested requirements apply when the parent object is supplied.
Select the account through data.relationships.account.data.id. Select an existing user through data.relationships.user.data.id, or supply the nested user attributes below data.attributes.
Update User Authorization
curl "https://api.hoalife.com/api/users/authorizations/42" \
-X PUT \
-H "Authorization: Token sk_SUPERSECRET" \
-H "Content-Type: application/vnd.api+json" \
-d '
{
"data": {
"type": "authorization",
"attributes": {
"role": "read_only"
}
}
}
'
The above command will return updated JSON, just like for the Create action with a successful status code of
200 OK.
This endpoint updates an existing User Authorization.
HTTP Request
PUT https://api.hoalife.com/api/users/authorizations/:user_authorization_id
URL Parameters
| Parameter | Type | Description |
|---|---|---|
| :user_authorization_id | Integer | Unique identifier of the User Authorization the request is being made for |
Editable Attributes
| Attribute | Type | Required | Nullable | Description |
|---|---|---|---|---|
role |
String | Yes | No | The authorization level for the User Allowed values: "administrator", "manager", "staff", "inspector", "board_member", "read_only", "architectural_committee_member", "architectural_committee_voter". |
Required refers to unconditional presence. See descriptions for conditional requirements. Nested requirements apply when the parent object is supplied.
Destroy User Authorization
curl "https://api.hoalife.com/api/users/authorizations/42" \
-X DELETE \
-H "Authorization: Token sk_SUPERSECRET" \
-H "Content-Type: application/vnd.api+json"
The above command will return an empty response successful response with a successful status code of
202 Accepted.
This endpoint destroys an existing User Authorization.
HTTP Request
DELETE https://api.hoalife.com/api/users/authorizations/:user_authorization_id
URL Parameters
| Parameter | Type | Description |
|---|---|---|
| :user_authorization_id | Integer | Unique identifier of the User Authorization the request is being made for |
Properties
Properties represent a property or unit in an Association.
Get All Properties
curl "https://api.hoalife.com/api/properties" \
-H "Authorization: Token sk_SUPERSECRET"
The above command returns JSON structured like this:
{
"data": [
{
"id": "1",
"type": "property",
"attributes": {
"id": 1,
"account_id": 2,
"formatted_id": "PRP-1",
"street_1": "944 Awesome Street",
"street_2": null,
"city": "Sun Valley",
"state": "ID",
"postal_code": "83353",
"override_geocoding": false,
"latitude": "37.233333",
"longitude": "-115.808333",
"route_leg": null,
"lot_number": null,
"route": "Awesome Street",
"emails": [
"johnAndJudySmithOnFacebook@aol.com"
],
"phone_numbers": [],
"created_at": "2019-07-30T21:16:45.265Z",
"updated_at": "2019-07-30T21:16:45.265Z",
"mailing_name": "John and Judy Smith",
"mailing_street_1": "944 Awesome Street",
"mailing_street_2": null,
"mailing_city": "Sun Valley",
"mailing_state": "ID",
"mailing_postal_code": "83353",
"notes": [
{
"body_text": "Neighbor has complained about poor lawn hygiene.",
"author": "John Owner",
"pinned": true,
"created_at": "2019-08-05T03:26:02.917Z"
}
],
"tags":[{"title":"Board Member","id":1,"color":"#6fa7ce"}],
"external_id": null
},
"relationships": {}
},
...
{
"id": "25",
"type": "property",
"attributes": {
"id": 25,
"account_id": 2,
"formatted_id": "PRP-25",
"street_1": "4242 Cowbell Drive",
"street_2": null,
"city": "Sun Valley",
"state": "ID",
"postal_code": "83353",
"override_geocoding": false,
"latitude": "37.233333",
"longitude": "-115.808333",
"route_leg": null,
"route": "Cowbell Drive",
"lot_number": null,
"emails": [
"earl-larae@cowbelldrive.com", "earl-larae@yahsnooze.com"
],
"phone_numbers": [
"+132155512345", "+132155512346"
],
"created_at": "2019-07-30T21:16:45.460Z",
"updated_at": "2019-07-30T21:16:45.460Z",
"mailing_name": "Earl LaRae",
"mailing_street_1": "PO BOX 42424242",
"mailing_street_2": null,
"mailing_city": "Sun Valley",
"mailing_state": "ID",
"mailing_postal_code": "83353",
"notes": [],
"tags": [],
"external_id": null
},
"relationships": {}
}
],
"meta": {
"current_page": 1,
"total": 858,
"total_pages": 35
},
"links": {
"self": "/api/properties?page=1",
"next": "/api/properties?page=2",
"prev": "/api/properties"
}
}
This endpoint retrieves all Properties accessible by the API key. If the API key has access to multiple Accounts, all Properties for all those accounts will be returned. Scope Properties to a specific Account by adding an account_id query parameter.
HTTP Request
GET https://api.hoalife.com/api/properties
Attributes
| Attribute | Type | Presence | Nullable | Description |
|---|---|---|---|---|
id |
Integer | Always | No | Unique identifier of the Property |
formatted_id |
String | Always | Yes | Humanized unique identifier |
display_address |
String | Always | No | Formatted street address, city, and state. |
street_1 |
String | Always | No | Street address of the property Required for a new property; may be omitted when create matches an existing property by external_id. |
city |
String | Always | No | City of the property Required for a new property; may be omitted when create matches an existing property by external_id. |
state |
String | Always | No | State of the property Required for a new property; may be omitted when create matches an existing property by external_id. |
postal_code |
String | Always | No | Postal code of the property Required for a new property; may be omitted when create matches an existing property by external_id. |
country |
String | Always | No | Country of the property. |
street_2 |
String | Always | Yes | Street address of the property |
route_leg |
String | Always | Yes | Inspection sub-route property belongs to, such as 'odd' or 'A'. May be overridden if override_geocoding is false. |
route |
String | Always | Yes | Inspection route property belongs to, such as the property street name. May be overridden if override_geocoding is false. |
lot_number |
String | Always | Yes | Any string to identify the lot |
external_id |
String | Always | Yes | A unique identifier from an external system used to map the record |
mailing_name |
String | Always | Yes | Mailing recipient name for the property. Defaults to 'Current Resident' when omitted for a new property. |
mailing_street_1 |
String | Always | Yes | Mailing street address of the property |
mailing_street_2 |
String | Always | Yes | Mailing street address of the property |
mailing_country |
String | Always | Yes | Mailing country. Defaults to 'US' for a new property. |
mailing_city |
String | Always | Yes | Mailing city. Required on the property when mailing_street_1 is not blank; existing values may be retained on updates. |
mailing_state |
String | Always | Yes | Mailing state. Required on the property when mailing_street_1 is not blank; existing values may be retained on updates. |
mailing_postal_code |
String | Always | Yes | Mailing postal code. Required on the property when mailing_street_1 is not blank; existing values may be retained on updates. |
override_geocoding |
Boolean | Always | Yes | Use the supplied latitude and longitude or automatically geocode the property's address. |
latitude |
String | Always | Yes | GPS Latitude coordinate of the Property Coordinates must be present on the property when override_geocoding is true. |
longitude |
String | Always | Yes | GPS Longitude coordinate of the Property Coordinates must be present on the property when override_geocoding is true. |
emails |
Array[String] | Always | No | Array of strings of the property owner's email addresses |
emails[] |
String | Each item | No | — |
phone_numbers |
Array[String] | Always | No | Array of strings of the property owner's phone numbers |
phone_numbers[] |
String | Each item | No | — |
created_at |
String | Always | No | Date and time the record was created. Format: date-time. |
updated_at |
String | Always | No | Date and time the record was last updated. Format: date-time. |
account_id |
Integer | Always | No | Unique identifier of Account the Property belongs to Required on create unless data.relationships.account supplies the account. |
tags |
Array[Object] | Always | No | Property tags. On create, inputs can be tag IDs or titles; responses contain tag details. |
tags[] |
Object | Each item | No | — |
tags[].id |
String | Always | No | — |
tags[].title |
String | Always | No | — |
tags[].color |
String | Always | No | — |
notes |
Array[Object] | Always | No | Property notes |
notes[] |
Object | Each item | No | — |
notes[].id |
Integer | Always | No | — |
notes[].body_text |
String | Always | No | — |
notes[].author |
String | Always | No | — |
notes[].pinned |
Boolean | Always | No | — |
notes[].created_at |
String | Always | No | Format: date-time. |
account_external_id |
String | Always | Yes | External identifier of the associated account. |
Nested presence applies when its parent object is present.
Query Parameters
| Parameter | Query By | Order By |
|---|---|---|
| id | true | true |
| account_id | true | true |
| formatted_id | true | true |
| street_1 | true | true |
| street_2 | true | true |
| city | true | true |
| state | true | true |
| postal_code | true | true |
| override_geocoding | true | true |
| latitude | true | true |
| longitude | true | true |
| route_leg | true | true |
| route | true | true |
| emails | false | false |
| phone_numbers | false | false |
| mailing_name | true | true |
| mailing_street_1 | true | true |
| mailing_street_2 | true | true |
| mailing_city | true | true |
| mailing_state | true | true |
| mailing_postal_code | true | true |
| notes | false | false |
| tags | false | false |
| external_id | true | true |
Create Property
curl "https://api.hoalife.com/api/properties" \
-X POST \
-H "Authorization: Token sk_SUPERSECRET" \
-H "Content-Type: application/vnd.api+json" \
-d '
{
"data": {
"type": "property",
"attributes": {
"street_1": "4242 Cowbell Drive",
"city": "Sun Valley",
"state": "ID",
"postal_code": "83353",
"external_id": "123"
},
"relationships": {
"account": {
"data": {
"type": "account",
"id": 5
}
}
}
}
}
'
The above command returns JSON structured like this:
{
"data": {
"id": "25",
"type": "property",
"attributes": {
"id": 25,
"account_id": 5,
"formatted_id": "PRP-25",
"street_1": "4242 Cowbell Drive",
"street_2": null,
"city": "Sun Valley",
"state": "ID",
"postal_code": "83353",
"override_geocoding": false,
"latitude": "37.233333",
"longitude": "-115.808333",
"route_leg": null,
"route": "Cowbell Drive",
"emails": [
"earl-larae@cowbelldrive.com", "earl-larae@yahsnooze.com"
],
"phone_numbers": [
"+132155512345", "+132155512346"
],
"created_at": "2019-07-30T21:16:45.460Z",
"updated_at": "2019-07-30T21:16:45.460Z",
"mailing_name": "Earl LaRae",
"mailing_street_1": "PO BOX 42424242",
"mailing_street_2": null,
"mailing_city": "Sun Valley",
"mailing_state": "ID",
"mailing_postal_code": "83353",
"notes": [],
"external_id": "123"
},
},
"relationships": {}
}
}
This endpoint creates a new Property.
HTTP Request
POST https://api.hoalife.com/api/properties
Property Writable Attributes
| Attribute | Type | Required | Nullable | Description |
|---|---|---|---|---|
street_1 |
String | No | No | Street address of the property Required for a new property; may be omitted when create matches an existing property by external_id. |
city |
String | No | No | City of the property Required for a new property; may be omitted when create matches an existing property by external_id. |
state |
String | No | No | State of the property Required for a new property; may be omitted when create matches an existing property by external_id. |
postal_code |
String | No | No | Postal code of the property Required for a new property; may be omitted when create matches an existing property by external_id. |
street_2 |
String | No | Yes | Street address of the property |
route_leg |
String | No | Yes | Inspection sub-route property belongs to, such as 'odd' or 'A'. May be overridden if override_geocoding is false. |
route |
String | No | Yes | Inspection route property belongs to, such as the property street name. May be overridden if override_geocoding is false. |
lot_number |
String | No | Yes | Any string to identify the lot |
external_id |
String | No | Yes | A unique identifier from an external system used to map the record |
mailing_name |
String | No | Yes | Mailing recipient name for the property. Defaults to 'Current Resident' when omitted for a new property. |
mailing_street_1 |
String | No | Yes | Mailing street address of the property |
mailing_street_2 |
String | No | Yes | Mailing street address of the property |
mailing_country |
String | No | Yes | Mailing country. Defaults to 'US' for a new property. |
mailing_city |
String | No | Yes | Mailing city. Required on the property when mailing_street_1 is not blank; existing values may be retained on updates. |
mailing_state |
String | No | Yes | Mailing state. Required on the property when mailing_street_1 is not blank; existing values may be retained on updates. |
mailing_postal_code |
String | No | Yes | Mailing postal code. Required on the property when mailing_street_1 is not blank; existing values may be retained on updates. |
override_geocoding |
Boolean | No | Yes | Use the supplied latitude and longitude or automatically geocode the property's address. |
latitude |
String or Number | No | Yes | GPS Latitude coordinate of the Property Coordinates must be present on the property when override_geocoding is true. |
longitude |
String or Number | No | Yes | GPS Longitude coordinate of the Property Coordinates must be present on the property when override_geocoding is true. |
emails |
Array[String] | No | No | Array of strings of the property owner's email addresses |
emails[] |
String | Each item | No | — |
phone_numbers |
Array[String] | No | No | Array of strings of the property owner's phone numbers |
phone_numbers[] |
String | Each item | No | — |
account_id |
String or Integer | No | No | Unique identifier of Account the Property belongs to Required on create unless data.relationships.account supplies the account. |
tags |
Array[String or Integer] | No | No | Property tags. On create, inputs can be tag IDs or titles; responses contain tag details. |
tags[] |
String or Integer | Each item | No | — |
Required refers to unconditional presence. See descriptions for conditional requirements. Nested requirements apply when the parent object is supplied.
When mailing_street_1 is not blank, the property must also have mailing_city, mailing_state, and mailing_postal_code. New properties default a missing mailing_name to "Current Resident" and mailing_country to "US". When matching an existing property, its existing mailing city, state, and postal code can satisfy these requirements.
Update Property
curl "https://api.hoalife.com/api/properties/42" \
-X PUT \
-H "Authorization: Token sk_SUPERSECRET" \
-H "Content-Type: application/vnd.api+json" \
-d '
{
"data": {
"type": "property",
"attributes": {
"street_1": "123 Updated St",
"tags": ["1", "Board Member"]
}
}
}
'
The above command will return updated JSON, just like for the Create action with a successful status code of
201 CREATED.
This endpoint updates an existing Property.
HTTP Request
PUT https://api.hoalife.com/api/properties/:property_id
URL Parameters
| Parameter | Type | Description |
|---|---|---|
| :property_id | Integer | Unique identifier of the Property the request is being made for |
Editable Attributes
| Attribute | Type | Required | Nullable | Description |
|---|---|---|---|---|
street_1 |
String | No | No | Street address of the property Required for a new property; may be omitted when create matches an existing property by external_id. |
city |
String | No | No | City of the property Required for a new property; may be omitted when create matches an existing property by external_id. |
state |
String | No | No | State of the property Required for a new property; may be omitted when create matches an existing property by external_id. |
postal_code |
String | No | No | Postal code of the property Required for a new property; may be omitted when create matches an existing property by external_id. |
street_2 |
String | No | Yes | Street address of the property |
route_leg |
String | No | Yes | Inspection sub-route property belongs to, such as 'odd' or 'A'. May be overridden if override_geocoding is false. |
route |
String | No | Yes | Inspection route property belongs to, such as the property street name. May be overridden if override_geocoding is false. |
lot_number |
String | No | Yes | Any string to identify the lot |
external_id |
String | No | Yes | A unique identifier from an external system used to map the record |
mailing_name |
String | No | Yes | Mailing recipient name for the property. Defaults to 'Current Resident' when omitted for a new property. |
mailing_street_1 |
String | No | Yes | Mailing street address of the property |
mailing_street_2 |
String | No | Yes | Mailing street address of the property |
mailing_country |
String | No | Yes | Mailing country. Defaults to 'US' for a new property. |
mailing_city |
String | No | Yes | Mailing city. Required on the property when mailing_street_1 is not blank; existing values may be retained on updates. |
mailing_state |
String | No | Yes | Mailing state. Required on the property when mailing_street_1 is not blank; existing values may be retained on updates. |
mailing_postal_code |
String | No | Yes | Mailing postal code. Required on the property when mailing_street_1 is not blank; existing values may be retained on updates. |
override_geocoding |
Boolean | No | Yes | Use the supplied latitude and longitude or automatically geocode the property's address. |
latitude |
String or Number | No | Yes | GPS Latitude coordinate of the Property Coordinates must be present on the property when override_geocoding is true. |
longitude |
String or Number | No | Yes | GPS Longitude coordinate of the Property Coordinates must be present on the property when override_geocoding is true. |
emails |
Array[String] | No | No | Array of strings of the property owner's email addresses |
emails[] |
String | Each item | No | — |
phone_numbers |
Array[String] | No | No | Array of strings of the property owner's phone numbers |
phone_numbers[] |
String | Each item | No | — |
skip_ownership_changes |
Boolean | No | No | Skip ownership-change processing for this update. |
tags |
Array[String or Integer] | No | No | Property tags. On create, inputs can be tag IDs or titles; responses contain tag details. |
tags[] |
String or Integer | Each item | No | — |
Required refers to unconditional presence. See descriptions for conditional requirements. Nested requirements apply when the parent object is supplied.
Destroy Property
curl "https://api.hoalife.com/api/properties/42" \
-X DELETE \
-H "Authorization: Token sk_SUPERSECRET" \
-H "Content-Type: application/vnd.api+json"
The above command will return an empty response successful response with a successful status code of
202 Accepted.
This endpoint destroys an existing Property. Because Property objects have many dependent resources, the actual deletion of the data is performed in a background process. This means that the API may continue to return data for the Property even after you've requested a it be deleted.
HTTP Request
DELETE https://api.hoalife.com/api/properties/:property_id
URL Parameters
| Parameter | Type | Description |
|---|---|---|
| :property_id | Integer | Unique identifier of the Property the request is being made for |
Property Conversations
Property Conversations are discussion threads associated with a property.
Get All Property Conversations
curl "https://api.hoalife.com/api/properties/456/conversations" \
-H "Authorization: Token sk_SUPERSECRET"
The above command returns JSON structured like this:
{
"data": [
{
"id": "125",
"type": "conversation",
"attributes": {
"id": 125,
"property_id": 456,
"description": "Pool maintenance",
"created_at": "2026-10-01T14:20:00.000Z",
"updated_at": "2026-10-02T09:15:00.000Z"
},
"relationships": {}
}
],
"meta": {
"current_page": 1,
"total": 1,
"total_pages": 1
},
"links": {
"self": "/api/properties/456/conversations?page=1",
"next": "/api/properties/456/conversations",
"prev": "/api/properties/456/conversations"
}
}
Returns conversation threads for the property identified by :property_id. Conversations are ordered by most recently updated first. Use Get Conversation Messages to read messages in a thread. Both endpoints require the Conversations Read permission (conversations: ["read"]) and accept service account and user access credentials. Only properties in the credential's account or descendant accounts are accessible. A missing or inaccessible property or conversation returns 404.
HTTP Request
GET https://api.hoalife.com/api/properties/:property_id/conversations
URL Parameters
| Parameter | Type | Description |
|---|---|---|
| :property_id | Integer | Unique identifier of a Property |
Query Parameters
| Parameter | Query By | Order By |
|---|---|---|
| id | false | false |
| property_id | false | false |
| description | false | false |
| created_at | false | false |
| updated_at | false | false |
Attributes
| Attribute | Type | Presence | Nullable | Description |
|---|---|---|---|---|
id |
Integer | Always | No | Unique identifier of the conversation. |
property_id |
Integer | Always | No | Property associated with the conversation. |
description |
String | Always | No | Conversation subject or the property's formatted identifier. |
created_at |
String | Always | No | Format: date-time. |
updated_at |
String | Always | No | Format: date-time. |
Nested presence applies when its parent object is present.
Get Conversation Messages
curl "https://api.hoalife.com/api/properties/456/conversations/125/messages" \
-H "Authorization: Token sk_SUPERSECRET"
Returns messages in the selected property's conversation, oldest first. Results
use the standard JSON:API collection envelope with data, pagination meta, and
links. Each resource has type conversation_message. Message bodies are plain
text; raw email headers and attachments are not included. This endpoint is
read-only and does not send messages.
HTTP Request
GET https://api.hoalife.com/api/properties/:property_id/conversations/:conversation_id/messages
URL Parameters
| Parameter | Type | Description |
|---|---|---|
| :property_id | Integer | Unique identifier of the Property |
| :conversation_id | Integer | Unique identifier of a conversation belonging to that Property |
Query Parameters
page selects a page of 25 messages. Filtering and custom ordering are not supported.
Attributes
| Attribute | Type | Presence | Nullable | Description |
|---|---|---|---|---|
id |
Integer | Always | No | Unique identifier of the message. |
conversation_id |
Integer | Always | No | Conversation containing the message. |
subject |
String | Always | Yes | — |
body_text |
String | Always | No | Message body as plain text. |
direction |
String | Always | No | Allowed values: "sent", "received". |
status |
String | Always | No | Allowed values: "undelivered", "received", "sent", "delivered", "opened", "error", "insufficient_delivery_credits", "payment_method_required". |
author_id |
Integer | Always | Yes | — |
created_at |
String | Always | No | Format: date-time. |
updated_at |
String | Always | No | Format: date-time. |
Nested presence applies when its parent object is present.
Property Notes
Property Notes are notes attached directly to a property.
Get All Property Notes
curl "https://api.hoalife.com/api/properties/456/notes" \
-H "Authorization: Token sk_SUPERSECRET"
The above command returns JSON structured like this:
{
"data": [
{
"id": "125",
"type": "note",
"attributes": {
"id": 125,
"pinned": true,
"auto_action": null,
"auto_action_at": null,
"created_at": "2026-10-01T14:20:00.000Z",
"author_id": 1,
"body_text": "Inspection follow-up",
"attached_to_type": "property",
"attached_to_id": 456
}
}
],
"meta": {
"current_page": 1,
"total": 1,
"total_pages": 1
},
"links": {
"self": "/api/properties/456/notes?page=1",
"next": "/api/properties/456/notes",
"prev": "/api/properties/456/notes"
}
}
Returns all notes attached directly to the selected property, including pinned and unpinned notes, newest first by creation date. Notes on the property's escalations or architectural requests are not included. Note bodies are returned as plain text using the existing note response format.
Requires Notes Read permission (notes: ["read"]) and accepts service
account and user access credentials. Access is limited to properties in the
credential's account or descendant accounts. A missing or inaccessible property
returns 404; missing read permission returns 403.
HTTP Request
GET https://api.hoalife.com/api/properties/:property_id/notes
URL Parameters
| Parameter | Type | Description |
|---|---|---|
| :property_id | Integer | Unique identifier of the Property |
Query Parameters
page selects a page of 25 notes. Filtering and custom ordering are not supported.
Attributes
| Attribute | Type | Presence | Nullable | Description |
|---|---|---|---|---|
id |
Integer | Always | No | — |
pinned |
Boolean | Always | No | Denote if the note should be pinned |
auto_action |
String | Always | Yes | Allowed values: "destroy", "remove_pin". |
auto_action_at |
String | Always | Yes | Format: date-time. |
created_at |
String | Always | No | Date and time the record was created. Format: date-time. |
author_id |
Integer | Always | No | ID of the User who authored the note. Supply a user with a role on the credential's account, a descendant, or an ancestor account, or pass null to select the default author. Omit on update to keep the current author. On create, omission uses the legacy author relationship or the default author. |
body_text |
String | Always | No | The note's text |
attached_to_type |
String | Always | No | Type of entity the note is attached to: property, escalation, deficiency, or acc_request. Allowed values: "property", "escalation", "deficiency", "acc_request". |
attached_to_id |
Integer | Always | No | ID of the existing entity identified by attached_to_type, within the credential's account scope; not the note's ID. |
Nested presence applies when its parent object is present.
Property Supplemental Mailing Addresses
Supplemental mailing addresses for properties. The primary property mailing address should be provided as Property data.
Get All Property Supplemental Mailing Addresses
curl "https://api.hoalife.com/api/properties/supplemental_mailing_addresses" \
-H "Authorization: Token sk_SUPERSECRET"
The above command returns JSON structured like this:
{
"data": [
{
"data": [
{
"id": "1",
"type": "supplemental_mailing_address",
"attributes": {
"id": 1,
"property_id": 42,
"send_notices": true,
"mailing_name": "John Smith",
"mailing_street_1": "1234 Tall Oak Court",
"mailing_street_2": "",
"mailing_city": "Low Point",
"mailing_state": "NC",
"mailing_postal_code": "23365",
"external_id": "abc-123"
},
"relationships": {}
},
{
"id": "2",
"type": "supplemental_mailing_address",
"attributes": {
"id": 2,
"property_id": 42,
"send_notices": false,
"mailing_name": "Jane Smith",
"mailing_street_1": "22558 Casa Grande Drive",
"mailing_street_2": "",
"mailing_city": "Cookersfield",
"mailing_state": "IA",
"mailing_postal_code": "91106",
"external_id": "abc-122"
},
"relationships": {}
}
],
"meta": {
"current_page": 1,
"total": 2,
"total_pages": 1
"self": "/api/properties/supplemental_mailing_addresses?page=1",
"next": "/api/properties/supplemental_mailing_addresses?page=2",
"prev": "/api/properties/supplemental_mailing_addresses"
}
}
This endpoint retrieves all Property Supplemental Mailing Addresses for the given Property ID.
This endpoint retrieves all Property Supplemental Mailing Addresses accessible by the API key. If the API key has access to multiple Accounts, all Properties for all those accounts will be returned. Scope Property Supplemental Mailing Addresses to a specific Property by adding an property_id query parameter.
HTTP Request
GET https://api.hoalife.com/api/properties/supplemental_mailing_addresses
Attributes
| Attribute | Type | Presence | Nullable | Description |
|---|---|---|---|---|
id |
Integer | Always | No | Unique identifier of the supplemental mailing address. |
property_id |
Integer | Always | No | Unique identifier of the Property the Supplemental Mailing Address belongs to Create accepts this ID when data.relationships.property is omitted; the controller resolves the property. |
send_notices |
Boolean | Always | Yes | Whether notices should be sent to this address. |
external_id |
String | Always | Yes | A unique identifier from an external system used to map the record |
mailing_name |
String | Always | No | Mailing recipient name. |
mailing_street_1 |
String | Always | No | Mailing street address |
mailing_street_2 |
String | Always | Yes | Mailing street address |
mailing_country |
String | Always | No | Mailing country. |
mailing_city |
String | Always | No | Mailing city address |
mailing_state |
String | Always | No | Mailing state address |
mailing_postal_code |
String | Always | No | Mailing postal code address |
property_external_id |
String | Always | Yes | External identifier of the associated property. Currently returns this supplemental address's external_id. |
Nested presence applies when its parent object is present.
Query Parameters
| Parameter | Query By | Order By |
|---|---|---|
| id | true | true |
| property_id | true | true |
| mailing_name | true | true |
| mailing_street_1 | true | true |
| mailing_street_2 | true | true |
| mailing_city | true | true |
| mailing_state | true | true |
| mailing_postal_code | true | true |
| external_id | true | true |
Create Property Supplemental Mailing Address
curl "https://api.hoalife.com/api/properties/supplemental_mailing_addresses" \
-X POST \
-H "Authorization: Token sk_SUPERSECRET" \
-H "Content-Type: application/vnd.api+json" \
-d '
{
"data": {
"type": "supplemental_mailing_address",
"attributes": {
"mailing_name": "Jane Smith",
"mailing_street_1": "22558 Casa Grande Drive",
"mailing_city": "Cookersfield",
"mailing_state": "IA",
"mailing_postal_code": "91106",
"send_notices": true,
"external_id": "abc-123"
},
"relationships": {
"property": {
"data": {
"type": "property",
"id": 42
}
}
}
}
}
'
The above command returns JSON structured like this:
{
"data": {
"id": "25",
"type": "supplemental_mailing_address",
"attributes": {
"id": 2,
"property_id": 42,
"send_notices": true,
"mailing_name": "Jane Smith",
"mailing_street_1": "22558 Casa Grande Drive",
"mailing_street_2": "",
"mailing_city": "Cookersfield",
"mailing_state": "IA",
"mailing_postal_code": "91106",
"external_id": "abc-123"
},
},
"relationships": {}
}
}
This endpoint creates a new Property Supplemental Mailing Address.
HTTP Request
POST https://api.hoalife.com/api/properties/supplemental_mailing_addresses
Property Supplemental Mailing Address Writable Attributes
| Attribute | Type | Required | Nullable | Description |
|---|---|---|---|---|
property_id |
String or Integer | No | No | Unique identifier of the Property the Supplemental Mailing Address belongs to Create accepts this ID when data.relationships.property is omitted; the controller resolves the property. |
send_notices |
Boolean | No | Yes | Whether notices should be sent to this address. |
external_id |
String | No | Yes | A unique identifier from an external system used to map the record |
mailing_name |
String | Yes | No | Mailing recipient name. |
mailing_street_1 |
String | Yes | No | Mailing street address |
mailing_street_2 |
String | No | Yes | Mailing street address |
mailing_country |
String | No | No | Mailing country. |
mailing_city |
String | Yes | No | Mailing city address |
mailing_state |
String | Yes | No | Mailing state address |
mailing_postal_code |
String | Yes | No | Mailing postal code address |
Required refers to unconditional presence. See descriptions for conditional requirements. Nested requirements apply when the parent object is supplied.
Update Property Supplemental Mailing Address
curl "https://api.hoalife.com/api/properties/supplemental_mailing_addresses/25" \
-X PUT \
-H "Authorization: Token sk_SUPERSECRET" \
-H "Content-Type: application/vnd.api+json" \
-d '
{
"data": {
"type": "supplemental_mailing_address",
"attributes": {
"mailing_name": "Josh Carunner",
}
}
}
'
The above command will return updated JSON, just like for the Create action with a successful status code of
201 CREATED.
This endpoint updates an existing Property Supplemental Mailing Address.
HTTP Request
PUT https://api.hoalife.com/api/properties/supplemental_mailing_addresses/:supplemental_mailing_addresses_id
URL Parameters
| Parameter | Type | Description |
|---|---|---|
| :supplemental_mailing_addresses_id | Integer | Unique identifier of the Property Supplemental Mailing Address the request is being made for |
Editable Attributes
| Attribute | Type | Required | Nullable | Description |
|---|---|---|---|---|
send_notices |
Boolean | No | Yes | Whether notices should be sent to this address. |
external_id |
String | No | Yes | A unique identifier from an external system used to map the record |
mailing_name |
String | No | No | Mailing recipient name. |
mailing_street_1 |
String | No | No | Mailing street address |
mailing_street_2 |
String | No | Yes | Mailing street address |
mailing_country |
String | No | No | Mailing country. |
mailing_city |
String | No | No | Mailing city address |
mailing_state |
String | No | No | Mailing state address |
mailing_postal_code |
String | No | No | Mailing postal code address |
Required refers to unconditional presence. See descriptions for conditional requirements. Nested requirements apply when the parent object is supplied.
Destroy Property
curl "https://api.hoalife.com/api/properties/supplemental_mailing_addresses/42" \
-X DELETE \
-H "Authorization: Token sk_SUPERSECRET" \
-H "Content-Type: application/vnd.api+json"
The above command will return an empty response successful response with a successful status code of
202 Accepted.
This endpoint destroys an existing Property Supplemental Mailing Address.
HTTP Request
DELETE https://api.hoalife.com/api/properties/supplemental_mailing_addresses/:supplemental_mailing_addresses_id
URL Parameters
| Parameter | Type | Description |
|---|---|---|
| :supplemental_mailing_addresses_id | Integer | Unique identifier of the Property Supplemental Mailing Address the request is being made for |
Assets
Assets represent a asset or common area in an association.
Get All Assets
curl "https://api.hoalife.com/api/assets" \
-H "Authorization: Token sk_SUPERSECRET"
The above command returns JSON structured like this:
{
"data": [
{
"id": "1",
"type": "asset",
"attributes": {
"id": 1,
"account_id": 2,
"formatted_id": "AST-1",
"description": "Pool",
"latitude": "37.233333",
"longitude": "-115.808333",
"created_at": "2019-07-30T21:16:45.460Z",
"updated_at": "2019-07-30T21:16:45.460Z",
"external_id": null
},
"relationships": {}
},
...
{
"id": "25",
"type": "asset",
"attributes": {
"id": 25,
"account_id": 2,
"formatted_id": "AST-25",
"description": "West Entrence Common Area",
"latitude": "37.233333",
"longitude": "-115.808333",
"created_at": "2019-07-30T21:16:45.460Z",
"updated_at": "2019-07-30T21:16:45.460Z",
"external_id": null
},
"relationships": {}
}
],
"meta": {
"current_page": 1,
"total": 858,
"total_pages": 35
},
"links": {
"self": "/api/assets?page=1",
"next": "/api/assets?page=2",
"prev": "/api/assets"
}
}
This endpoint retrieves all Assets accessible by the API key. If the API key has access to multiple Accounts, all Assets for all those accounts will be returned. Scope Assets to a specific Account by adding an account_id query parameter.
HTTP Request
GET https://api.hoalife.com/api/assets
Attributes
| Attribute | Type | Presence | Nullable | Description |
|---|---|---|---|---|
id |
Integer | Always | No | Unique identifier of the Asset |
formatted_id |
String | Always | Yes | Humanized unique identifier |
created_at |
String | Always | No | Date and time the record was created. Format: date-time. |
updated_at |
String | Always | No | Date and time the record was last updated. Format: date-time. |
account_id |
Integer | Always | No | Unique identifier of Account the Asset belongs to Account on create; may instead be provided through the account relationship. |
description |
String | Always | No | Description of the asset. |
latitude |
String | Always | No | GPS Latitude coordinate of the Asset |
longitude |
String | Always | No | GPS Longitude coordinate of the Asset |
external_id |
String | Always | Yes | A unique identifier from an external system used to map the record |
tags |
Array[Object] | Always | No | Tags associated with this record. |
tags[] |
Object | Each item | No | — |
tags[].id |
String | Always | No | — |
tags[].title |
String | Always | No | — |
tags[].color |
String | Always | No | — |
account_external_id |
String | Always | Yes | External identifier of the associated account. |
Nested presence applies when its parent object is present.
Query Parameters
| Parameter | Query By | Order By |
|---|---|---|
| id | true | true |
| account_id | true | true |
| formatted_id | true | true |
| description | true | true |
| latitude | true | true |
| longitude | true | true |
| external_id | true | true |
Create Asset
curl "https://api.hoalife.com/api/assets" \
-X POST \
-H "Authorization: Token sk_SUPERSECRET" \
-H "Content-Type: application/vnd.api+json" \
-d '
{
"data": {
"type": "asset",
"attributes": {
"description": "4242 Cowbell Drive",
"latitude": "37.233333",
"longitude": "-115.808333",
"external_id": "123"
},
"relationships": {
"account": {
"data": {
"type": "account",
"id": 5
}
}
}
}
}
'
The above command returns JSON structured like this:
{
"data": {
"id": "25",
"type": "asset",
"attributes": {
"id": 25,
"account_id": 5,
"formatted_id": "AST-25",
"latitude": "37.233333",
"longitude": "-115.808333",
"created_at": "2019-07-30T21:16:45.460Z",
"updated_at": "2019-07-30T21:16:45.460Z",
"external_id": "123"
},
},
"relationships": {}
}
}
This endpoint creates a new Asset.
HTTP Request
POST https://api.hoalife.com/api/assets
Asset Writable Attributes
| Attribute | Type | Required | Nullable | Description |
|---|---|---|---|---|
account_id |
Integer or String | No | No | Unique identifier of Account the Asset belongs to Account on create; may instead be provided through the account relationship. |
description |
String | Yes | No | Description of the asset. |
latitude |
String or Number | Yes | No | GPS Latitude coordinate of the Asset |
longitude |
String or Number | Yes | No | GPS Longitude coordinate of the Asset |
external_id |
String | No | Yes | A unique identifier from an external system used to map the record |
Required refers to unconditional presence. See descriptions for conditional requirements. Nested requirements apply when the parent object is supplied.
Update Asset
curl "https://api.hoalife.com/api/assets/42" \
-X PUT \
-H "Authorization: Token sk_SUPERSECRET" \
-H "Content-Type: application/vnd.api+json" \
-d '
{
"data": {
"type": "asset",
"attributes": {
"description": "Common aread West entrance"
}
}
}
'
The above command will return updated JSON, just like for the Create action with a successful status code of
201 CREATED.
This endpoint updates an existing Asset.
HTTP Request
PUT https://api.hoalife.com/api/assets/:asset_id
URL Parameters
| Parameter | Type | Description |
|---|---|---|
| :asset_id | Integer | Unique identifier of the Asset the request is being made for |
Editable Attributes
| Attribute | Type | Required | Nullable | Description |
|---|---|---|---|---|
description |
String | No | No | Description of the asset. |
latitude |
String or Number | No | No | GPS Latitude coordinate of the Asset |
longitude |
String or Number | No | No | GPS Longitude coordinate of the Asset |
external_id |
String | No | Yes | A unique identifier from an external system used to map the record |
Required refers to unconditional presence. See descriptions for conditional requirements. Nested requirements apply when the parent object is supplied.
Destroy Asset
curl "https://api.hoalife.com/api/assets/42" \
-X DELETE \
-H "Authorization: Token sk_SUPERSECRET" \
-H "Content-Type: application/vnd.api+json"
The above command will return an empty response successful response with a successful status code of
202 Accepted.
This endpoint destroys an existing Asset. Because Asset objects have many dependent resources, the actual deletion of the data is performed in a background process. This means that the API may continue to return data for the Asset even after you've requested a it be deleted.
HTTP Request
DELETE https://api.hoalife.com/api/assets/:asset_id
URL Parameters
| Parameter | Type | Description |
|---|---|---|
| :asset_id | Integer | Unique identifier of the Asset the request is being made for |
CCR Articles
CCR Articles represent a group of similar Violation Types an Association enforces.
Get All CCR Articles
curl "https://api.hoalife.com/api/ccr_articles" \
-H "Authorization: Token sk_SUPERSECRET"
The above command returns JSON structured like this:
{
"data": [
{
"id": "1",
"type": "ccr_article",
"attributes": {
"id": 1,
"article": "5.14.a",
"title": "Lot Maintenance",
"description": "As stated in Article 5.14(a) - Each Owner of a Lot shall maintain all Improvements located thereon in good and sufficient repair and shall keep the improvements thereon painted or stained, lawn cut, shrubbery trimmed, windows glazed, rubbish and debris removed, weeds cut and otherwise maintain the same in a neat and aesthetically pleasing condition.",
"created_at": "2019-07-30T21:16:47.540Z",
"updated_at": "2019-07-31T15:44:01.646Z",
"account_id": 2,
"external_id": null
},
"relationships": {}
},
...
{
"id": "4",
"type": "ccr_article",
"attributes": {
"id": 4,
"article": "5.24.c",
"title": "Fences and Walls",
"description": "As stated in Article 5.24(c) - All fences and walls shall be constructed and installed and maintained in good appearance and condition at the expense of the Owner of the Lot on which they are located and all damaged fencing and walls shall be repainted or replaced to original design, materials, and color within a reasonable time after said damage occurs.",
"created_at": "2019-07-30T21:16:48.006Z",
"updated_at": "2019-07-30T21:16:48.009Z",
"account_id": 2,
"external_id": null
},
"relationships": {}
}
],
"meta": {
"current_page": 1,
"total": 4,
"total_pages": 1
},
"links": {
"self": "/api/ccr_articles?page=1",
"next": "/api/ccr_articles",
"prev": "/api/ccr_articles"
}
}
This endpoint retrieves all CCR Articles accessible by the API key. If the API key has access to multiple Accounts, all CCR Articles for all those accounts will be returned. Scope CCR Articles to a specific Account by adding an account_id query parameter.
HTTP Request
GET https://api.hoalife.com/api/ccr_articles
Attributes
| Attribute | Type | Presence | Nullable | Description |
|---|---|---|---|---|
id |
Integer | Always | No | Unique identifier of the CCR Article |
article |
String | Always | No | Article of CCRs, used for ordering and nesting |
title |
String | Always | No | Title of the CCR Article |
description |
String | Always | Yes | Description of the CCR Article |
created_at |
String | Always | No | Date and time the record was created. Format: date-time. |
updated_at |
String | Always | No | Date and time the record was last updated. Format: date-time. |
account_id |
Integer | Always | No | Unique identifier of Account the CCR Article belongs to Required on create unless supplied through the account relationship. |
external_id |
String | Always | Yes | A unique identifier from an external system used to map the record |
account_external_id |
String | Always | Yes | External identifier of the associated account. |
Nested presence applies when its parent object is present.
Query Parameters
| Parameter | Query By | Order By |
|---|---|---|
| id | true | true |
| account_id | true | true |
| article | true | true |
| title | true | true |
| description | true | true |
| external_id | true | true |
Create CCR Article
curl "https://api.hoalife.com/api/ccr_articles" \
-X POST \
-H "Authorization: Token sk_SUPERSECRET" \
-H "Content-Type: application/vnd.api+json" \
-d '
{
"data": {
"type": "ccr_article",
"attributes": {
"article": "5.24.c",
"title": "Fences and Walls",
"description": "As stated in Article 5.24(c) - All fences and walls shall be constructed and installed and maintained in good appearance and condition at the expense of the Owner of the Lot on which they are located and all damaged fencing and walls shall be repainted or replaced to original design, materials, and color within a reasonable time after said damage occurs.",
"external_id": "123"
},
"relationships": {
"account": {
"data": {
"type": "account",
"id": 5
}
}
}
}
}
'
The above command returns JSON structured like this:
{
"data": {
"id": "4",
"type": "ccr_article",
"attributes": {
"id": 4,
"article": "5.24.c",
"title": "Fences and Walls",
"description": "As stated in Article 5.24(c) - All fences and walls shall be constructed and installed and maintained in good appearance and condition at the expense of the Owner of the Lot on which they are located and all damaged fencing and walls shall be repainted or replaced to original design, materials, and color within a reasonable time after said damage occurs.",
"created_at": "2019-07-30T21:16:48.006Z",
"updated_at": "2019-07-30T21:16:48.009Z",
"account_id": 2,
"external_id": null
}
},
"relationships": {}
}
}
This endpoint creates a new CCR Article.
HTTP Request
POST https://api.hoalife.com/api/ccr_articles
CCR Article Writable Attributes
| Attribute | Type | Required | Nullable | Description |
|---|---|---|---|---|
article |
String | Yes | No | Article of CCRs, used for ordering and nesting |
title |
String | Yes | No | Title of the CCR Article |
description |
String | No | Yes | Description of the CCR Article |
account_id |
Integer or String | No | No | Unique identifier of Account the CCR Article belongs to Required on create unless supplied through the account relationship. |
external_id |
String | No | Yes | A unique identifier from an external system used to map the record |
Required refers to unconditional presence. See descriptions for conditional requirements. Nested requirements apply when the parent object is supplied.
Update CCR Article
curl "https://api.hoalife.com/api/ccr_articles/42" \
-X PUT \
-H "Authorization: Token sk_SUPERSECRET" \
-H "Content-Type: application/vnd.api+json" \
-d '
{
"data": {
"type": "property",
"attributes": {
"title": "Fences and Walls and Doors"
}
}
}
'
The above command will return updated JSON, just like for the Create action with a successful status code of
201 CREATED.
This endpoint updates an existing CCR Article.
HTTP Request
PUT https://api.hoalife.com/api/ccr_articles/:ccr_article_id
URL Parameters
| Parameter | Type | Description |
|---|---|---|
| :ccr_article_id | Integer | Unique identifier of the CCR Article the request is being made for |
Editable Attributes
| Attribute | Type | Required | Nullable | Description |
|---|---|---|---|---|
article |
String | No | No | Article of CCRs, used for ordering and nesting |
title |
String | No | No | Title of the CCR Article |
description |
String | No | Yes | Description of the CCR Article |
external_id |
String | No | Yes | A unique identifier from an external system used to map the record |
Required refers to unconditional presence. See descriptions for conditional requirements. Nested requirements apply when the parent object is supplied.
Destroy CCR Article
curl "https://api.hoalife.com/api/ccr_articles/42" \
-X DELETE \
-H "Authorization: Token sk_SUPERSECRET" \
-H "Content-Type: application/vnd.api+json"
The above command will return an empty response successful response with a successful status code of
202 Accepted.
This endpoint destroys an existing CCR Article. Because CCR Article objects have many dependent resources, the actual deletion of the data is performed in a background process. This means that the API may continue to return data for the CCR Article even after you've requested a it be deleted.
HTTP Request
DELETE https://api.hoalife.com/api/ccr_articles/:ccr_article_id
URL Parameters
| Parameter | Type | Description |
|---|---|---|
| :ccr_article_id | Integer | Unique identifier of the CCR Article the request is being made for |
CCR Violation Types
CCR Violation Types represent a rule that the Association enforces and records violations for.
Get All CCR Violation Types
curl "https://api.hoalife.com/api/ccr_violation_types" \
-H "Authorization: Token sk_SUPERSECRET"
The above command returns JSON structured like this:
{
"data": [
{
"id": "1",
"type": "ccr_violation_type",
"attributes": {
"id": 1,
"title": "Broadleaf and/or weeds in turf",
"resolution_step": "Apply broadleaf/week mitigation",
"days_to_cure": 20,
"created_at": "2019-07-30T21:16:47.707Z",
"updated_at": "2019-07-31T15:44:01.640Z",
"ccr_article_id": 1,
"external_id": null
},
"relationships": {}
},
...
{
"id": "4",
"type": "ccr_violation_type",
"attributes": {
"id": 4,
"title": "Dead tree(s) needs to be removed/replaced",
"resolution_step": "Remove dead tree(s)",
"days_to_cure": 20,
"created_at": "2019-07-30T21:16:47.712Z",
"updated_at": "2019-07-30T21:16:47.712Z",
"ccr_article_id": 1,
"external_id": null
},
"relationships": {}
}
],
"meta": {
"current_page": 1,
"total": 4,
"total_pages": 1
},
"links": {
"self": "/api/ccr_violation_types?page=1",
"next": "/api/ccr_violation_types",
"prev": "/api/ccr_violation_types"
}
}
This endpoint retrieves all CCR Violation Types accessible by the API key. If the API key has access to multiple Accounts or CCR Violation Types, all Violation Types for all those accounts will be returned. Scope CCR Violation Types to a specific Account by adding an account_id query parameter.
HTTP Request
GET https://api.hoalife.com/api/ccr_violation_types
Attributes
| Attribute | Type | Presence | Nullable | Description |
|---|---|---|---|---|
id |
Integer | Always | No | Unique identifier of the CCR Violation Type |
title |
String | Always | No | Title of the CCR Violation Type |
resolution_step |
String | Always | Yes | Suggested step for resolution of the Violation |
days_to_cure |
Integer | Always | Yes | Days after the Violation was recorded that a Cure Reminder Notification will be sent |
created_at |
String | Always | No | Date and time the record was created. Format: date-time. |
updated_at |
String | Always | No | Date and time the record was last updated. Format: date-time. |
escalation_threshold |
Integer | Always | No | — |
external_id |
String | Always | Yes | A unique identifier from an external system used to map the record |
fine |
String | Always | Yes | — |
account_id |
Integer | Always | No | Unique identifier of Account the CCR Violation Type belongs to |
account_external_id |
String | Always | Yes | External identifier of the associated account. |
ccr_article_external_id |
String | Always | Yes | External identifier of the associated CCR article. |
ccr_article_id |
Integer | Always | No | Unique identifier of CCR Article the CCR Violation Type belongs to Required on create unless supplied through the ccr_article relationship. |
Nested presence applies when its parent object is present.
Query Parameters
| Parameter | Query By | Order By |
|---|---|---|
| id | true | true |
| account_id | true | true |
| ccr_article_id | true | true |
| article | true | true |
| title | true | true |
| resolution_step | true | true |
| days_to_cure | true | true |
| external_id | true | true |
Create CCR Violation Type
curl "https://api.hoalife.com/api/ccr_violation_types" \
-X POST \
-H "Authorization: Token sk_SUPERSECRET" \
-H "Content-Type: application/vnd.api+json" \
-d '
{
"data": {
"type": "ccr_violation_type",
"attributes": {
"title": "Dead tree(s) needs to be removed/replaced",
"resolution_step": "Remove dead tree(s)",
"days_to_cure": 20,
"external_id": "123"
},
"relationships": {
"ccr_article": {
"data": {
"type": "ccr_article",
"id": 1
}
}
}
}
}
'
The above command returns JSON structured like this:
{
"data": {
"id": "4",
"type": "ccr_violation_type",
"attributes": {
"id": 4,
"title": "Dead tree(s) needs to be removed/replaced",
"resolution_step": "Remove dead tree(s)",
"days_to_cure": 20,
"created_at": "2019-07-30T21:16:47.712Z",
"updated_at": "2019-07-30T21:16:47.712Z",
"ccr_article_id": 1,
"external_id": "123"
},
},
"relationships": {}
}
}
This endpoint creates a new CCR Violation Type.
HTTP Request
POST https://api.hoalife.com/api/ccr_violation_types
CCR Violation Type Writable Attributes
| Attribute | Type | Required | Nullable | Description |
|---|---|---|---|---|
title |
String | Yes | No | Title of the CCR Violation Type |
resolution_step |
String | No | Yes | Suggested step for resolution of the Violation |
days_to_cure |
Integer | No | Yes | Days after the Violation was recorded that a Cure Reminder Notification will be sent |
external_id |
String | No | Yes | A unique identifier from an external system used to map the record |
ccr_article_id |
Integer or String | No | No | Unique identifier of CCR Article the CCR Violation Type belongs to Required on create unless supplied through the ccr_article relationship. |
local_escalation_threshold |
Integer | No | Yes | Override the number of repeat violations that triggers an escalation. |
Required refers to unconditional presence. See descriptions for conditional requirements. Nested requirements apply when the parent object is supplied.
Update CCR Violation Type
curl "https://api.hoalife.com/api/ccr_violation_types/42" \
-X PUT \
-H "Authorization: Token sk_SUPERSECRET" \
-H "Content-Type: application/vnd.api+json" \
-d '
{
"data": {
"type": "ccr_violation_type",
"attributes": {
"title": "Dead tree(s) needs to be replaced"
}
}
}
'
The above command will return updated JSON, just like for the Create action with a successful status code of
201 CREATED.
This endpoint updates an existing CCR Violation Type.
HTTP Request
PUT https://api.hoalife.com/api/ccr_violation_types/:ccr_violation_type_id
URL Parameters
| Parameter | Type | Description |
|---|---|---|
| :ccr_violation_type_id | Integer | Unique identifier of the CCR Violation Type the request is being made for |
Editable Attributes
| Attribute | Type | Required | Nullable | Description |
|---|---|---|---|---|
title |
String | No | No | Title of the CCR Violation Type |
resolution_step |
String | No | Yes | Suggested step for resolution of the Violation |
days_to_cure |
Integer | No | Yes | Days after the Violation was recorded that a Cure Reminder Notification will be sent |
external_id |
String | No | Yes | A unique identifier from an external system used to map the record |
local_escalation_threshold |
Integer | No | Yes | Override the number of repeat violations that triggers an escalation. |
Required refers to unconditional presence. See descriptions for conditional requirements. Nested requirements apply when the parent object is supplied.
Destroy CCR Violation Type
curl "https://api.hoalife.com/api/ccr_violation_types/42" \
-X DELETE \
-H "Authorization: Token sk_SUPERSECRET" \
-H "Content-Type: application/vnd.api+json"
The above command will return an empty response successful response with a successful status code of
202 Accepted.
This endpoint destroys an existing CCR Violation Type. Because CCR Violation Type objects have many dependent resources, the actual deletion of the data is performed in a background process. This means that the API may continue to return data for the CCR Violation Type even after you've requested a it be deleted.
HTTP Request
DELETE https://api.hoalife.com/api/ccr_violation_types/:ccr_violation_type_id
URL Parameters
| Parameter | Type | Description |
|---|---|---|
| :ccr_violation_type_id | Integer | Unique identifier of the CCR Violation Type the request is being made for |
Inspections
Inspections represent an inspection of the entire Association for Violations.
Get All Inspections
curl "https://api.hoalife.com/api/inspections" \
-H "Authorization: Token sk_SUPERSECRET"
The above command returns JSON structured like this:
{
"data": [
{
"id": "1",
"type": "inspection",
"attributes": {
"id": 1,
"account_id": 2,
"state": "completed",
"completed_at": "2019-07-30T21:16:49.510Z",
"duration": 7488,
"created_at": "2019-07-30T21:16:49.194Z",
"updated_at": "2019-07-31T15:44:01.575Z",
"total_violations": 18,
"inspector": {
"name": "John Owner",
"email": "john-owner@hoalife.com"
}
},
"relationships": {}
},
{
"id": "2",
"type": "inspection",
"attributes": {
"id": 2,
"account_id": 3,
"state": "started",
"completed_at": null,
"duration": 0,
"created_at": "2019-08-05T14:45:21.291Z",
"updated_at": "2019-08-05T14:45:21.291Z",
"total_violations": 0,
"inspector": {
"name": "John Owner",
"email": "john-owner@hoalife.com"
}
},
"relationships": {}
}
],
"meta": {
"current_page": 1,
"total": 2,
"total_pages": 1
},
"links": {
"self": "/api/inspections?page=1",
"next": "/api/inspections",
"prev": "/api/inspections"
}
}
This endpoint retrieves all Inspections accessible by the API key. If the API key has access to multiple Accounts, all Inspections for all those accounts will be returned. Scope Inspections to a specific Account by adding an account_id query parameter.
HTTP Request
GET https://api.hoalife.com/api/inspections
Attributes
| Attribute | Type | Presence | Nullable | Description |
|---|---|---|---|---|
id |
Integer | Always | No | Unique identifier of the Inspection |
duration |
Integer | Always | No | Number of seconds between the Inspection's created_at and completed_at values |
account_id |
Integer | Always | No | Unique identifier of Account the Inspection belongs to |
state |
String | Always | Yes | Allowed values: "started", "abandoned", "completed". |
completed_at |
String | Always | Yes | Date the Inspection was completed Format: date-time. |
created_at |
String | Always | No | Date and time the record was created. Format: date-time. |
updated_at |
String | Always | No | Date and time the record was last updated. Format: date-time. |
total_violations |
Integer | Always | No | The number of Violations recorded in the Inspection |
inspector |
Object | Always | No | Name and email address of the inspector. |
inspector.name |
String | Always | No | — |
inspector.email |
String | Always | No | — |
account_external_id |
String | Always | Yes | External identifier of the associated account. |
Nested presence applies when its parent object is present.
Query Parameters
| Parameter | Query By | Order By |
|---|---|---|
| id | true | true |
| account_id | true | true |
| completed_at | true | true |
| duration | true | true |
| total_violations | false | false |
Violations
Violations represent a recorded infraction of a Violation Type for given Property.
Get All Violations
curl "https://api.hoalife.com/api/violations" \
-H "Authorization: Token sk_SUPERSECRET"
The above command returns JSON structured like this:
{
"data": [
{
"id": "1",
"type": "violation",
"attributes": {
"id": 1,
"ccr_violation_type_id": 5,
"comment": "Very bad stuff, here",
"external_id": null,
"violated_at": "2019-06-15T12:04:16.111Z",
"cure_by": "2019-06-25T12:04:16.111Z",
"created_at": "2019-07-30T21:16:48.873Z",
"updated_at": "2019-07-31T15:44:01.522Z",
"property_id": 56,
"inspection_id": null,
"upload_urls": [],
"consecutive_violation_count": 1,
"inspector": {
"name": "John Owner",
"email": "john-owner@hoalife.com"
}
},
"relationships": {}
},
...
{
"id": "4",
"type": "violation",
"attributes": {
"id": 4,
"ccr_violation_type_id": 1,
"comment": "",
"external_id": null,
"violated_at": "2019-07-30T22:07:17.135Z",
"cure_by": null,
"created_at": "2019-07-30T22:07:17.135Z",
"updated_at": "2019-07-31T15:44:01.634Z",
"property_id": 28,
"inspection_id": null,
"upload_urls": [
{
"url": "https://hoal-subdomain.s3.us-west-2.amazonaws.com/aaff3c01-3753-43c7-bc37-98c092e7ab2c/camper.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAJSPPTZMGN7FDW4AQ%2F20190805%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20190805T145317Z&X-Amz-Expires=604800&X-Amz-SignedHeaders=host&X-Amz-Signature=999c66db8f6e182793b921989b8724160a45cc973f706f4f7bc56e5bcc9c08eb",
"url_expiration": "2019-08-06T14:53:17.301Z"
}
],
"consecutive_violation_count": 1,
"inspector": {
"name": "John Owner",
"email": "john-owner@hoalife.com"
}
},
"relationships": {}
}
],
"meta": {
"current_page": 1,
"total": 4,
"total_pages": 1
},
"links": {
"self": "/api/violations?page=1",
"next": "/api/violations",
"prev": "/api/violations"
}
}
This endpoint retrieves all Violations accessible by the API key. If the API key has access to multiple Accounts, all Violations for all those accounts will be returned. Scope Violations to a specific Account by adding an account_id query parameter.
HTTP Request
GET https://api.hoalife.com/api/violations
Attributes
| Attribute | Type | Presence | Nullable | Description |
|---|---|---|---|---|
id |
Integer | Always | No | Unique identifier of the Violation |
comment |
String | Always | Yes | A comment added by the inspector |
external_id |
String | Always | Yes | A unique identifier from an external system used to map the record |
violated_at |
String | Always | No | Date the infraction occured in ISO8601 format Defaults to the current time when omitted on create. Format: date-time. |
created_at |
String | Always | No | Date and time the record was created. Format: date-time. |
updated_at |
String | Always | No | Date and time the record was last updated. Format: date-time. |
cure_by |
String | Always | Yes | Date the violation should be cured by in ISO8601 Format: date-time. |
property_id |
Integer | Always | No | Unique identifier of Property the Violation belongs to Property on create; may instead be provided through the property relationship. |
account_id |
Integer | Always | No | Unique identifier of Account the Violation belongs to |
account_external_id |
String | Always | Yes | External identifier of the associated account. |
property_external_id |
String | Always | Yes | External identifier of the associated property. |
tags |
Array[Object] | Always | No | Tags associated with this record. |
tags[] |
Object | Each item | No | — |
tags[].id |
String | Always | No | — |
tags[].title |
String | Always | No | — |
tags[].color |
String | Always | No | — |
ccr_violation_type_id |
Integer | Always | No | Unique identifier of CCR Violation Type the Violation belongs to Violation type on create; may instead be provided through the ccr_violation_type relationship. |
inspection_id |
Integer | Always | Yes | Unique identifier of Inspection the Violation belongs to |
upload_urls |
Array[Object] | Always | No | Image uploads associated with the violation. Write inputs are publicly accessible URLs; responses contain URL details. |
upload_urls[] |
Object | Each item | No | — |
upload_urls[].url |
String | Always | No | — |
upload_urls[].url_expiration |
String | Always | No | Format: date-time. |
consecutive_violation_count |
Integer | Always | Yes | Number of consecutive notices for this violation type and property. |
inspector |
Object | Always | No | Name and email address of the inspector. |
inspector.name |
String | Always | No | — |
inspector.email |
String | Always | No | — |
closed |
Boolean | Conditional | Yes | Whether the violation is closed. Included when violation closing is enabled for the account. |
closed_at |
String | Conditional | Yes | When the violation was closed. Included when violation closing is enabled for the account. Format: date-time. |
Nested presence applies when its parent object is present.
Query Parameters
| Parameter | Query By | Order By |
|---|---|---|
| id | true | true |
| account_id | true | true |
| property_id | true | true |
| ccr_violation_type_id | true | true |
| inspection_id | true | true |
| violated_at | true | true |
| cure_by | true | true |
| comment | true | true |
| external_id | true | true |
Create Violation
curl "https://api.hoalife.com/api/violations" \
-X POST \
-H "Authorization: Token sk_SUPERSECRET" \
-H "Content-Type: application/vnd.api+json" \
-d '
{
"data": {
"type": "violation",
"attributes": {
"comment": "",
"external_id": "123",
"violated_at": "2019-07-30T22:07:17.135Z",
"upload_urls": [
"https://foobar.com/image.jpg"
]
},
"relationships": {
"ccr_violation_type": {
"data": {
"type": "ccr_violation_type",
"id": 1
}
},
"property": {
"data": {
"type": "property",
"id": 2
}
},
"inspector": {
"data": {
"type": "user",
"email": "user-with-access@example.com"
}
}
}
}
}
'
The above command returns JSON structured like this:
{
"data": {
"id": "4",
"type": "violation",
"attributes": {
"id": 4,
"ccr_violation_type_id": 1,
"comment": "",
"external_id": "123",
"violated_at": "2019-07-30T22:07:17.135Z",
"created_at": "2019-07-30T22:07:17.135Z",
"updated_at": "2019-07-31T15:44:01.634Z",
"property_id": 2,
"inspection_id": null,
"upload_urls": [],
"consecutive_violation_count": 1,
"inspector": {
"name": "John User With Access",
"email": "user-with-access@example.com"
}
},
},
"relationships": {}
}
}
This endpoint creates a new Violation.
HTTP Request
POST https://api.hoalife.com/api/violations
Writable Attributes
| Attribute | Type | Required | Nullable | Description |
|---|---|---|---|---|
comment |
String | No | Yes | A comment added by the inspector |
external_id |
String | No | Yes | A unique identifier from an external system used to map the record |
violated_at |
String | No | No | Date the infraction occured in ISO8601 format Defaults to the current time when omitted on create. Format: date-time. |
cure_by |
String | No | Yes | Date the violation should be cured by in ISO8601 Format: date-time. |
property_id |
Integer or String | No | No | Unique identifier of Property the Violation belongs to Property on create; may instead be provided through the property relationship. |
inspector_email |
String | No | No | Email of a HOALife User who has access to the Property Inspector on create; may instead be provided through the inspector relationship. |
ccr_violation_type_id |
Integer or String | No | No | Unique identifier of CCR Violation Type the Violation belongs to Violation type on create; may instead be provided through the ccr_violation_type relationship. |
upload_urls |
Array[String] | No | No | Image uploads associated with the violation. Write inputs are publicly accessible URLs; responses contain URL details. |
upload_urls[] |
String | Each item | No | — |
Required refers to unconditional presence. See descriptions for conditional requirements. Nested requirements apply when the parent object is supplied.
Note when passing upload_urls: URLs passed are validated and copied in a background process. The responding JSON may not contain the uploads until this completes. This may take many seconds depending on the amount of data that must be copied.
Update Violation
curl "https://api.hoalife.com/api/violations/42" \
-X PUT \
-H "Authorization: Token sk_SUPERSECRET" \
-H "Content-Type: application/vnd.api+json" \
-d '
{
"data": {
"type": "violation",
"attributes": {
"comment": "Dead tree(s) needs to be replaced",
"upload_urls": [
"https://foobar.com/image.jpg"
]
}
}
}
'
The above command will return updated JSON, just like for the Create action with a successful status code of
201 CREATED.
This endpoint updates an existing Violation.
HTTP Request
PUT https://api.hoalife.com/api/violations/:violation_id
URL Parameters
| Parameter | Type | Description |
|---|---|---|
| :violation_id | Integer | Unique identifier of the Violation the request is being made for |
Editable Attributes
| Attribute | Type | Required | Nullable | Description |
|---|---|---|---|---|
comment |
String | No | Yes | A comment added by the inspector |
external_id |
String | No | Yes | A unique identifier from an external system used to map the record |
cure_by |
String | No | Yes | Date the violation should be cured by in ISO8601 Format: date-time. |
upload_urls |
Array[String] | No | No | Image uploads associated with the violation. Write inputs are publicly accessible URLs; responses contain URL details. |
upload_urls[] |
String | Each item | No | — |
Required refers to unconditional presence. See descriptions for conditional requirements. Nested requirements apply when the parent object is supplied.
Note when passing upload_urls: URLs passed are validated and copied in a background process. The responding JSON may not contain the uploads until this completes. This may take many seconds depending on the amount of data that must be copied.
Close Violation
curl "https://api.hoalife.com/api/violations/42/close" \
-X POST \
-H "Authorization: Token sk_SUPERSECRET" \
-H "Content-Type: application/vnd.api+json" \
-d '
{
"data": {
"attributes": {
"remarks": "Repairs have been verified"
}
}
}
'
The response contains the updated Violation, including its closed and closed_at attributes when violation closing is enabled for the account. The remarks attribute is optional. Closing is available to API credentials with update permission when the Violation is open, violation closing is enabled, and it has no subsequent violations.
HTTP Request
POST https://api.hoalife.com/api/violations/:violation_id/close
URL Parameters
| Parameter | Type | Description |
|---|---|---|
| :violation_id | Integer | Unique identifier of the Violation the request is being made for |
Writable Attributes
| Parameter | Type | Description | Required |
|---|---|---|---|
| remarks | String | Optional HTML remarks recorded when closing the Violation. The content is sanitized before it is saved. | false |
Destroy Violation
curl "https://api.hoalife.com/api/violations/42" \
-X DELETE \
-H "Authorization: Token sk_SUPERSECRET" \
-H "Content-Type: application/vnd.api+json"
The above command will return an empty response successful response with a successful status code of
202 Accepted.
This endpoint destroys an existing Violation. Because Violation objects have many dependent resources, the actual deletion of the data is performed in a background process. This means that the API may continue to return data for the Violation even after you've requested a it be deleted.
HTTP Request
DELETE https://api.hoalife.com/api/violations/:violation_id
URL Parameters
| Parameter | Type | Description |
|---|---|---|
| :violation_id | Integer | Unique identifier of the Violation the request is being made for |
Escalations
Escalations represent a group of Violations which have contributed to exceeding the Account's escalation_threshold.
Get All Escalations
curl "https://api.hoalife.com/api/escalations" \
-H "Authorization: Token sk_SUPERSECRET"
The above command returns JSON structured like this:
{
"data": [
{
"id": "1",
"type": "escalation",
"attributes": {
"id": 1,
"state": "resolved",
"resolved_at": "2019-08-03T24:16:22.118Z",
"account_id": 2,
"property_id": 56,
"created_at": "2019-07-30T21:16:49.818Z",
"updated_at": "2019-07-31T15:40:39.125Z",
"violations": [
{
"id": 1,
"triggered_escalation": false,
"link": "/api/violations?id=1"
},
{
"id": 2,
"triggered_escalation": false,
"link": "/api/violations?id=2"
},
{
"id": 3,
"triggered_escalation": true,
"link": "/api/violations?id=3"
}
],
"resolver": {
"name": "John Owner",
"email": "john-owner@hoalife.com"
},
"notes": [
{
"body_text": "Confirmed property is currently not compliant \n \nhttps://app.hoalife.com/authenticated_uploads/Pn1YWIZjUghE0+B3cCG+CG8e1b0nPX%2FQAkm2soWSu3f4lDHvZoVck7VU39hp--Jzsz3YIEJ%2Fs2oru1--3+77KoR+M6S96SqFMVD8Tw==",
"author": "John Owner",
"pinned": false,
"created_at": "2019-08-05T16:08:24.542Z"
}
]
},
"relationships": {}
},
{
"id": "2",
"type": "escalation",
"attributes": {
"id":2,
"state": "in_process",
"resolved_at": null,
"account_id": 2,
"property_id": 56,
"created_at": "2019-07-30T21:16:49.818Z",
"updated_at": "2019-07-31T15:40:39.125Z",
"violations": [
{
"id": 4,
"triggered_escalation": false,
"link": "/api/violations?id=4"
},
{
"id": 5,
"triggered_escalation": false,
"link": "/api/violations?id=5"
},
{
"id": 6,
"triggered_escalation": true,
"link": "/api/violations?id=6"
}
],
"resolver": {
"name": null,
"email": null
},
"notes": []
},
"relationships": {}
}
],
"meta": {
"current_page": 1,
"total": 2,
"total_pages": 1
},
"links": {
"self": "/api/escalations?page=1",
"next": "/api/escalations",
"prev": "/api/escalations"
}
}
This endpoint retrieves all Escalations accessible by the API key. If the API key has access to multiple Accounts, all Escalations for all those accounts will be returned. Scope Escalations to a specific Account by adding an account_id query parameter.
HTTP Request
GET https://api.hoalife.com/api/escalations
Attributes
| Attribute | Type | Presence | Nullable | Description |
|---|---|---|---|---|
id |
Integer | Always | No | Unique identifier of the Escalation |
account_id |
Integer | Always | No | Unique identifier of Account the Escalation belongs to |
property_id |
Integer | Always | No | Unique identifier of Property the Escalation belongs to |
violation_type_id |
Integer | Always | Yes | — |
state |
String | Always | No | State of the Escalation Allowed values: "in_process", "snoozed", "resolved". |
resolved_at |
String | Always | Yes | Date the Escalation was marked as resolved Format: date-time. |
created_at |
String | Always | No | Date and time the record was created. Format: date-time. |
updated_at |
String | Always | No | Date and time the record was last updated. Format: date-time. |
violations |
Array[Object] | Always | No | Information pertaining to the violations associated with the Escalation |
violations[] |
Object | Each item | No | — |
violations[].id |
Integer | Always | No | — |
violations[].triggered_escalation |
Boolean | Always | No | — |
violations[].link |
String | Always | No | — |
account_external_id |
String | Always | Yes | External identifier of the associated account. |
property_external_id |
String | Always | Yes | External identifier of the associated property. |
resolver |
Object | Always | No | Information pertaining to the user who marked the Escalation as resolved |
resolver.name |
String | Always | Yes | — |
resolver.email |
String | Always | Yes | — |
notes |
Array[Object] | Always | No | Escalation notes |
notes[] |
Object | Each item | No | — |
notes[].id |
Integer | Always | No | — |
notes[].body_text |
String | Always | No | — |
notes[].author |
String | Always | No | — |
notes[].pinned |
Boolean | Always | No | — |
notes[].created_at |
String | Always | No | Format: date-time. |
Nested presence applies when its parent object is present.
Query Parameters
| Parameter | Query By | Order By |
|---|---|---|
| id | true | true |
| account_id | true | true |
| property_id | true | true |
| state | true | true |
| resolved_at | true | true |
| violations | false | false |
| resolver | false | false |
| notes | false | false |
Deficiencies
Deficiencies represent a recorded flaw for a given Asset.
Get All Deficiencies
curl "https://api.hoalife.com/api/deficiencies" \
-H "Authorization: Token sk_SUPERSECRET"
The above command returns JSON structured like this:
{
"data": [
{
"id": "1",
"type": "deficiency",
"attributes": {
"id": 1,
"description": "Pool circulation pump is non-functional",
"details": "Pool circulation pump does not turn on. No indications of power",
"state": "resolved",
"external_id": null,
"resolved_at": "2019-06-15T12:04:16.111Z",
"created_at": "2019-07-30T21:16:48.873Z",
"updated_at": "2019-07-31T15:44:01.522Z",
"asset_id": 56,
"inspection_id": null,
"upload_urls": [],
"formatted_id": "DEF-1"
},
"relationships": {}
},
...
{
"id": "4",
"type": "deficiency",
"attributes": {
"id": 4,
"description": "Entry light out",
"details": "West entry light is out",
"state": "identified",
"external_id": null,
"resolved_at": null,
"created_at": "2019-07-30T22:07:17.135Z",
"updated_at": "2019-07-31T15:44:01.634Z",
"asset_id": 28,
"inspection_id": 4,
"upload_urls": [
{
"url": "https://hoal-subdomain.s3.us-west-2.amazonaws.com/aaff3c01-3753-43c7-bc37-98c092e7ab2c/camper.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAJSPPTZMGN7FDW4AQ%2F20190805%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20190805T145317Z&X-Amz-Expires=604800&X-Amz-SignedHeaders=host&X-Amz-Signature=999c66db8f6e182793b921989b8724160a45cc973f706f4f7bc56e5bcc9c08eb",
"url_expiration": "2019-08-06T14:53:17.301Z"
}
],
"formatted_id": "DEF-4"
},
"relationships": {}
}
],
"meta": {
"current_page": 1,
"total": 4,
"total_pages": 1
},
"links": {
"self": "/api/deficiencies?page=1",
"next": "/api/deficiencies",
"prev": "/api/deficiencies"
}
}
This endpoint retrieves all Deficiencies accessible by the API key. If the API key has access to multiple Accounts, all Deficiencies for all those accounts will be returned. Scope Deficiencies to a specific Account by adding an account_id query parameter.
HTTP Request
GET https://api.hoalife.com/api/deficiencies
Attributes
| Attribute | Type | Presence | Nullable | Description |
|---|---|---|---|---|
id |
Integer | Always | No | Unique identifier of the Deficiency |
account_id |
Integer | Always | No | Unique identifier of Account the Deficiency belongs to |
state |
String | Always | No | Current state of the deficiency Allowed values: "identified", "snoozed", "resolved". |
description |
String | Always | No | A short description added by the inspector |
details |
String | Always | No | A long details about the deficiencies added by the inspector |
resolved_at |
String | Always | Yes | Date the deficiency was resolved Format: date-time. |
asset_id |
Integer | Always | No | Unique identifier of Asset the Deficiency belongs to Asset on create; may instead be provided through the asset relationship. |
created_at |
String | Always | No | Date and time the record was created. Format: date-time. |
updated_at |
String | Always | No | Date and time the record was last updated. Format: date-time. |
external_id |
String | Always | Yes | A unique identifier from an external system used to map the record |
inspection_id |
Integer | Always | Yes | Unique identifier of Inspection the Deficiency belongs to |
formatted_id |
String | Always | Yes | Humanized unique identifier |
tags |
Array[Object] | Always | No | Tags associated with this record. |
tags[] |
Object | Each item | No | — |
tags[].id |
String | Always | No | — |
tags[].title |
String | Always | No | — |
tags[].color |
String | Always | No | — |
account_external_id |
String | Always | Yes | External identifier of the associated account. |
asset_external_id |
String | Always | Yes | — |
upload_urls |
Array[Object] | Always | No | Image uploads associated with the Deficiency |
upload_urls[] |
Object | Each item | No | — |
upload_urls[].url |
String | Always | No | — |
upload_urls[].url_expiration |
String | Always | No | Format: date-time. |
notes |
Array[Object] | Always | No | Notes associated with this record. |
notes[] |
Object | Each item | No | — |
notes[].id |
Integer | Always | No | — |
notes[].body_text |
String | Always | No | — |
notes[].author_name |
String | Always | No | — |
notes[].pinned |
Boolean | Always | No | — |
notes[].created_at |
String | Always | No | Format: date-time. |
Nested presence applies when its parent object is present.
Query Parameters
| Parameter | Query By | Order By |
|---|---|---|
| id | true | true |
| account_id | true | true |
| state | true | true |
| asset_id | true | true |
| inspection_id | true | true |
| resolved_at | true | true |
| description | true | true |
| details | true | true |
| external_id | true | true |
| formatted_id | true | true |
Create Deficiency
curl "https://api.hoalife.com/api/deficiencies" \
-X POST \
-H "Authorization: Token sk_SUPERSECRET" \
-H "Content-Type: application/vnd.api+json" \
-d '
{
"data": {
"type": "deficiency",
"attributes": {
"description": "Foo",
"details": "Bar",
"external_id": "123",
"state": "resolved"
"resolved_at": "2019-07-30T22:07:17.135Z",
},
"relationships": {
"asset": {
"data": {
"type": "asset",
"id": 2
}
}
}
}
}
'
The above command returns JSON structured like this:
{
"data": {
"id": "4",
"type": "deficiency",
"attributes": {
"id": 4,
"description": "Foo",
"details": "Bar",
"external_id": "123",
"resolved_at": "2019-07-30T22:07:17.135Z",
"created_at": "2019-07-30T22:07:17.135Z",
"updated_at": "2019-07-31T15:44:01.634Z",
"asset_id": 2,
"inspection_id": null,
"upload_urls": [],
},
},
"relationships": {}
}
}
This endpoint creates a new Deficiency.
HTTP Request
POST https://api.hoalife.com/api/deficiencies
Deficiency Writable Attributes
| Attribute | Type | Required | Nullable | Description |
|---|---|---|---|---|
state |
String | No | No | Current state of the deficiency Allowed values: "identified", "snoozed", "resolved". |
description |
String | Yes | No | A short description added by the inspector |
details |
String | Yes | No | A long details about the deficiencies added by the inspector |
resolved_at |
String | No | Yes | Date the deficiency was resolved Format: date-time. |
asset_id |
Integer or String | No | No | Unique identifier of Asset the Deficiency belongs to Asset on create; may instead be provided through the asset relationship. |
external_id |
String | No | Yes | A unique identifier from an external system used to map the record |
Required refers to unconditional presence. See descriptions for conditional requirements. Nested requirements apply when the parent object is supplied.
Update Deficiency
curl "https://api.hoalife.com/api/deficiencies/42" \
-X PUT \
-H "Authorization: Token sk_SUPERSECRET" \
-H "Content-Type: application/vnd.api+json" \
-d '
{
"data": {
"type": "deficiency",
"attributes": {
"details": "Dead tree(s) needs to be replaced"
}
}
}
'
The above command will return updated JSON, just like for the Create action with a successful status code of
201 CREATED.
This endpoint updates an existing Deficiency.
HTTP Request
PUT https://api.hoalife.com/api/deficiencies/:deficiency_id
URL Parameters
| Parameter | Type | Description |
|---|---|---|
| :deficiency_id | Integer | Unique identifier of the Deficiency the request is being made for |
Editable Attributes
| Attribute | Type | Required | Nullable | Description |
|---|---|---|---|---|
state |
String | No | No | Current state of the deficiency Allowed values: "identified", "snoozed", "resolved". |
description |
String | No | No | A short description added by the inspector |
details |
String | No | No | A long details about the deficiencies added by the inspector |
resolved_at |
String | No | Yes | Date the deficiency was resolved Format: date-time. |
external_id |
String | No | Yes | A unique identifier from an external system used to map the record |
Required refers to unconditional presence. See descriptions for conditional requirements. Nested requirements apply when the parent object is supplied.
Destroy Deficiency
curl "https://api.hoalife.com/api/deficiencies/42" \
-X DELETE \
-H "Authorization: Token sk_SUPERSECRET" \
-H "Content-Type: application/vnd.api+json"
The above command will return an empty response successful response with a successful status code of
202 Accepted.
This endpoint destroys an existing Deficiency. Because Deficiency objects have many dependent resources, the actual deletion of the data is performed in a background process. This means that the API may continue to return data for the Deficiency even after you've requested a it be deleted.
HTTP Request
DELETE https://api.hoalife.com/api/deficiencies/:deficiency_id
URL Parameters
| Parameter | Type | Description |
|---|---|---|
| :deficiency_id | Integer | Unique identifier of the Deficiency the request is being made for |
ACC Request Forms
ACC Request Forms define the information collected for architectural change requests.
Get All ACC Request Forms
curl "https://api.hoalife.com/api/acc_request_forms" \
-H "Authorization: Token sk_SUPERSECRET"
The above command returns JSON structured like this:
{
"data": [
{
"id": "42",
"type": "acc_request_form",
"attributes": {
"id": 42,
"name": "Exterior Changes",
"description": "Describe your planned changes.",
"created_at": "2025-01-01T12:00:00.000Z",
"updated_at": "2025-01-01T12:00:00.000Z",
"account_id": 123
},
"relationships": {}
}
],
"meta": {
"current_page": 1,
"total": 1,
"total_pages": 1
},
"links": {
"self": "/api/acc_request_forms?page=1",
"next": "/api/acc_request_forms",
"prev": "/api/acc_request_forms"
}
}
Returns enabled forms defined for the API key's account, its descendant accounts, and ancestor accounts whose forms are inherited. Requirements are available from the ACC request requirements endpoint and refer to their form with request_type_id.
The API key must have read permission for acc_request_forms.
HTTP Request
GET https://api.hoalife.com/api/acc_request_forms
Attributes
| Attribute | Type | Presence | Nullable | Description |
|---|---|---|---|---|
id |
Integer | Always | No | Unique identifier of the ACC Request Form |
name |
String | Always | No | Name of the form |
description |
String | Always | No | Description of the form |
created_at |
String | Always | No | Date and time the record was created. Format: date-time. |
updated_at |
String | Always | No | Date and time the record was last updated. Format: date-time. |
account_id |
Integer | Always | No | Unique identifier of the Account the form belongs to. Pass it in data.relationships.account.data.id or data.attributes.account_id Required on create unless supplied through the account relationship. |
Nested presence applies when its parent object is present.
Query Parameters
| Parameter | Query By | Order By |
|---|---|---|
| id | true | true |
| account_id | true | false |
| name | true | true |
Create ACC Request Form
curl "https://api.hoalife.com/api/acc_request_forms" \
-X POST \
-H "Authorization: Token sk_SUPERSECRET" \
-H "Content-Type: application/vnd.api+json" \
-d '
{
"data": {
"type": "acc_request_form",
"attributes": {
"name": "Exterior Changes",
"description": "Describe your planned changes."
},
"relationships": {
"account": {
"data": {
"type": "account",
"id": 123
}
}
}
}
}
'
The above command returns JSON structured like this:
{
"data": {
"id": "42",
"type": "acc_request_form",
"attributes": {
"id": 42,
"name": "Exterior Changes",
"description": "Describe your planned changes.",
"created_at": "2025-01-01T12:00:00.000Z",
"updated_at": "2025-01-01T12:00:00.000Z",
"account_id": 123
},
"relationships": {}
}
}
This endpoint creates a new ACC Request Form. The API key must have create permission for acc_request_forms. The account must be the API key's account or one of its descendants.
HTTP Request
POST https://api.hoalife.com/api/acc_request_forms
ACC Request Form Writable Attributes
| Attribute | Type | Required | Nullable | Description |
|---|---|---|---|---|
name |
String | Yes | No | Name of the form |
description |
String | Yes | No | Description of the form |
account_id |
Integer or String | No | No | Unique identifier of the Account the form belongs to. Pass it in data.relationships.account.data.id or data.attributes.account_id Required on create unless supplied through the account relationship. |
Required refers to unconditional presence. See descriptions for conditional requirements. Nested requirements apply when the parent object is supplied.
Update ACC Request Form
curl "https://api.hoalife.com/api/acc_request_forms/42" \
-X PUT \
-H "Authorization: Token sk_SUPERSECRET" \
-H "Content-Type: application/vnd.api+json" \
-d '
{
"data": {
"type": "acc_request_form",
"attributes": {
"name": "Exterior Changes",
"description": "Describe your planned changes."
}
}
}
'
The above command will return updated JSON, just like for the Create action with a successful status code of
200 OK.
This endpoint updates an existing ACC Request Form. The API key must have update permission for acc_request_forms. The form must be owned by the API key's account or a descendant account.
HTTP Request
PUT https://api.hoalife.com/api/acc_request_forms/:id
URL Parameters
| Parameter | Type | Description |
|---|---|---|
| :id | Integer | Unique identifier of the ACC Request Form the request is being made for |
Editable Attributes
| Attribute | Type | Required | Nullable | Description |
|---|---|---|---|---|
name |
String | No | No | Name of the form |
description |
String | No | No | Description of the form |
Required refers to unconditional presence. See descriptions for conditional requirements. Nested requirements apply when the parent object is supplied.
Destroy ACC Request Form
curl "https://api.hoalife.com/api/acc_request_forms/42" \
-X DELETE \
-H "Authorization: Token sk_SUPERSECRET" \
-H "Content-Type: application/vnd.api+json"
The above command will return an empty response with a successful status code of
202 Accepted.
This endpoint destroys an existing ACC Request Form. The API key must have destroy permission for acc_request_forms. The form must be owned by the API key's account or a descendant account. Forms that already have requests are disabled to preserve their history. Otherwise, the actual deletion is performed in a background process, so the API may continue to return the form for a short time after you've requested it be deleted.
HTTP Request
DELETE https://api.hoalife.com/api/acc_request_forms/:id
URL Parameters
| Parameter | Type | Description |
|---|---|---|
| :id | Integer | Unique identifier of the ACC Request Form the request is being made for |
ACC Request Requirements
ACC Request Requirements define the fields shown on an ACC Request Form.
Get All ACC Request Requirements
curl "https://api.hoalife.com/api/acc_request_requirements" \
-H "Authorization: Token sk_SUPERSECRET"
The above command returns JSON structured like this:
{
"data": [
{
"id": "84",
"type": "acc_request_requirement",
"attributes": {
"id": 84,
"title": "Project description",
"description": "Describe the work.",
"input_type": "plain_text",
"options": null,
"include_other": false,
"optional": false,
"order": 1,
"request_type_id": 42
},
"relationships": {}
}
],
"meta": {
"current_page": 1,
"total": 1,
"total_pages": 1
},
"links": {
"self": "/api/acc_request_requirements?page=1",
"next": "/api/acc_request_requirements",
"prev": "/api/acc_request_requirements"
}
}
Returns enabled requirements for enabled forms in the API key's account tree, including inherited ancestor forms.
The API key must have read permission for acc_request_requirements.
HTTP Request
GET https://api.hoalife.com/api/acc_request_requirements
Attributes
| Attribute | Type | Presence | Nullable | Description |
|---|---|---|---|---|
id |
Integer | Always | No | Unique identifier of the requirement |
title |
String | Always | No | Title of the requirement |
description |
String | Always | No | Description of the requirement. May be an empty string |
input_type |
String | Always | No | Type of input. Allowed values: "rich_text", "plain_text", "dropdown", "radio", "checkbox", "file_upload", "date", "date_range", "signature", "payment". |
options |
String or Number or Boolean | Always | Yes | Newline-separated choices for dropdown, radio, or checkbox inputs Normally newline-delimited choices; numeric and boolean scalar inputs are retained unchanged. |
include_other |
Boolean | Always | Yes | Whether to include an “other” option for choice inputs |
optional |
Boolean | Always | Yes | Whether the requester may skip this requirement |
order |
Integer | Always | Yes | Display order. If omitted or null, the next order is assigned |
request_type_id |
Integer | Always | No | Unique identifier of the form the requirement belongs to. Pass it in data.relationships.request_type.data.id or data.attributes.request_type_id Required on create unless supplied through the request_type relationship. |
Nested presence applies when its parent object is present.
Query Parameters
| Parameter | Query By | Order By |
|---|---|---|
| id | true | true |
| account_id | true | false |
| request_type_id | true | true |
| title | true | true |
| input_type | true | true |
| optional | true | true |
| order | false | true |
Create ACC Request Requirement
curl "https://api.hoalife.com/api/acc_request_requirements" \
-X POST \
-H "Authorization: Token sk_SUPERSECRET" \
-H "Content-Type: application/vnd.api+json" \
-d '
{
"data": {
"type": "acc_request_requirement",
"attributes": {
"title": "Project description",
"description": "Describe the work.",
"input_type": "plain_text",
"include_other": false,
"optional": false,
"order": 1
},
"relationships": {
"request_type": {
"data": {
"type": "acc_request_form",
"id": 42
}
}
}
}
}
'
The above command returns JSON structured like this:
{
"data": {
"id": "84",
"type": "acc_request_requirement",
"attributes": {
"id": 84,
"title": "Project description",
"description": "Describe the work.",
"input_type": "plain_text",
"options": null,
"include_other": false,
"optional": false,
"order": 1,
"request_type_id": 42
},
"relationships": {}
}
}
This endpoint creates a new ACC Request Requirement. The API key must have create permission for acc_request_requirements. The form must belong to the API key's account or one of its descendants.
HTTP Request
POST https://api.hoalife.com/api/acc_request_requirements
ACC Request Requirement Writable Attributes
| Attribute | Type | Required | Nullable | Description |
|---|---|---|---|---|
title |
String | Yes | No | Title of the requirement |
description |
String | Yes | No | Description of the requirement. May be an empty string |
input_type |
String or Integer | No | No | Type of input. Allowed values: "rich_text", "plain_text", "dropdown", "radio", "checkbox", "file_upload", "date", "date_range", "signature", "payment", 0, 10, 1000, 1001, 10000, 100000, 200000, 200001, 300000, 400000. Default: "rich_text". |
options |
String or Number or Boolean | No | Yes | Newline-separated choices for dropdown, radio, or checkbox inputs Normally newline-delimited choices; numeric and boolean scalar inputs are retained unchanged. |
include_other |
Boolean | No | Yes | Whether to include an “other” option for choice inputs |
optional |
Boolean | No | Yes | Whether the requester may skip this requirement |
order |
Integer | No | Yes | Display order. If omitted or null, the next order is assigned |
request_type_id |
Integer or String | No | No | Unique identifier of the form the requirement belongs to. Pass it in data.relationships.request_type.data.id or data.attributes.request_type_id Required on create unless supplied through the request_type relationship. |
Required refers to unconditional presence. See descriptions for conditional requirements. Nested requirements apply when the parent object is supplied.
Update ACC Request Requirement
curl "https://api.hoalife.com/api/acc_request_requirements/84" \
-X PUT \
-H "Authorization: Token sk_SUPERSECRET" \
-H "Content-Type: application/vnd.api+json" \
-d '
{
"data": {
"type": "acc_request_requirement",
"attributes": {
"title": "Work description",
"optional": true
}
}
}
'
The above command will return updated JSON, just like for the Create action with a successful status code of
200 OK.
This endpoint updates an existing ACC Request Requirement. The API key must have update permission for acc_request_requirements. Its form must be owned by the API key's account or a descendant account.
HTTP Request
PUT https://api.hoalife.com/api/acc_request_requirements/:id
URL Parameters
| Parameter | Type | Description |
|---|---|---|
| :id | Integer | Unique identifier of the ACC Request Requirement the request is being made for |
Editable Attributes
| Attribute | Type | Required | Nullable | Description |
|---|---|---|---|---|
title |
String | No | No | Title of the requirement |
description |
String | No | No | Description of the requirement. May be an empty string |
input_type |
String or Integer | No | No | Type of input. Allowed values: "rich_text", "plain_text", "dropdown", "radio", "checkbox", "file_upload", "date", "date_range", "signature", "payment", 0, 10, 1000, 1001, 10000, 100000, 200000, 200001, 300000, 400000. |
options |
String or Number or Boolean | No | Yes | Newline-separated choices for dropdown, radio, or checkbox inputs Normally newline-delimited choices; numeric and boolean scalar inputs are retained unchanged. |
include_other |
Boolean | No | Yes | Whether to include an “other” option for choice inputs |
optional |
Boolean | No | Yes | Whether the requester may skip this requirement |
order |
Integer | No | Yes | Display order. If omitted or null, the next order is assigned |
Required refers to unconditional presence. See descriptions for conditional requirements. Nested requirements apply when the parent object is supplied.
Destroy ACC Request Requirement
curl "https://api.hoalife.com/api/acc_request_requirements/84" \
-X DELETE \
-H "Authorization: Token sk_SUPERSECRET" \
-H "Content-Type: application/vnd.api+json"
The above command will return an empty response with a successful status code of
202 Accepted.
This endpoint destroys an existing ACC Request Requirement. The API key must have destroy permission for acc_request_requirements. Its form must be owned by the API key's account or a descendant account. The actual deletion is performed in a background process, so the API may continue to return the requirement for a short time after you've requested it be deleted.
HTTP Request
DELETE https://api.hoalife.com/api/acc_request_requirements/:id
URL Parameters
| Parameter | Type | Description |
|---|---|---|
| :id | Integer | Unique identifier of the ACC Request Requirement the request is being made for |
ACC Requests
ACC Requests represent architectural change requests submitted for properties.
Get All ACC Requests
curl "https://api.hoalife.com/api/acc_requests" \
-H "Authorization: Token sk_SUPERSECRET"
The above command returns JSON structured like this:
{
"data": [
{
"id": "256",
"type": "acc_request",
"attributes": {
"id": 256,
"formatted_id": "ARC-256",
"status": "submitted",
"status_last_changed_at": "2025-01-01T12:00:00.000Z",
"remarks": null,
"tags": [],
"created_at": "2025-01-01T12:00:00.000Z",
"updated_at": "2025-01-01T12:00:00.000Z",
"property_id": 456,
"request_type_id": 42,
"account_id": 123
},
"relationships": {}
}
],
"meta": {
"current_page": 1,
"total": 1,
"total_pages": 1
},
"links": {
"self": "/api/acc_requests?page=1",
"next": "/api/acc_requests",
"prev": "/api/acc_requests"
}
}
Returns ACC requests for the API key's account and descendant accounts. Request response values are available from the ACC request responses endpoint and refer to their request and requirement with request_id and request_requirement_id.
The API key must have read permission for acc_requests.
HTTP Request
GET https://api.hoalife.com/api/acc_requests
Attributes
| Attribute | Type | Presence | Nullable | Description |
|---|---|---|---|---|
id |
Integer | Always | No | Unique identifier of the ACC Request |
formatted_id |
String | Always | Yes | Humanized unique identifier |
status |
String | Always | Yes | Current request status Allowed values: "draft", "submitted", "approved", "conditionally_approved", "denied". |
status_last_changed_at |
String | Always | Yes | Date the status last changed Format: date-time. |
remarks |
String | Always | Yes | Remarks associated with the request |
tags |
Array[String] | Always | No | Tags associated with the request |
tags[] |
String | Each item | No | — |
created_at |
String | Always | No | Date the request was created Format: date-time. |
updated_at |
String | Always | No | Date the request was last updated Format: date-time. |
property_id |
Integer | Always | No | Unique identifier of the Property the request belongs to |
request_type_id |
Integer | Always | No | Unique identifier of the form used by the request |
account_id |
Integer | Always | No | Unique identifier of the Account the request belongs to |
Nested presence applies when its parent object is present.
Query Parameters
| Parameter | Query By | Order By |
|---|---|---|
| id | true | true |
| account_id | true | false |
| property_id | true | true |
| request_type_id | true | true |
| formatted_id | true | true |
| status | true | true |
| status_last_changed_at | false | true |
ACC Request Responses
ACC Request Responses contain answers submitted for ACC Request Requirements.
Get All ACC Request Responses
curl "https://api.hoalife.com/api/acc_request_responses" \
-H "Authorization: Token sk_SUPERSECRET"
The above command returns JSON structured like this:
{
"data": [
{
"id": "512",
"type": "acc_request_response",
"attributes": {
"id": 512,
"data": {"content": "The planned exterior changes are..."},
"skipped": false,
"request_id": 256,
"request_requirement_id": 84,
"created_at": "2025-01-01T12:00:00.000Z",
"updated_at": "2025-01-01T12:00:00.000Z",
"input_type": "plain_text"
},
"relationships": {}
}
],
"meta": {
"current_page": 1,
"total": 1,
"total_pages": 1
},
"links": {
"self": "/api/acc_request_responses?page=1",
"next": "/api/acc_request_responses",
"prev": "/api/acc_request_responses"
}
}
Returns responses for ACC requests in the API key's account and descendant accounts.
The API key must have read permission for acc_request_responses.
HTTP Request
GET https://api.hoalife.com/api/acc_request_responses
Attributes
| Attribute | Type | Presence | Nullable | Description |
|---|---|---|---|---|
id |
Integer | Always | No | Unique identifier of the response |
data |
Object | Always | Yes | Response value Response values vary by input_type; skipped responses may contain an empty object. Signature meta may contain arbitrary JSON. Additional keys may be present. |
data.content |
String | Optional | Yes | — |
data.value |
String | Optional | Yes | — |
data.values |
Array[String] | Optional | No | — |
data.values[] |
String | Each item | No | — |
data.other |
String | Optional | Yes | — |
data.upload_ids |
Array[String] | Optional | No | — |
data.upload_ids[] |
String | Each item | No | Format: uuid. |
data.start_date |
String | Optional | No | Date formatted as MM/DD/YYYY. |
data.end_date |
String | Optional | No | Date formatted as MM/DD/YYYY. |
data.stripe_payment_method_id |
String | Optional | No | — |
data.property_invoice_id |
Integer | Optional | No | — |
data.meta |
Any JSON value | Optional | Yes | — |
skipped |
Boolean | Always | Yes | Whether the requester skipped the requirement |
request_id |
Integer | Always | No | Unique identifier of the ACC Request the response belongs to |
request_requirement_id |
Integer | Always | No | Unique identifier of the requirement the response answers |
created_at |
String | Always | No | Date the response was created Format: date-time. |
updated_at |
String | Always | No | Date the response was last updated Format: date-time. |
input_type |
String | Always | No | Type of input used for the response |
Nested presence applies when its parent object is present.
Query Parameters
| Parameter | Query By | Order By |
|---|---|---|
| id | true | true |
| account_id | true | false |
| request_id | true | true |
| request_requirement_id | true | true |
| skipped | true | true |
ACC Request Votes
ACC Request Votes represent votes and pending vote requests attached to ACC Requests.
Get All ACC Request Votes
curl "https://api.hoalife.com/api/acc_request_votes" \
-H "Authorization: Token sk_SUPERSECRET"
The above command returns JSON structured like this:
{
"data": [
{
"id": "789",
"type": "acc_request_vote",
"attributes": {
"id": 789,
"status": "requested",
"comment": null,
"created_at": "2025-01-01T12:00:00.000Z",
"updated_at": "2025-01-01T12:00:00.000Z",
"voter_type": "User",
"voter_id": 456,
"request_id": 256
},
"relationships": {}
}
],
"meta": {
"current_page": 1,
"total": 1,
"total_pages": 1
},
"links": {
"self": "/api/acc_request_votes?page=1",
"next": "/api/acc_request_votes",
"prev": "/api/acc_request_votes"
}
}
Returns votes attached to ACC requests in the API key's account and descendant accounts. Votes for other resource types are not included.
The API key must have read permission for acc_request_votes.
HTTP Request
GET https://api.hoalife.com/api/acc_request_votes
Attributes
| Attribute | Type | Presence | Nullable | Description |
|---|---|---|---|---|
id |
Integer | Always | No | Unique identifier of the vote |
comment |
String | Always | Yes | Comment associated with the vote |
created_at |
String | Always | No | Date the vote was created Format: date-time. |
updated_at |
String | Always | No | Date the vote was last updated Format: date-time. |
voter_type |
String | Always | Yes | Type of voter |
voter_id |
Integer | Always | No | Unique identifier of the voter |
status |
String | Always | No | Vote status. Creates a vote request; omitted or blank input defaults to requested. Allowed values: "requested", "approve", "deny", "did_not_vote", "cast". |
request_id |
Integer | Always | No | Unique identifier of the submitted ACC Request |
Nested presence applies when its parent object is present.
Query Parameters
| Parameter | Query By | Order By |
|---|---|---|
| id | true | true |
| account_id | true | false |
| request_id | true | false |
| voter_id | true | true |
| status | true | true |
Request an ACC Request Vote
curl "https://api.hoalife.com/api/acc_request_votes" \
-X POST \
-H "Authorization: Token sk_SUPERSECRET" \
-H "Content-Type: application/vnd.api+json" \
-d '
{
"data": {
"type": "acc_request_vote",
"attributes": {
"request_id": 256,
"user_id": 456,
"status": "requested"
}
}
}
'
The above command returns JSON structured like this:
{
"data": {
"id": "789",
"type": "acc_request_vote",
"attributes": {
"id": 789,
"status": "requested",
"comment": null,
"created_at": "2025-01-01T12:00:00.000Z",
"updated_at": "2025-01-01T12:00:00.000Z",
"voter_type": "User",
"voter_id": 456,
"request_id": 256
},
"relationships": {}
}
}
This endpoint creates a pending vote request. The API key must have create permission for acc_request_votes. The user must have a non-read-only authorization in the ACC request's account or an ancestor account. The request must be submitted and within the API key's account scope. The API sends the same vote-request notifications as the dashboard workflow.
HTTP Request
POST https://api.hoalife.com/api/acc_request_votes
Writable Attributes
| Attribute | Type | Required | Nullable | Description |
|---|---|---|---|---|
status |
String | No | Yes | Vote status. Creates a vote request; omitted or blank input defaults to requested. Allowed values: "requested", "". |
request_id |
Integer or String | Yes | No | Unique identifier of the submitted ACC Request |
user_id |
Integer or String | Yes | No | Unique identifier of the User who should vote |
Required refers to unconditional presence. See descriptions for conditional requirements. Nested requirements apply when the parent object is supplied.
Revoke an ACC Request Vote Request
curl "https://api.hoalife.com/api/acc_request_votes/789" \
-X DELETE \
-H "Authorization: Token sk_SUPERSECRET" \
-H "Content-Type: application/vnd.api+json"
The above command will return an empty response with a successful status code of
202 Accepted.
This endpoint destroys a pending vote request. The API key must have destroy permission for acc_request_votes. Cast votes cannot be revoked through this endpoint. The vote must belong to an ACC request in the API key's account or a descendant account.
HTTP Request
DELETE https://api.hoalife.com/api/acc_request_votes/:id
URL Parameters
| Parameter | Type | Description |
|---|---|---|
| :id | Integer | Unique identifier of the ACC Request Vote the request is being made for |
Notes
Notes are attached to other data objects, including property, escalation, deficiency, and acc_request.
Create Note
curl "https://api.hoalife.com/api/notes" \
-X POST \
-H "Authorization: Token sk_SUPERSECRET" \
-H "Content-Type: application/vnd.api+json" \
-d '
{
"data": {
"type": "note",
"attributes": {
"body_text": "This is my very important note",
"pinned": true,
"attached_to_type": "acc_request",
"attached_to_id": 42,
"author_id": 1
}
}
}
'
The above command returns JSON structured like this:
{
"data": {
"id": "4",
"type": "note",
"attributes": {
"id": 4,
"pinned": true,
"auto_action": null,
"auto_action_at": null,
"author_id": 1,
"body_text": "This is my very important note",
"attached_to_type": "acc_request",
"attached_to_id": 42,
"created_at": "2019-07-30T21:16:47.712Z"
}
}
}
This endpoint creates a new Note.
HTTP Request
POST https://api.hoalife.com/api/notes
Response Attributes
| Attribute | Type | Presence | Nullable | Description |
|---|---|---|---|---|
id |
Integer | Always | No | — |
pinned |
Boolean | Always | No | Denote if the note should be pinned |
auto_action |
String | Always | Yes | Allowed values: "destroy", "remove_pin". |
auto_action_at |
String | Always | Yes | Format: date-time. |
created_at |
String | Always | No | Date and time the record was created. Format: date-time. |
author_id |
Integer | Always | No | ID of the User who authored the note. Supply a user with a role on the credential's account, a descendant, or an ancestor account, or pass null to select the default author. Omit on update to keep the current author. On create, omission uses the legacy author relationship or the default author. |
body_text |
String | Always | No | The note's text |
attached_to_type |
String | Always | No | Type of entity the note is attached to: property, escalation, deficiency, or acc_request. Allowed values: "property", "escalation", "deficiency", "acc_request". |
attached_to_id |
Integer | Always | No | ID of the existing entity identified by attached_to_type, within the credential's account scope; not the note's ID. |
Nested presence applies when its parent object is present.
Note Writable Attributes
| Attribute | Type | Required | Nullable | Description |
|---|---|---|---|---|
pinned |
Boolean | No | No | Denote if the note should be pinned |
author_id |
Integer | No | Yes | ID of the User who authored the note. Supply a user with a role on the credential's account, a descendant, or an ancestor account, or pass null to select the default author. Omit on update to keep the current author. On create, omission uses the legacy author relationship or the default author. |
body_text |
String | Yes | No | The note's text |
attached_to_type |
String | Yes | No | Type of entity the note is attached to: property, escalation, deficiency, or acc_request. Allowed values: "property", "escalation", "deficiency", "acc_request". |
attached_to_id |
Integer | Yes | No | ID of the existing entity identified by attached_to_type, within the credential's account scope; not the note's ID. |
Required refers to unconditional presence. See descriptions for conditional requirements. Nested requirements apply when the parent object is supplied.
Supply attached_to_type and attached_to_id in data.attributes. Supported types
are property, escalation, deficiency, and acc_request. The ID identifies an
existing target of that type within the credential's account scope, not a note.
Both attachment fields are advertised as required creation attributes by /api/schema.
Legacy clients can still supply data.relationships.attached_to.data.type and
id. Each legacy value is used only when the corresponding attribute key is
absent. An explicit attribute value, including null or an invalid value, always
takes precedence and is validated.
Supply an optional author_id in data.attributes to select a user with a role
on the credential's account, a descendant account, or an ancestor account that
provides inherited access. A role only on a sibling or unrelated account does
not qualify. The schema advertises this optional creation input,
available as --attr-author_id after refreshing the CLI schema. Invalid IDs,
nonexistent users, and users outside the account scope return 400 Invalid data
with an author_id error.
The legacy data.relationships.author.data.id remains supported when the
author_id attribute is absent. Explicit null selects the default author and
ignores the legacy author; other explicit values are validated without falling
back to the legacy or default author. When neither author input is supplied,
the default author is selected as before.
If no default author is available, creation returns 400 Invalid data with an
author error asking for an author within the account scope. No note is created.
Omitting required attributes returns 400 Invalid data identifying all missing
fields before target lookup or author selection. Invalid attachment types return
an attached_to_type error; invalid IDs and nonexistent targets return an
attached_to_id error. Targets outside the account scope return 403 Permission denied.
After refreshing the CLI schema with hoalife schema --refresh, create a note
using the advertised attachment flags:
hoalife note create \
--attr-body_text "Inspection follow-up" \
--attr-attached_to_type property \
--attr-attached_to_id 42
Update Note
curl "https://api.hoalife.com/api/notes/42" \
-X PATCH \
-H "Authorization: Token sk_SUPERSECRET" \
-H "Content-Type: application/vnd.api+json" \
-d '{
"data": {
"type": "note",
"attributes": {
"body_text": "Updated inspection follow-up",
"pinned": false,
"author_id": 1
}
}
}'
This endpoint updates an existing Note and returns 200 OK with the same note
response structure shown for creation. It requires the Notes Update
permission and a note within the credential's account or descendant accounts.
HTTP Request
PATCH https://api.hoalife.com/api/notes/:note_id
PUT https://api.hoalife.com/api/notes/:note_id is also supported.
URL Parameters
| Parameter | Type | Description |
|---|---|---|
| :note_id | Integer | Unique identifier of the Note to update |
Note Update Attributes
| Attribute | Type | Required | Nullable | Description |
|---|---|---|---|---|
pinned |
Boolean | No | No | Denote if the note should be pinned |
author_id |
Integer | No | Yes | ID of the User who authored the note. Supply a user with a role on the credential's account, a descendant, or an ancestor account, or pass null to select the default author. Omit on update to keep the current author. On create, omission uses the legacy author relationship or the default author. |
body_text |
String | No | No | The note's text |
Required refers to unconditional presence. See descriptions for conditional requirements. Nested requirements apply when the parent object is supplied.
Supply fields in data.attributes; omitted fields keep their existing values.
Send pinned: false to unpin a note. The note's attachment cannot be changed.
Legacy relationships are not processed on update.
An author_id must identify a user with a role on the credential's account,
a descendant account, or an ancestor account that provides inherited access.
A role only on a sibling or unrelated account does not qualify.
Explicit null selects the default author, just as on creation; omitting the
attribute keeps the current author. Invalid or out-of-scope authors return
400 Invalid data with an author_id error. If no default author is available,
explicit null returns an author error. Blank note text also returns
400 Invalid data. Validation failures leave the entire note unchanged.
Refresh the CLI schema with hoalife schema --refresh to discover the update
fields.
Destroy Note
curl "https://api.hoalife.com/api/notes/42" \
-X DELETE \
-H "Authorization: Token sk_SUPERSECRET" \
-H "Content-Type: application/vnd.api+json"
The above command returns an empty response with status
202 Accepted.
This endpoint destroys an existing Note. It requires the Notes Destroy
permission and a note within the credential's account or descendant accounts.
Deletion is performed in a background process, so the API may continue to return
the Note until that process completes. Missing permissions or an out-of-scope
note return 403 Permission denied without queuing deletion.
HTTP Request
DELETE https://api.hoalife.com/api/notes/:note_id
URL Parameters
| Parameter | Type | Description |
|---|---|---|
| :note_id | Integer | Unique identifier of the Note the request is being made for |
Errors
{
"data": {
"id": "556629c9-c2ad-494b-82d4-53d9639f7174",
"type": "error",
"attributes": {
"id": "556629c9-c2ad-494b-82d4-53d9639f7174",
"title": "Resource Not Found",
"status": 404,
"detail": "Resource not found with this API key's scope"
}
}
}
Response Attributes
| Attribute | Type | Presence | Nullable | Description |
|---|---|---|---|---|
id |
String | Always | No | The request ID associated with the error. |
title |
String | Always | No | — |
status |
Integer | Always | No | — |
detail |
String or Object | Always | No | An error message or a map of validation errors, which can contain nested errors. Additional keys may be present. |
Nested presence applies when its parent object is present.
| Error Code | Meaning |
|---|---|
| 400 | Bad Request -- Request was not sufficient to provide a response. |
| 401 | Unauthorized -- Provided API key was not valid. |
| 403 | Forbidden -- Provided API key does not have access to the requested resource. |
| 404 | Not Found -- Resource does not exist. |
| 409 | Conflict -- The request conflicts with the current state of the resource. |
| 429 | Too Many Requests -- Rate limit exceeded |
| 500 | Internal Server Error -- An error has occured and we've been notified. |
| 503 | Service Unavailable -- We're temporarily offline for maintenance. Please try again later. |
Validation Errors
POST, PUT, and PATCH requests to create or update resources may respond with a 400 when not enough data was provided. In this case, reference the detail object for an explanation.
Resource writes check required top-level attributes for the requested operation
before processing input. Missing keys return 400 Invalid data, with all
missing required attributes named in detail, for example
{"title": ["can't be blank"], "description": ["can't be blank"]}. Missing or malformed data and
attributes objects are identified by those names. Update requests do not need
attributes marked as required only for creation.
A supplied null, blank string, false, zero, or empty collection counts as a present attribute; existing value validations still apply. Relationship and conditional requirements are documented with each resource.
{
"data": {
"id": "656629c9-c2ad-494b-82d4-53d9639f7174",
"type": "error",
"attributes": {
"id": "656629c9-c2ad-494b-82d4-53d9639f7174",
"title": "Invalid data",
"status": 400,
"detail": {
"name": "can't be blank",
"parent_id": "is invalid"
}
}
}
}
Webhooks
Webhooks allow your application to be notified, in real-time, about changes in HOALife. As a change happens, HOALife will make an HTTP POST with the event's data to your publicly accessible webserver. Contact HOALife to configure a webhook endpoint.
Respond with a 200 status code to confirm receipt of the webhook. Non-2XX status codes will result in a Retry of the webhook.
{
"event": "property.update",
"date": "2020-04-14T16:40:17.332Z",
"payload": {
"data": {
"id": "25",
"type": "property",
"attributes": {
"id": 25,
"account_id": 5,
"formatted_id": "PRP-25",
"street_1": "4242 Cowbell Drive",
"street_2": null,
"city": "Sun Valley",
"state": "ID",
"postal_code": "83353",
"override_geocoding": false,
"latitude": "37.233333",
"longitude": "-115.808333",
"route_leg": null,
"route": "Cowbell Drive",
"emails": [
"earl-larae@cowbelldrive.com", "earl-larae@yahsnooze.com"
],
"phone_numbers": [
"+132155512345", "+132155512346"
],
"created_at": "2019-07-30T21:16:45.460Z",
"updated_at": "2019-07-30T21:16:45.460Z",
"mailing_name": "Earl LaRae",
"mailing_street_1": "PO BOX 42424242",
"mailing_street_2": null,
"mailing_city": "Sun Valley",
"mailing_state": "ID",
"mailing_postal_code": "83353",
"notes": [],
"external_id": "123"
},
},
"relationships": {}
}
}
| Webhook | Description |
|---|---|
| account.create | New Account Created |
| account.update | Account Updated |
| account.destroy | Account Destroyed |
| ccr.article.create | New CCR Article Created |
| ccr.article.update | CCR Article Updated |
| ccr.article.destroy | CCR Article Destroyed |
| ccr.violation_type.create | New CCR Violation Type Created |
| ccr.violation_type.update | CCR Violation Type Updated |
| ccr.violation_type.destroy | CCR Violation Type Destroyed |
| user_account.create | User Added to Account |
| user_account.update | User Permission Updated |
| user_account.destroy | User Removed from Account |
| inspections.violation.create | Inspection Violation Created |
| inspections.violation.cure_reminder | Inspection Violation Cure Reminder |
| inspections.violation.individual_create | Individual Inspection Violation Created |
| inspections.violation_delivery.create | Inspection Violation Notice Delivery Created |
| inspections.violation_delivery.sent | Inspection Violation Notice Sent |
| inspections.violation_delivery.failed | Inspection Violation Notice Delivery Failure |
| inspections.violation_response.create | Property Owner Violation Response Created |
| inspections.violation.tag.added | Inspection Violation Tag Added |
| inspections.violation.tag.removed | Inspection Violation Tag Removed |
| inspections.violation.closed | Inspection Violation Closed |
| inspections.violation.regression | Inspection Violation Reopened |
| inspections.violation.update | Inspection Violation Updated |
| inspections.violation.destroy | Inspection Violation Destroyed |
| inspection.complete | Inspection Completed |
| inspection.create | Inspection Created |
| inspection.inspection_reminder | Association Due for Inspection |
| inspections.escalation.create | Inspection Escalation Created |
| inspections.escalation.snoozed | Inspection Escalation Snoozed |
| inspections.escalation.snooze_expired | Inspection Escalation Snooze Expired |
| inspections.escalation.resolve | Inspection Escalation Resolved |
| inspections.escalation.note.create | Inspection Escalation Note Created |
| inspections.escalations.letter.create | Inspection Escalation Letter Created |
| inspections.suggested_violation.create | Inspection Violation Suggestion Created |
| inspections.suggested_deficiency.create | Inspection Deficiency Suggestion Created |
| property.create | Property Created |
| property.update | Property Updated |
| property.destroy | Property Destroyed |
| property.note.create | Property Note Created |
| property.tag.added | Property Tag Added |
| property.tag.removed | Property Tag Removed |
| properties.ownership_change_suggested | Property Ownership Change Suggested |
| properties.ownership_change | Property Ownership Changed |
| inspections.deficiency.create | Asset Deficiency Created |
| inspections.deficiency.resolve | Asset Deficiency Resolved |
| inspections.deficiency.snoozed | Asset Deficiency Snoozed |
| inspections.deficiency.snooze_expired | Asset Deficiency Snooze Expired |
| inspections.deficiency.note.create | Asset Deficiency Note Created |
| inspections.deficiency.tag.added | Asset Deficiency Tag Added |
| inspections.deficiency.tag.removed | Asset Deficiency Tag Removed |
| asset.create | Asset Created |
| asset.update | Asset Updated |
| asset.destroy | Asset Destroyed |
| asset.tag.added | Asset Tag Added |
| asset.tag.removed | Asset Tag Removed |
| violation_cover_letter.create | Violation Cover letter Created |
| violation_cover_letter.update | Violation Cover letter Updated |
| violation_cover_letter.destroy | Violation Cover letter Destroyed |
| letter_templates.escalation.create | Escalation Letter Template Created |
| letter_templates.escalation.update | Escalation Letter Template Updated |
| letter_templates.escalation.destroy | Escalation Letter Template Destroyed |
| letters.letter_delivery.create | Letter Delivery Created |
| letters.letter_delivery.sent | Letter Delivery Sent |
| letters.letter_delivery.failed | Letter Delivery Failure |
| conversation.create | Conversation Started |
| conversations.sent_message.create | Conversation Message Sent |
| conversations.received_message.create | Conversation Message Received |
Remove a Webhook
Service account keys with Accounts Read permission can remove a configured webhook belonging to the same account. Removing a webhook deletes its service account key, stopping future webhook delivery and revoking that key's API access. The calling key remains active unless it is the key being removed. User access credentials cannot manage webhooks.
curl "https://api.hoalife.com/api/webhooks/sk_WEBHOOK_KEY_ID" \
-X DELETE \
-H "Authorization: Token sk_SUPERSECRET"
HTTP Request
DELETE https://api.hoalife.com/api/webhooks/:id
:id is the webhook's service account key ID, as returned by webhook creation
or shown in API key management. A successful request returns an empty 202 Accepted
response. Missing keys, keys without a configured webhook, and keys in other
accounts return 404. Credentials without the required permission receive 403.
Verify Webhook Request Authenticity
Webhook POST requests will include the X-Signature HTTP header just like our API endpoint responses. See Verify Response Authenticity for details.
Idempotentcy
Webhooks may be retried a later time if a network error is encountered. Ensure your webhook endpoint supports handling of webhooks in an idempotent manner.
Retries
If a 2XX response code is not provided by your webhook endpoint, the webhook will be resent with an exponential back-off period. This accommodates service outages of your webhook endpoint.
Ordering
Many webhooks may be fired at once in near parallel order. As a result, it is important to consider the expected order of receipt and real-world order of receipt. For example, when an inspection is created, any related inspections.violation.create webhooks may be triggered shortly afterwards. You might be expecting the inspection.create webhook to be delivered first, however, this is not a guarantee (network latency or errors may disrupt this expectation).
Utilize your application's shared state (perhaps existence/absence of a database record) and non-2XX response codes to leverage Retries as a method of enforcing ordering of data. For example, if you require receipt of the inspection.create webhook before processing any inspections.violation.create webhooks, you could respond to any inspections.violation.create webhooks with a 404 until you've received and processed the inspection.create webhook. The 404-ed inspections.violation.create will automatically be retried.
Quick Responses
Webhooks are intended to quickly be fired and receive a response. A strict 4 second timeout is enforced for webhook network requests. Do any time consuming work outside of the network request of the webhook to avoid the request being terminated and the webhook being resent.