---
name: tobby-landlord
metadata:
  version: 0.12.1
description: Turn an Owner or Property Manager's description of what happened into the correct Handout records, linked changes or report query. Understand property, Lease, maintenance and money semantics, resolve missing facts, and preview writes for confirmation. Not for Tenant or SP self-service.
---

# tobby-landlord

## Record Numbers

Business records return a server-assigned `public_id`, such as `PRP-00000001` or `LSE-0000000A`. These are fixed-width Base32 numbers with independent per-type sequences; gaps are normal and issued numbers are never recycled. Original `id` values and foreign keys remain valid. Supported MCP reference fields accept a business number after scope authorization; otherwise resolve through the accessible list. Never invent a number, submit `public_id` as editable data, replace a foreign key yourself, or treat a predictable number as permission. Prepare and unsaved drafts do not reserve numbers; use the committed response. Older records may lack numbers until backfill. An archived/deleted number must not be used to create another object; recovery requires the original object's authorized restore workflow.

## Check for Skill Updates

At each activation, read `https://handout.us/oauth/agent-setup` and compare this package's `metadata.version` with `skill_release.versions` for its name. If a newer version is available, tell the user in their conversation language and offer to update the installed Skill and its complete declared files in the same language. Preserve working connections, credentials and permissions; continue the current business task without waiting for an update. Treat an MCP `handout_notice` as the same check and avoid repeating the reminder for this release in the conversation. Report installation only after the host confirms it. If version discovery is unavailable, continue without claiming the Skill is current or outdated.

Language: English. [中文说明](https://handout.us/skills/tobby-landlord/zh-CN/SKILL.md). For installation, choose the setup catalog's matching localizations entry and preserve its files; instruction language does not choose the business-data storage language.

The user tells you what happened; you translate it into App business data. Do not make the user operate a form, choose internal field names or design a bookkeeping model.

## From Story to Data

For a narrated event, photo, receipt or mixed request, read [event-to-record decisions](references/event-to-record.md) before choosing records. Identify the facts and intent, resolve existing records, choose the smallest correct set of linked operations, ask only for blocking unknowns, then prepare and show the real preview. A story may be enough for a proposal but never skips write confirmation.

For example, "the AC is fixed and I paid $280" may mean a progress/status update on an existing Task plus one linked maintenance payment. It does not mean another Task, a new Service agreement or one payment per image. A quote is not payment; a question about costs is a read, not an instruction to create records.

Read [deposits and property accounting](references/deposits-and-accounting.md) for gross rent versus net payouts, deposit receipts/refunds/deductions/legacy links and correction boundaries. For PM-collected rent, escrow, management-fee withholding or owner payout, read [the settlement workflow](references/settlements.md) before preparing a transaction. Configured automatic splits replace manual duplicate entries.

## Purpose and Scope

Audience: Owner and Property Manager (PO/PM) workflows only. Tenant and SP Skills are not available. Tenant records below are profiles managed by the PO/PM, not Tenant logins.

For account connection, installation errors, resuming or restarting setup, use [Handout Connection Guide](../handout-connect/SKILL.md). Read the live installation manifest first; user credentials remain in the host's secure client.

For bulk onboarding or importing an existing portfolio from spreadsheets/PDFs/files, use [Handout Data Migration](../handout-data-migration/SKILL.md) and the live `migration.schema` contract. The dedicated `migration` tool previews and confirms resumable imports; do not simulate a bulk import by issuing unreviewed individual writes.

Your AI agent prepares data in the caller's environment, then uses Handout MCP under the user's own account. Skill instructions explain the App, not a separate permission system. Every operation follows the App's dependencies, permissions and business services. Read `app_schema.get` for current actions and fields; it describes contracts, not permission grants. Preview every business write before explicit approval.

Focused PO/PM Skills are available in the live setup manifest's `skill_catalog`. For record management and file repair, use [Property Records](https://handout.us/skills/handout-property-records/SKILL.md); for invitations or ownership/management transfer use [Access and Handover](https://handout.us/skills/handout-access-handover/SKILL.md); for profile or support use [Account and Support](https://handout.us/skills/handout-account-support/SKILL.md). Load only the relevant workflow. This original entry remains compatible and retains bookkeeping/report guidance.

For ordinary record creation/editing/deletion, lease renewal/status/archive, structured validation feedback, or repairing an uploaded file association, read [App operations and text confirmation](references/app-operations.md) before preparing the action. Use live tool discovery to handle older deployments safely.

For invitations, access removal, history handover or the PO/PM's own profile/support, read [Access, handover and account support](references/access-and-self-service.md). A Tenant profile managed by a PO/PM is not a Tenant login or permission grant. Tenant/SP self-service and plan changes remain outside these Skills.

## When to Use This Skill

**Queries and operations**

- Properties: query/create, edit ordinary details/HOA, change usage, or delete through the App deletion-and-backup workflow. Deletion is not reversible hide/archive.
- Leases and tenants: read records, create or edit a Lease/Tenant, renew or terminate a Lease, archive a Lease, or delete a Tenant profile using the live App-operation schemas. Renewal creates a new Lease, not an overwrite.
- Inventory: "List the furniture and keys for this lease," "Add a refrigerator to the kitchen," "Record returned keys," or "Start the move-out checklist."
- Tasks: "Schedule a plumber at Comanche next week," "What is the status of Daffodil's leak?" or "Mark this task complete."
- Reminders: "Remind me to collect rent on the fifth next month" or "Set a reminder for insurance renewal."
- Documents: "Save this lease under Julia" or "Find Daffodil's HOA documents."
- Reports and search: "How much did maintenance cost last month?" "What deposits are held?" or "Find plumbing-related records."
- Structured drafts: "Turn this description into a task or reminder." Prepare a draft without saving.

**Bookkeeping**

- Transactions: "Received $1,200 for Daffodil's July rent," "Paid the plumber $350," or "Received Julia's $1,500 deposit."
- Statement reconciliation: "Enter this property manager's monthly statement." The user may provide text, screenshots, or files.
- Receivables: "Who owes rent this month?" "Record a late fee receivable," "Allocate this receipt to unpaid rent," or "Close future charges after early termination."
- Ledger queries: "How much did Comanche maintenance cost this year?" "What was last month's net income?" or "What was this $800 payment?"
- Corrections: "Change last month's utilities payment to maintenance" or "The amount should be $420."
- Receipts: "Attach this receipt to the maintenance transaction."

## Connection and Authentication

- Protocol: MCP over Streamable HTTP.
- Endpoint: `https://handout.us/mcp`.
- Preferred authentication: MCP OAuth 2.1 with PKCE. Add `https://handout.us/mcp` in the client's secure MCP settings and select OAuth. The client discovers authorization metadata, registers its callback, opens Handout login/consent, and manages access/refresh tokens. Users do not manually create a key. Keep credentials in the client's credential store, outside conversation text and skill files.
- Request `handout.read handout.write` for recording, uploading, linking, editing or deleting records. Business mutations still require preview and explicit user confirmation; OAuth does not substitute for that confirmation. For queries only use `https://handout.us/query-mcp` with `handout.read`. Existing read-only grants cannot be upgraded by refreshing; reconnect with the requested permissions.
- On an OAuth authorization challenge, let the client's connection flow open Handout login and consent. On insufficient scope, explain which write operation requires broader access and ask the user to reconnect. Connections expire after 90 days and can be revoked at https://handout.us/connect-ai.
- Compatibility fallback: if the client cannot perform MCP OAuth, direct the user to https://handout.us/connect-ai and select API key. Add the resulting `tk_...` key through secure Bearer settings (`Authorization: Bearer <key>`). On expired/revoked keys, use connection management instead of repeatedly retrying the old key.
- Rate limits: 60 reads/minute and 20 writes/minute, independently per connection. AI tools use the application's AI quota.
- Identity comes from the verified connection. The server ignores `X-User-Id` and `X-User-Role`; existing property and finance permissions continue to apply.

## Rule 1: Resolve the Property First

For operations on existing properties, `property_id` is a first-class input. Resolve it first. For a new property, confirm the address without supplying a nonexistent property ID.

1. Use `property.resolve` for a nickname, name, or address, and retrieve the top five candidates.
2. Ask the user to choose when multiple candidates match. Never invent a `property_id`.
3. Where the action supports it, a user with exactly one property may omit `property_id` for server inference.

Access is restricted to the user's authorized properties. Cross-tenant calls are rejected with 403.

## Property and Asset Creation: Caller Preparation

Use `property.prepare_create / confirm_create` and `asset.prepare_create / confirm_create`. Check live MCP `tools/list` and action schemas first. If actions are missing, inspect connection capabilities/permissions and explain what is unavailable; prepare only a local draft, not a claimed save.

### Property

The user's agent may search by address to obtain property type, bedroom/bathroom counts, size, and construction year. Identify sources and uncertainties. Leave unavailable fields empty; do not require an APN. Check existing properties for duplicates, then ask the user to confirm the address and proposed details.

- Before calling creation, confirm the user's relationship to the property: Owner, PM, or delegated data entry. Confirm whether the actual Owner is the current user, another known person, or not yet identified. Do not repeat questions the user already answered explicitly.
- Search results do not establish ownership, and creation does not automatically make the creator an Owner. User confirmation is a business declaration, not legal title verification. Do not guess another Owner's account; leave association pending when no account is confirmed.
- Inputs include `address,city,state,zip,nickname,type,notes,spec`, and required `property_usage:rental|personal_use`. Reuse an explicit statement of intended use; otherwise ask. Use schema fields such as `bedroom,full_bathroom,half_bathroom,size,lot_size,year_built` under `spec`. Do not invent numeric values. Confirm unclear area units; do not mix square meters and square feet.
- Required: `ownership:{relationship:"owner"|"manager"|"delegate",owner_status:"self"|"other"|"unknown",confirmed:true,owner_name?,owner_email?}`. Set `confirmed:true` only after user confirmation. `self` requires `owner`; other statuses require `manager` or `delegate`. `other` requires a known Owner name or email. `unknown` allows creation with later association.
- `self` associates the current user as Owner and manager. `other/unknown` grants only management access to the creator; the Owner is linked through the existing invitation flow. Declared names/emails do not grant account access. Check duplicates through resolve/list; on `PROPERTY_ALREADY_EXISTS`, use the existing property rather than create another.

### Asset

Resolve the property first. Recommend two photos: an exterior/appearance photo and a nameplate photo. The user's agent uses its own vision capability to extract name, type, brand, model number, and serial number, then asks for installation location. Preserve identifiers exactly. If illegible, ask for another photo or clarification, or leave the field empty instead of guessing. Missing photos need not prevent a draft; the tool's required fields determine whether creation can proceed.

Upload photos through Storage and retain them as attachments. Purchase date, price, installation date, and warranty period require user or documentary evidence. Pass supported extracted fields and actual upload results to the creation tool; preview and confirm before saving. Translation and photo recognition are performed by the caller.

Use `property_id,nickname,type,location,brand,model,serial,mfg_date,acquired_date,notes`. Model number maps to `model`; serial number maps to `serial`. Name is required. Omit an unknown type to use Other. Dates use YYYY-MM-DD. Request upload credentials with `storage.create_upload_url` for the target property and actually upload bytes, then provide `photos:[{reservation_id,kind:"appearance"},{reservation_id,kind:"nameplate"}]`. Photos are optional; never fabricate URLs or reservations. Confirm commits the uploads and writes them to the Asset's images.

For both creation tools, confirm executes only the previewed content: send `preview_id,user_confirmed:true`. Changed fields require a new prepare. A successful response returns `property` or `asset` with its real ID. Claim success only from that response. Recovery retries reuse the creation result to avoid duplicates.

## Input Validation: One Submission, One Record

- Treat text and images submitted together as one record by default. Interpret the images as evidence or details for that record, not as separate tasks, transactions, or documents. An upload reservation is not a business record.
- Resolve the record type and target property first. For an existing record, resolve its real ID. If text and images appear to refer to different properties, events, dates, or amounts, explain the conflict and ask which record they belong to before preparing a write.
- If one submission genuinely contains multiple business events, ask the user to approve the split and assign each image to the correct event. Preserve configured rent-settlement rules; generated accounting splits are not a reason to create duplicate records for attachments.
- Extract only evidenced details. Preserve identifiers and uncertainty; ask about illegible amounts, dates, model/serial numbers, or contradictory evidence. Confirm the storage-language preference before translating.
- Task titles support at most 200 characters; descriptions/comments and transaction notes at most 10,000. Validate positive transaction amounts with at most two decimals, YYYY-MM-DD dates, canonical categories, and payer/payee directions. Do not silently truncate text.
- Upload the actual image bytes to reservations for the selected property before preparing the record. Show the final text, target record/property, attachment count, and which submitted images are attached in one preview. Obtain explicit approval of the combined record.
- A changed target, text, or attachment set requires a fresh prepare and confirmation. Do not attach new images through confirm-time overrides. Report success only after confirm returns the saved record; a successful upload alone is not a saved task or transaction.

### Images on Every Record

- **New Task:** upload up to five images using `storage.create_upload_url`, then call `task.prepare_create` with the text fields and `image_reservation_ids:[<reservation IDs>]`. After combined approval, call `task.confirm_create` with `preview_id,user_confirmed:true`. Confirm commits the uploads and stores the images on that same task.
- **Existing Task/comment:** call `task.prepare_comment` with `property_id,task_id,comment,image_reservation_ids` (up to five images). After approval, `task.confirm_comment` stores text and images in one entry in the task's shared Web/mobile history. Use this flow to add image-backed updates instead of creating another task.
- **Transaction:** supports one receipt image through `reservation_id` in `transaction.prepare_create` or `prepare_update`; matching confirm commits and attaches it to the same transaction as `picture_url`. An already committed image may instead use `picture_url`; do not supply both.
- **All existing records:** `record_images` supports `property,task,transaction,asset,document,reminder,lease,tenant,service,receivable,settlement`. Supply the real `property_id,record_type,record_id`. Settlement targets are settlement-operation IDs; attach cash-payment evidence to its transaction ID when appropriate. A property's `record_id` equals its `property_id`.
- **Missing or multiple images:** read `record_images.get` -> upload images for the same property -> `record_images.prepare_attach(image_reservation_ids)` -> show the record name/property, existing images and additions -> explicit approval -> `record_images.confirm_attach(preview_id,user_confirmed:true)` -> read again to verify. Each operation adds up to ten images, with at most twenty images in the linked-image collection. Existing text and images are preserved; duplicate uploads are not appended twice.
- **Task repair shortcut:** `task.get_images(property_id,task_id)` -> upload -> `task.prepare_attach_images(property_id,task_id,image_reservation_ids)` -> explicit approval -> `task.confirm_attach_images(preview_id,user_confirmed:true)` -> `task.get_images`. Use this for a failed or missing attachment without inventing a comment or another task.
- **Creation without native image inputs:** create the business record through its supported confirmation flow, retain the returned ID, then associate the photos through `record_images` to that same ID. Preview the record and intended photo association before starting, and confirm the attachment preview separately. If association fails, say "record saved; images not yet attached" and repair that record. New property photos require its saved property ID before upload. Lease, Tenant and Service use their separate lifecycle creation actions, not the image tool.
- **Inventory items:** preserve the native versioned workflow described below. Use item-bound uploads and `inventory.prepare_update/confirm_update`, not `record_images` or a direct database edit. This preserves move-in/move-out evidence and signature invalidation.
- **AI intake/search:** the caller interprets submitted images and supplies evidenced text to the existing draft/query tools. Attach the original images to the eventual business record; a draft or search is not an image-bearing record.
- Reservations must belong to the caller and selected property and contain image files. Expired, duplicate, foreign, or non-image reservations are rejected. Confirm rechecks uploads and permissions; failed image association is not success.

## Storage Language: Confirm the User's Preference

- Before preparing non-English business text, ask in the user's language: "Would you like me to translate this into English before saving, or keep the original language?" Wait for an explicit choice; do not assume permission to translate or save. If the user already stated a storage-language preference for this conversation, follow it without asking again unless they change it. Text already in English needs no translation.
- If the user chooses English, the agent using this Skill translates the text before calling Handout prepare/confirm tools. If the user declines, preserve the original language. Handout validates and stores supplied data; do not rely on server-side translation or call Handout AI tools solely to translate.
- Apply the chosen language to newly created or modified task titles/descriptions/comments, transaction notes, reminders, document descriptions, Inventory item/room names and condition notes, and receivable titles/notes. Any translation must be accurate and concise. Apply the same choice to newly supplied confirm text. If previewed content or the language choice changes, prepare again and confirm the final version.
- Preserve facts, amounts, dates, quantities, and uncertainty. Add no unsupported information. Preserve personal/company names, property proper names, addresses, account/record IDs, reference numbers, URLs, and original files/photos. Use exact tool field names and enum values.
- Show the exact final text and storage language that will be saved; explain it in the user's language if helpful. Language preference alone is not write authorization: obtain explicit approval of the final preview before confirm. Replies and query explanations remain in the user's language. Reading old records never triggers translation writes; translate only fields added or modified now when the user chooses English.

## Rule 2: Preview and Confirm Business Writes

Business creation and modification follow:

```text
prepare_create / prepare_update / prepare_receipt / prepare_comment
-> user reviews the preview and any missing inputs
-> matching confirm action
```

**Prepare** stages a repeatable preview without applying the business change. Send structured fields interpreted from the user's request. The response contains:

- `preview`: a human-readable description of the proposed action, amount, and property. Show it to the user.
- `preview_id`: an expiring, one-time handle for this staged operation, not an account credential.
- `missing_inputs`: server-defined missing fields, with `ui` descriptions and possibly `upload` instructions. Ask for these inputs rather than invent fields.
- For `prepare_update`, the preview is a before/after diff.

**Confirm** is the business-write entry. Prefer only `action,preview_id,user_confirmed:true`; staged facts must remain unchanged even when an older schema accepts an `inputs` compatibility field. Missing facts, new text or attachments require preparing and reviewing again. Call confirm only after explicit approval; the server rejects confirmation without `user_confirmed:true`.

New App-operation actions and `record_files` accept only `action,preview_id,user_confirmed` at confirmation. Property `confirm_delete` additionally requires `confirmation:"DELETE"` after the user explicitly confirms deletion. A `ready:false` result or any required `missing_inputs` blocks confirmation, even if a preview ID exists. Optional inputs do not block an otherwise complete proposal. JSON-RPC service errors may add `error.data.input_feedback.issues` while preserving the original `error.data.code`.

Show a compact text confirmation: operation and object names, property/scope, exact final fields or before/after changes, inferred values, actual attachment names/count, dependency effects and reversibility. User edits, attachment/target changes, expired previews or changed dependencies require a fresh preview and approval. Cancellation means no confirmation call. An uploaded file, an OAuth grant or a storage-language preference is not approval to save business data.

Explain concurrency and expiration errors rather than forcing retries:

- `PREVIEW_IN_USE`: wait and inspect the result; do not stage a duplicate concurrent write.
- `PREVIEW_TOOL_MISMATCH / PREVIEW_KIND_MISMATCH`: prepare again for the correct tool/action.
- Expired preview: prepare again.
- On a timeout or `PREVIEW_NOT_FOUND`, read the target/history before preparing another create; the earlier call may have succeeded.
- A failed write is not a successful saved record. Correct deterministic fields from the schema, reprepare changed content, and stop after two unsuccessful repair attempts with a safe error/support reference. Do not retry denied permissions or manufacture missing dependencies.

## Bookkeeping Rule: Cash Basis

Record actual money received or paid. This is not accrual accounting, tax accounting, or double-entry bookkeeping.

- **Amount** is the actual received/paid amount, not the billed or receivable amount.
- **Date** is the actual receipt/payment date. Resolve an explicit relative date using the user's context; ask if absent rather than substituting an invoice, work date or today.
- Record only rows with **Paid / Received > 0**. A billed $25 with $0 received is not a cash transaction.
- Skip handling/balance rows only when they are not additional business events. A real property cost paid directly by its Owner still belongs to that property's expenses; do not drop it based only on a statement label.
- Owner funds added to the account use `owner_contribution`; money withdrawn uses `owner_distribution`, not income.
- Preserve billed-versus-paid differences, invoice numbers, payment methods, and original row meanings in an accurate `note` using the user's confirmed storage language. Keep reference numbers unchanged and retain original files for reconciliation.

## Task Costs From Natural Language

When a user describes a task cost (for example, "I paid the plumber $350 for the kitchen leak"), resolve the existing task and property with `task.list/get` and `property.list/get`. Use its real ID internally; show the task title and property name to the user. Do not create a duplicate task.

- A cost, quote, estimate, or invoice alone does not establish payment. Ask whether money was actually paid, and how much, before creating a cash transaction. Leave unpaid or estimated costs in the task context.
- Collect the actual positive USD amount, payment date, payer, payee, and category. Ask only about missing or ambiguous details; never assume today, PO versus PM, or a supplier versus service provider. Ask which task/property if several match. Split separate payments only when the user identifies them as separate payments.
- Search existing transactions for the same task/payment before creating another. If a matching payment is already recorded, explain that rather than duplicate it. If an existing unlinked payment should be associated, use the app's task transaction linking flow; do not create a replacement payment.
- Call `transaction.prepare_create` with the resolved `property_id`, **`task_id`**, and confirmed payment fields. Use `maintenance` for repair labor or `supply` for parts/materials as appropriate; confirm ambiguous categories. Preserve the relevant work description in `note`.
- Show the task, property, amount, payment date, category, and from → to in the preview. Follow `missing_inputs` to ask for missing details, then prepare again. Only call `confirm_create` with the final `preview_id` and `user_confirmed:true` after explicit approval. The server creates one transaction linked to the existing task; the task remains unchanged.

## Accounts Receivable: Separate Debt From Receipts

Use `receivables` for unpaid obligations. Create a Transaction only for an actual receipt. Unpaid rent, fees, and penalties appear in receivables, not cash income. Access requires the property's canonical owner/manager; tenants and viewers cannot read or write AR.

1. After resolving the property, use `leases` to locate the lease. Use `list` for balances and `history` for receipts, closures, and adjustments. `list` defaults through today; `from / to` filter due dates (YYYY-MM-DD), including future months. Returned monetary values and allocation `amount_cents` use **integer USD cents**; manually created `amount` and receipt `amount` use dollars.
2. The filtered `summary` shows **current balances** for the selected due-date range, not balances as of a historical date. Aging uses today: 1-30, 31-60, 61-90, 91+ days. State the filter range and do not describe future charges as overdue.
3. For changes, call `prepare_update` with `command`, show `preview.changes`, obtain explicit approval, then `confirm_update` with `preview_id,user_confirmed:true`. Confirm only shown content; changed inputs require a fresh prepare. Actual receipts use the dedicated flow below.

| Intent | Command/action and required fields |
| --- | --- |
| Manual fee/penalty | `command.type:create`; `lease_id,title,category:other_income,amount,due_date`; optional `note` |
| Manual rent, including post-expiration rent | `create` with the above fields and `category:rental_income,period:YYYY-MM`; use an existing active receivable for that lease/month when present |
| Configure/backfill lease charges | Read `lease_plan` for `revision`, then `generate` with `lease_id,expected_lease_revision`; optional `settings` |
| Waiver/bad-debt adjustment | `adjust`; `receivable_id,expected_revision,amount,reason`; amount cannot exceed balance |
| Close remaining balance | `close`; `receivable_id,expected_revision,reason`; preserve receipts and closed amounts |
| Void an incorrect charge with no receipts/adjustments | `void`; `receivable_id,expected_revision,reason` |
| Allocate an existing receipt | `allocate`; `transaction_id,expected_revision` (transaction `ar_revision`, default 0), `lease_id,allocations`; supply the transaction's **complete** allocation list, preserving other existing links |
| Reverse an incorrect/returned receipt | `reverse_receipt`; `transaction_id,expected_revision,reason`; reverse the whole transaction, restore allocated balances, and retain the original transaction/history |

**Record an actual receipt:** call `prepare_receipt` with `receipt:{lease_id,amount,transac_date,transac_from:"tt",transac_to:"po"` or `"pm",financial_category,ar_allocations:[{receivable_id,amount_cents}]}`. For a PM recipient, omit `to_account_id` when the property has one automatic match; resolve multiple managers by name. Show actual amount, date, recipient, and allocations, then obtain explicit approval before `confirm_receipt`. The server atomically creates the Transaction and allocates it; do not also create a standalone receipt. This records money and does not transfer it. Allocate across months of the same lease and income category. Excess stays unallocated; partial balances remain outstanding, and zero balances settle automatically.

**Rent shortfall:** read `shortfall` with `lease_id,period`. If `amount` is positive, confirm that all receipts for that month have been entered. Then prepare `command:{type:"shortfall",lease_id,period,expected_snapshot:<returned fingerprint>}`. The server creates the **full rent obligation** and associates existing unallocated monthly receipts, leaving only the unpaid balance and avoiding reversal errors. On `AR_SHORTFALL_CHANGED`, read and confirm again. If a receivable exists, allocate to it instead of creating another shortfall.

Automatic lease-charge generation is frontend-triggered: activation generates monthly charges within the contract term; rendered product views idempotently complete unfinished plans. There is no scheduled backend scan, and expiration does not extend charges beyond the contract end. For existing leases, confirm `ar_start_period`. Settings include `ar_enabled,ar_start_period,ar_due_day` (1-28), and `ar_proration` (`actual_days` or `full_month`). Monthly leases support at most 240 months. Future rent is not received money.

**Early termination:** read `lease_plan`, then prepare `generate` with `settings:{status:"Terminated"}`. The preview closes future remaining balances while preserving receipts and current/historical debt. Have the user review each closed amount before confirming. Use `close` for an individual charge. `closed` means no longer pursued, not received; `settled` is different. Closed balances appear separately, cannot accept allocations, and are not reopened by backfill. Charges already due or with receipts/adjustments require explicit adjustments instead of direct rewriting through lease edits.

On `AR_BALANCE_CHANGED / AR_LEASE_CHANGED / AR_PLAN_CHANGED`, read again, prepare again, and confirm the revised amounts. After success, refresh `list / history`; a preview is not proof of saving.

## Financial Categories

Use server enum values for `financial_category`; invalid values receive 400. The table below describes property bookkeeping. The PM's own account statement interprets actual direction and scope: a management fee can be PM income while remaining the property's cost.

| Category | Meaning | Treatment |
| --- | --- | --- |
| `rental_income` | Rent income | Income |
| `other_income` | Other income, such as a separate late fee | Income |
| `management_fee` | Property management fee | Expense |
| `maintenance` | Maintenance and repairs | Expense |
| `supply` | Parts, materials, and consumables | Expense |
| `utilities` | Utilities | Expense |
| `insurance` | Insurance | Expense |
| `property_tax` | Property tax | Expense |
| `hoa` | HOA fees | Expense |
| `other_expense` | Other expenses, such as commissions | Expense |
| `security_deposit` | Tenant security deposit | Neutral |
| `owner_contribution` | Owner funding | Neutral |
| `owner_distribution` | Owner distribution/payment | Neutral |
| `transfer` | Internal transfer | Neutral |
| `reimbursement` | Reimbursement | Neutral |
| `unclassified` | Uncertain classification; do not guess | Neutral |

**Common mappings**

- Rent -> `rental_income`. Combined rent/late-fee receipts use `rental_income`, with the component amounts explained in `note`.
- A separate late fee -> `other_income`.
- PM payout, disbursement, or rent distribution -> `owner_distribution`, not duplicate income.
- Owner funds deposited -> `owner_contribution`; tenant deposit -> `security_deposit`.
- Parts/materials/consumables -> `supply`; default PO -> Supplier (`po -> supplier`), adjusted to the actual payer.
- Management fee -> `management_fee`; leasing commission -> `other_expense`; other repairs -> `maintenance`.

### Participants and Default Directions

Supply both `transac_from / transac_to`: `po` (Owner), `pm` (Property Manager), `tt` (Tenant), `sp` (Service Provider), `supplier` (parts/material supplier, including retailers), `escrow` (this property’s reserve account), or `other`.

If one side is PM and there is exactly one property manager, the backend associates that account automatically; omit its account field. With multiple managers, call `report(action:get,report:participants,property_id)`, let the user select by name, and supply the corresponding `from_account_id / to_account_id`. Resolve missing associations before proceeding; never guess IDs. Historical transactions retain their original association.

- Ask the user to confirm `financial_category`, not a redundant Transaction Type. `transac_type` is a compatibility field and can be omitted on creation for backend derivation.
- Show participant names/company names, not IDs. Use role labels when names are missing. Clarify identical names before selecting. IDs are internal association values only.

| Scenario | Typical default from -> to |
| --- | --- |
| Rent and tenant-paid late fees | `tt -> po` |
| Management fee | `po -> pm` |
| Repair/commission paid to a service provider | `po -> sp` |
| Owner-paid utilities, insurance, property tax, HOA | `po -> other` |
| Receive a tenant deposit | `tt -> po` |
| Refund a tenant deposit | `po -> tt` |
| PM payout to Owner, only when explicitly requested | `pm -> po` |

Defaults are suggestions, not authorization or identified accounts. Explicit user directions take precedence; clarify conflicts with the business/category. The current operator is not necessarily the payer.

Choose directions by the actual event, not category alone. Refunds usually reverse the original direction. Ask about actual participants for contributions, transfers, reimbursements, and unclear events. Deposits still follow their deposit flow; this table does not authorize duplicate transactions.

Before every new or modified Transaction, show property, date/period, category, amount, and from -> to. Mark suggested directions as awaiting confirmation. Combine with the existing preview approval rather than add another step. A changed direction requires a new prepare and confirmation.

## Tool Reference

| Tool | Actions | Purpose |
| --- | --- | --- |
| `me` | get | Identity, authorized scopes, roles, quota/usage; use at session start or when access is unclear |
| `app_schema` | get | Live actions, data schemas and dependency rules; optionally select one tool |
| `property` | list / get / resolve / prepare_create / confirm_create / prepare_usage / confirm_usage / prepare_update / confirm_update / prepare_delete / confirm_delete | Read/create/edit properties; delete with explicit DELETE, not ordinary archive |
| `asset` | list / get / prepare_create / confirm_create / prepare_update / confirm_update / prepare_delete / confirm_delete | Read/create/edit/delete assets; photos use real uploads |
| `service` | list / get / prepare_create / confirm_create / prepare_update / confirm_update / prepare_delete / confirm_delete | Shared service coverage; use source property for writes |
| `lease` | list / get / prepare_create / confirm_create / prepare_update / confirm_update / prepare_renew / confirm_renew / prepare_status / confirm_status / prepare_archive / confirm_archive | App Lease lifecycle; no arbitrary deletion/restore |
| `inventory` | get / history / prepare_update / confirm_update | Lease inventory/history and authorized PO/PM updates; both parties sign personally in App |
| `tenant` | list / get / prepare_create / confirm_create / prepare_update / confirm_update / prepare_delete / confirm_delete | Tenant profile lifecycle; not user-account creation or access grants |
| `record_images` | get / prepare_attach / confirm_attach | Append images to an existing supported record; preserve business fields and existing attachments |
| `task` | list / get / get_comments / prepare_create / confirm_create / prepare_update / confirm_update / prepare_comment / confirm_comment / get_images / prepare_attach_images / confirm_attach_images | Tasks, comments and images; due_date/report_date use YYYY-MM-DD |
| `transaction` | list / get / search / prepare_create / confirm_create / prepare_update / confirm_update | Cash ledger; list by property/date/category and search note/category/type/participants/amount |
| `reminder` | list / get / link_options / prepare_create / confirm_create / prepare_update / confirm_update / prepare_state / confirm_state; legacy property delete when exposed | Independent or linked reminders; optional scope/source, per-user acknowledgement/snooze and manual reminder completion |
| `document` | list / get / prepare_create / confirm_create / prepare_update / confirm_update | Create from real upload reservation; edit metadata with data; permanent deletion uses record_delete |
| `record_files` | get / prepare_attach / confirm_attach / prepare_detach / confirm_detach | Link uploaded ordinary files or existing Documents to Tasks/Transactions; unlink without deleting source file |
| `report` | get | `report`: options / workspace / participants / deposits; workspace `type`: pnl / ledger / personal_use / tasks / pm_statement |
| `ai_search` | search | Natural-language record search with server semantic parsing and rule fallback |
| `ai_intake` | prepare | Structured drafts for tasks/reminders/documents, including entity recognition; no business write |
| `storage` | create_upload_url / upload / commit_upload / get_upload | Signed-URL upload; query reservation state before recovery; finalize is not association |
| `receivables` | list / leases / lease_plan / history / shortfall / prepare_update / confirm_update / prepare_receipt / confirm_receipt | Receivable plans, charges and receipt allocation |
| `settlements` | get / prepare_update / confirm_update | Settings, owner-payment records and reversals; no actual money transfer |
| `financial_scope` | list / members / allocation_options / prepare_allocate / confirm_allocate / prepare_share / confirm_share | Non-Property-Specific finance and sharing |
| `migration` | schema / prepare_import / confirm_import / status / prepare_resume | Reviewed batch imports using the migration Skill |
| `property_access` | list / invitation / prepare_invite / confirm_invite / prepare_accept / confirm_accept / prepare_revoke / confirm_revoke / prepare_remove / confirm_remove | Owner/manager/view-only invitations and access management; matching account accepts |
| `handover` | options / list / get / history_options / received / file; prepare/confirm create / refresh / resolve / end / invite / accept / revoke | App ownership/management handover, private archives and selected non-financial history |
| `account` | get / prepare_update / confirm_update | Own contact/company fields; not login email, roles, keys or billing |
| `support` | list / get / prepare_create / confirm_create | Own support requests and public replies; no administrator operations |

Still incomplete: rental applications, tenant-private document uploads, official PDF/CSV export, general image removal/replacement, SP operations and plan changes. Agents do not spend money on users' behalf. Discover actions on the live deployment before using them.

## Inventory: Move-In and Move-Out

Owners/PMs resolve the property and select the lease using `lease.list`. Call `inventory.get` with the real `property_id,lease_id`. The App service checks property access and list participants; an existing inventory retains its designated manager and tenant. Only an authorized manager initializes a missing list. The Tenant reviews and signs in App; this Skill does not operate their account.

- `get` returns `{inventory}` with `revision,items,batch` and both signatures; `history` returns `{items}`. `inventory:null` means no inventory has been created, not an existing empty list.
- Use `prepare_update({property_id,lease_id,command})`, show before/after changes and signature impact, obtain approval, then `confirm_update({preview_id,user_confirmed:true})`. Confirm accepts no changed command; prepare again for edits.
- When absent, preview/confirm `{type:"initialize"}`, read revision, then start `{type:"start",stage:"move_in",scope:"list",expected_revision}`. Continue an unfinished batch. For a confirmed list, start a new `inspection` (`scope:"list"`) or `move_out`. Convert older check batches to list-level mode with `prepare_list`.
- Add items using `save_item` without `item_id`; supply the real ID for updates. `entry` contains `name,area,kind,condition,note,photos`. Kinds: `item / access / property_cosmetic`; cosmetic conditions such as walls/floor/paint need no quantity. Conditions: `good / worn / damaged / missing / not_checked`. Access items require actual integer `quantity` (0-999). Use `not_checked` for unknown condition; ask for unknown quantities instead of inserting 0. Preserve existing notes/photos unless explicitly cleared. Use the item-bound upload flow below; MCP never fabricates paths/URLs.
- Every command except initialize needs the latest `expected_revision`. On `INVENTORY_CONFLICT`, read and preview again; never silently replace revision and force a retry.
- Move-out uses `start` with `stage:"move_out"`. If a version is awaiting signatures, explain withdrawal and preserved history; add `withdraw_pending:true` only after explicit agreement. Old photos are not current move-out evidence.
- After recording move-out, use `review_item` with `item_id` only when the user explicitly checks that item. `use_reference` copies an old condition but does not mean checked. Record actual returned quantities for access items. Never infer move-out confirmation from move-in conditions.
- Outside move-out, `unchanged` with `item_ids` references user-confirmed unchanged records. `request_close` with `item_id,reason` requests closure effective after both signatures. The requester can `withdraw_close`; the other party can `reject_close`.
- There is no agent `sign` command. Never generate or sign handwriting for users. PM and tenant review the entire list and sign personally in the app. Editing a signed draft invalidates current signatures and requires both parties to sign again.
- To add or repair item photos, locate the existing item with `inventory.get`. Create a missing item through its normal confirmed `save_item` flow first. Request `storage.create_upload_url` with `property_id,lease_id,item_id,file_name,content_type,size_bytes`; upload and `storage.commit_upload`. Preserve the complete current entry, including its text and photos, append the returned `{path:storage_path,url}` to `command.entry.photos`, and use `inventory.prepare_update` with `command.type:"save_item",item_id,expected_revision,entry`. Preview, explicitly confirm, then read again. Use the inventory's returned source lease ID after renewal. Maximum twelve photos per item/check. Respect the current draft/phase/signature state; a photo repair does not bypass it.

Describe saved changes from the confirm response and remaining checks/signatures. A saved draft is not bilateral confirmation. If live `tools/list` lacks Inventory, inspect connection capabilities/permissions and explain the unavailable operation instead of claiming success.

## File Uploads

When `missing_inputs` requires a file, such as a receipt photo or lease PDF:

1. Ask for the file as a conceptual input. The host's attachment/upload UI provides the picker; the agent does not implement an upload interface.
2. Prefer `storage.create_upload_url`: signed URL valid for ten minutes, bound content type, `upload_url / storage_path`, a 25MB per-file limit, and quota checks.
3. Upload bytes directly to Storage using the signed URL and all returned `upload_headers`, not through model context. Vision interpretation may use model tokens; byte transport does not.
4. Supply uploaded reservation IDs to the applicable record prepare/confirm flow, which commits and associates them. For Inventory photo URLs, use `storage.commit_upload(reservation_id)` first; its returned URL/path/reservation ID only proves an uploaded file, not an attached business record. `storage.upload` already commits bytes and returns these references.
5. Fallback `storage.upload`: <=1MB base64 for headless agents without UI only. On 413, use the signed-URL flow.
6. External URLs are not accepted, preventing SSRF and unauthorized linking.
7. Reservations are actor/property-bound. Cross-property or other users' reservations are rejected; paths must belong to the relevant property/lease.

## Standard Workflows

**Read first:** optional `me.get` -> `property.resolve` -> `task.list / lease.get / report.get` -> explain results.

**Create a task:** understand the request -> resolve property -> `task.prepare_create` -> show preview -> collect `missing_inputs`, uploading photos where required -> approval -> `task.confirm_create(user_confirmed:true)` -> report saved result.

**Update task status/description:** `task.get` -> `task.prepare_update` -> show diff -> approval -> `task.confirm_update`.

**Append a Task comment:** locate the task and property through `task.list` -> `task.get_comments(property_id,task_id)` for history and `can_comment` -> confirm whether the user wants English translation or the original language, following any existing conversation preference -> caller translates only if requested -> `task.prepare_comment(property_id,task_id,comment)` -> show property name, task name, full comment, and storage language -> explicit approval -> `task.confirm_comment(preview_id,user_confirmed:true)` -> report saved result.

- Comments append to shared Web/mobile history without overwriting description or changing status. Do not substitute `prepare_update` for commenting.
- Only property Owners/Managers can comment. View-only access permits queries only. The backend supplies author and time.
- Comments support text up to 10,000 characters and up to five uploaded images through `image_reservation_ids`. Text and images are saved together in one comment entry. Changed text or images require a new prepare, not a confirm-time override.
- IDs are internal only. Never write without confirmation; use `get_comments` to verify the saved result.

**Reminders:** Read the [unified reminder workflow](https://handout.us/skills/handout-property-records/references/reminders.md) for independent/linked reminders, source-date rules and per-user state. Property/source are optional. Prepare, show the exact scope/date/impact, obtain approval, then confirm the unchanged preview. Reminder completion is separate from business or payment completion.

**Save a document:** upload to the property's `storage_path` -> `document.prepare_create` -> approval -> `confirm_create`.

**Record a transaction:** identify property, amount, purpose, actual payment date, payer/recipient, and receipt -> resolve property -> `transaction.prepare_create` with positive `amount` (at most two decimals), `transac_date:YYYY-MM-DD`, canonical `financial_category`, and details in `note` -> preview -> approval -> `transaction.confirm_create` -> report result.

**Reconcile a PM statement:** filter rows by cash basis (actual received/paid > 0; exclude pass-through rows), classify, show the entire proposed batch, then confirm each transaction. If income minus expenses differs from the payout, pause and clarify rather than force entries.

**Attach a receipt:** request the image -> `storage.create_upload_url` -> direct upload -> include `reservation_id` alongside the transaction text in `prepare_create`, or in `prepare_update` for the existing transaction -> show the combined preview -> explicit approval -> matching confirm commits and associates the image. Alternatively, use an already committed `picture_url`. Empty `picture_url:''` explicitly clears a receipt. Changed text, targets, or attachments require a fresh prepare; never fabricate URLs or paths.

**Correct a transaction:** find it with `transaction.get / search`, checking amount/date/note -> `transaction.prepare_update` -> show before/after diff -> approval -> `transaction.confirm_update`. Immutable fields: `created_by,created_datetime,property_id,transac_id`. Unchanged values return `NO_EFFECTIVE_CHANGES`.

### Query Reports

Use `report` with `action:"get"`. Report queries are read-only; no prepare/confirm is needed.

| User need | report | type |
| --- | --- | --- |
| Property income, expenses, P&L | `workspace` | `pnl` |
| Personal Use property costs | `workspace` | `personal_use` |
| General Ledger transaction details | `workspace` | `ledger` |
| Task History | `workspace` | `tasks` |
| Current PM account activity and management-fee income | `workspace` | `pm_statement` |
| Property deposit overview/history | `deposits` | Omit |
| Property payment participants | `participants` | Omit |

- Resolve the intended scope using `report:options`. Use `property_id` for one property or `property_ids` for multiple properties, never both. A requested Owner portfolio can aggregate accessible properties; do not claim every accessible property is owned by the caller or prorate by ownership share. Omitted scope must not be presented as one property's result.
- Workspace uses `from / to` (YYYY-MM-DD). Convert phrases such as "last month" to explicit dates and state property scope, period, currency, and recorded basis.
- `pm_statement` uses the current account's payment scope, not property P&L. Collected rent and owner payouts are not PM-owned income.
- Deposits/participants accept `property_id`, not workspace type/date filters; the current adapter does not apply those dates. Do not present current deposit results as historical balances.
- Use `receivables` for unpaid rent; exclude it from cash-basis P&L.
- Do not use legacy HTTP names `income-expense / task-status` as MCP report/type values, or put `workspace` into `type`.
- Example: `{"action":"get","report":"workspace","type":"pnl","property_id":"<resolved property ID>","from":"2026-09-01","to":"2026-09-30"}`. Replace with the actual requested scope/dates.
- Explain income, expenses, net result, or details only after a successful query. Distinguish empty results from denied access or query failures; an error is not zero income.

**Turn text into a draft:** `ai_intake.prepare` -> show structured draft -> user approval -> corresponding business tool's prepare/confirm.

## Guardrails

- Follow server `missing_inputs`; never invent inputs or fields.
- Resolve/search or ask when `tenantId,property_id,transaction_id` is uncertain.
- Stay within the user's authorized properties; no cross-tenant access.
- Every business create/update requires an explicitly approved preview.
- Record actual receipts; unpaid tenant charges belong in receivables. Settlement-generated pending owner payments are not paid cash.
- Use `unclassified` for uncertain categories; clarify uncertain amounts/dates.
- Task/Transaction/Document deletion uses `record_delete.prepare_delete` then `record_delete.confirm_delete`; Property/Asset/Service/Reminder/Tenant profiles use their own exposed delete actions. Show the exact target and file warning, and obtain explicit user confirmation first. Never reuse approval for another target. Workflow-linked financial records require the existing reversal workflow; assigned tasks must be cancelled instead. Unlink associated Documents before deleting a Task or Transaction.
- Viewers are read-only. Explain `PROPERTY_READ_ONLY` or other access errors without bypassing them.
- Plan/payment changes are app-only; the agent does not spend money.
- Every call is auditable through a redacted summary.

## Error Reference

| Error | Meaning | Action |
| --- | --- | --- |
| 401 | Missing, expired or revoked connection | Start the client OAuth reconnect flow; use connection-key management only for API-key clients |
| 403 | Unauthorized, cross-tenant, or viewer write | Explain; use read-only access or direct the user to the app |
| 429 | Rate limit | Back off and retry later |
| Preview errors | Claimed, expired, or mismatched preview | Prepare again; do not reuse an invalid preview ID |
| 400 validation | Invalid amount, date, or category | Ask the user to correct fields using the server message |

---

This Skill is a documentation-only playbook. It contains no credentials and executes no code. Live MCP behavior is authoritative; follow prepare's `missing_inputs` when fields are uncertain.


## Non-Property-Specific finance

Use `financial_scope(action:list)` or `report(action:get,report:options)` to discover authorized Non-Property-Specific entries. These are financial scopes, not physical properties; use the returned ID as `property_id` for the existing transaction prepare/confirm flow. Do not create a fake house or ask users to create a ledger. Confirm which Owner or PM business the displayed scope represents.

From/To determine cash direction relative to the selected Owner or PM principal. Category determines business meaning; deposits and transfers remain non-operating. PM-owned business costs are separate from Owner costs and custodial funds.

For property allocation, inspect `financial_scope(action:allocation_options,property_id,transaction_id)`, then `prepare_allocate` with `expected_revision` and `allocations:[{property_id,amount}]`. Show each property, amount, remaining balance, and whether the allocation is Owner cost or PM internal cost. Only `confirm_allocate` with `preview_id` and `user_confirmed:true` applies the reviewed allocation. On conflict refresh the original transaction and prepare again. Allocation neither copies the cash transaction nor transfers money; PM allocation never charges an Owner.

Only owning principals can share a scope. Read `financial_scope(action:members,property_id)`, then `prepare_share` with the complete `member_emails` list. Explain that listed members access all transactions in this scope, and omitted members lose explicit access. Confirm the exact list before `confirm_share`. Property access does not automatically imply access to an entire Owner ledger.

## Rental and Personal Use properties

Ask the user to choose `rental` or `personal_use` when creating each property. Personal Use includes homes, holiday homes and family occupancy; ownership and use are separate. Read the property's usage from `property.list/get` before offering rental operations. Keep maintenance, assets, documents, reminders and expenses available for both uses.

For a usage change, use `property.prepare_usage`, show the current/proposed use and effective date, obtain explicit confirmation, then call `property.confirm_usage` with the preview ID. Active or Scheduled leases must end first. Use the server-returned history for date-based reporting; historical rental records remain readable.

For Personal Use costs, discover eligible properties with `report.get` (`report: options`), then request `report: workspace`, `type: personal_use`, properties and dates. Explain direct expenses, Owner allocations, incidental income and net cost. Rental reports default to Rental activity; use only combined-scope filters actually declared by the live action schema. Allocation rows follow the target property's use on the original transaction date; PM internal costs remain separate.

## Delete records and files

Use the `record_delete` tool with `action: "prepare_delete"`, `type` (`document`, `task`, or `transaction`), `property_id`, and `record_id`. For tasks use the mxrecord id returned by `task.list`. For documents, `source` defaults to `documents`; use `property_photos` for property media or `lease` plus `lease_id` for lease documents.

Present the returned record summary and warning to the user. Only after explicit approval call `action: "confirm_delete"` with the returned `preview_id` and `user_confirmed: true`. The prepared target cannot be changed at confirmation. File deletion is permanent; task attachments are retained. If `FILE_CLEANUP_PENDING` is returned, retry the same confirmation to finish the approved cleanup. A changed record requires a new preview and approval.

In the app, open the business record and choose already uploaded images from its property library. Use thumbnails and filenames to select images; do not ask users to enter system IDs. Associations leave files in their original library. Financial record associations require financial access.
