Guides
Rent and property management
Sync residents, every charge, and every payment from a property management app into HelloLedger.
How it maps
Use your own primary keys as external IDs. Retries and nightly re-syncs then never create duplicates.
| Your app | HelloLedger | Endpoint |
|---|---|---|
| Resident or student | Customer | PUT /customers/by-external-id/{resident_id} |
| Each charge (rent, late fee, utilities, parking) | Invoice with one or more lines | PUT /invoices/by-external-id/{charge_id} |
| Each payment | Customer payment applied to the charges it paid | PUT /customer-payments/by-external-id/{payment_id} |
| Special, waived balance, write-off, kept deposit | Customer credit applied to the charge it covers | PUT /customer-credits/by-external-id/{credit_id} |
| Deposit return or refund | Customer refund | PUT /customer-refunds/by-external-id/{refund_id} |
| Processor payout | Payout with its fees | PUT /payouts/by-external-id/{payout_id} |
| Charge types | Products and services | GET /items |
One-time setup (bookkeeper)
- Companies with the Property Management business type get a chart of accounts with Tenant Security Deposits, Payment Processor Clearing, Rent Concessions, Bad Debt, and Bank and Payment Processing Fees, plus items for Rent, Room Rent, Late Fee, Application Fee, Move-in Fee, Pet Fee, Parking, and Utility Billback.
- Otherwise, create items for each charge type, each pointing at its income account.
- Use a liability account for security deposits. Deposits are owed back to the resident, so they are not income.
- Use a clearing asset account (or Undeposited Funds) when your processor pays out in batches.
- Connect the company. If your app serves many landlords or managers, use Connect HelloLedger: they approve from your app and your server gets the key. Otherwise a company admin creates a developer key under Profile → Developers with the Property management / receivables sync template and sets integration to your app's name. The secret is shown once; store it in your secret manager.
- Save the account IDs your app needs (deposit liability, clearing, concessions, bad debt, fees) from GET /coa in your app's settings for that company.
1. New residents the same day
When a resident is added, PUT the customer. If your sync might send a charge first, include the customer object on the invoice; HelloLedger creates the customer automatically.
curl -X PUT "https://api.helloledger.ai/v1/companies/{company_id}/customers/by-external-id/stu_1001" \
-H "Authorization: Bearer $HL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Jamie Rivera",
"email": "jamie@example.com",
"metadata": {
"unit": "4B"
}
}'2. Every charge is its own invoice
Post each charge when it's created. Use an item line for income and an account line for a security deposit, so the deposit lands in the liability account.
curl -X PUT "https://api.helloledger.ai/v1/companies/{company_id}/invoices/by-external-id/chg_2026_10_rent_1001" \
-H "Authorization: Bearer $HL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"customer_external_id": "stu_1001",
"date": "2026-10-01",
"due_date": "2026-10-05",
"lines": [
{
"item_id": "66f1a2b3c4d5e6f708091a2b",
"description": "October rent",
"amount": "850.00"
}
]
}'curl -X PUT "https://api.helloledger.ai/v1/companies/{company_id}/invoices/by-external-id/chg_deposit_1001" \
-H "Authorization: Bearer $HL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"customer_external_id": "stu_1001",
"date": "2026-08-15",
"terms": "due_on_receipt",
"lines": [
{
"account_id": "66f1a2b3c4d5e6f708091d4e",
"description": "Security deposit",
"amount": "500.00"
}
]
}'3. Every payment applied to what it paid
Record each payment individually and list the charges it paid. Deposit it to the clearing account; when the processor's payout reaches the bank, the bookkeeper matches the bank deposit against the clearing account.
curl -X PUT "https://api.helloledger.ai/v1/companies/{company_id}/customer-payments/by-external-id/pay_pi_3Q8x" \
-H "Authorization: Bearer $HL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"customer_external_id": "stu_1001",
"date": "2026-10-03",
"amount": "850.00",
"deposit_account_id": "66f1a2b3c4d5e6f708091c3d",
"method": "card",
"reference": "pi_3Q8x",
"applications": [
{
"invoice_external_id": "chg_2026_10_rent_1001",
"amount": "850.00"
}
]
}'4. Specials, waived balances, and write-offs
Record each as a credit applied to the charge it covers. A move-in special goes to Rent Concessions, so gross rent and the discount both show on the P&L. A balance you give up on goes to Bad Debt with reason write_off.
curl -X PUT "https://api.helloledger.ai/v1/companies/{company_id}/customer-credits/by-external-id/cr_2026_10_special_1001" \
-H "Authorization: Bearer $HL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"customer_external_id": "stu_1001",
"date": "2026-10-01",
"reason": "concession",
"lines": [
{
"account_id": "66f1a2b3c4d5e6f708091f60",
"description": "Move-in special",
"amount": "100.00"
}
],
"applications": [
{
"invoice_external_id": "chg_2026_10_rent_1001",
"amount": "100.00"
}
]
}'5. Move-out: deposits kept and returned
- Post any move-out charges (cleaning, damage, final utilities) as invoices.
- Apply the deposit to them with a credit: reason deposit_applied, line account the deposit liability.
- Return the rest with a refund: source deposit, paid from the bank account.
- PUT the customer with status inactive.
curl -X PUT "https://api.helloledger.ai/v1/companies/{company_id}/customer-credits/by-external-id/cr_deposit_kept_1001" \
-H "Authorization: Bearer $HL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"customer_external_id": "stu_1001",
"date": "2027-05-20",
"reason": "deposit_applied",
"lines": [
{
"account_id": "66f1a2b3c4d5e6f708091d4e",
"description": "Deposit applied",
"amount": "150.00"
}
],
"applications": [
{
"invoice_external_id": "chg_2027_05_cleaning_1001",
"amount": "150.00"
}
]
}'curl -X PUT "https://api.helloledger.ai/v1/companies/{company_id}/customer-refunds/by-external-id/rf_deposit_1001" \
-H "Authorization: Bearer $HL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"customer_external_id": "stu_1001",
"date": "2027-06-01",
"amount": "350.00",
"paid_from_account_id": "66f1a2b3c4d5e6f708091e5f",
"source": "deposit",
"deposit_account_id": "66f1a2b3c4d5e6f708091d4e",
"method": "ach"
}'6. Payouts and fees
Keep recording each payment at its full amount into the clearing account. When the processor pays out, send the payout with its gross, fee, and net. HelloLedger posts the fee to the fees expense account, leaving exactly the net in clearing for the bookkeeper to match against the bank deposit.
Don't use the daily revenue journal for this: it bypasses invoices, so resident balances would be wrong.
curl -X PUT "https://api.helloledger.ai/v1/companies/{company_id}/payouts/by-external-id/po_1QxYz" \
-H "Authorization: Bearer $HL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"date": "2026-10-06",
"clearing_account_id": "66f1a2b3c4d5e6f708091c3d",
"gross": "2550.00",
"fee": "74.25",
"net": "2475.75",
"fee_account_id": "66f1a2b3c4d5e6f708092182",
"bank_account_id": "66f1a2b3c4d5e6f708091e5f",
"reference": "po_1QxYz"
}'7. Corrections
- Charge amount changed before it was paid: PUT the invoice again with the new lines. HelloLedger replaces it and keeps the invoice number.
- Charge waived before anything was applied: POST /invoices/by-external-id/{id}/void. After a payment, use a credit instead.
- Charge changed after payment: void the payment, update the invoice, then record the payment again under a new external ID.
- Returned payment (ACH return, chargeback): POST /customer-payments/by-external-id/{id}/void. The charges reopen.
- Overpayment sent back: refund with source payment.
- A voided invoice stays voided. If the charge comes back, send it under a new external ID.
8. Never miss a record
Send records in real time, and also run a nightly job that re-sends the last few days of residents, charges, and payments. Records already in HelloLedger return result unchanged, so the job is safe to run as often as you like and fills any gap left by an outage.
Process residents first, then charges, then payments, then credits, refunds, and payouts, since each step refers to the ones before. Stay under 120 requests per minute.
Send your own X-Request-Id header (for example your sync batch and record ID). HelloLedger echoes it, puts it in errors, and shows it in the key's activity.
9. Check the totals
Compare your app's open balances against HelloLedger's A/R aging (reports:read) at month end.
curl "https://api.helloledger.ai/v1/companies/{company_id}/reports/ar-aging?as_of=2026-10-31" \
-H "Authorization: Bearer $HL_API_KEY"