NAV
cURL

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:

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_SUPERSECRET with 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_SUPERSECRET with 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_SUPERSECRET with 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_SUPERSECRET with 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
email 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
email 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.