> **Correction, 2026-09-15.** The functional coverage map below originally repeated
> run 1's pre-reconciliation figure for core write coverage (81.4%). The reconciled
> figure used by check C1.2 and by the published score is 85.2%, and the map now
> says so. **No mark or score changes: Propertyware remains 71 (C-).**

# API Report Card: Propertyware Open API

## Run metadata
- Methodology version: 1.1
- Evaluating model: claude-opus-5 (the model serving this session may differ)
- Date run: 2026-09-14
- Provisional evidence-packet version or ID: PW-2026-09-14-p1
- Final evidence-packet version or ID: PW-2026-09-14-final (frozen 2026-09-14 after the controlled verification pass)
- Independent grading runs: **three**, all against this frozen packet and the same fixed coverage classification. Runs 2 and 3 were performed by separate evaluator instances with no access to run 1's marks or to each other's. Pre-reconciliation totals 66 / 70 / 68; **22 of 27 checks unanimous**; five 2–1 disagreements, each one step on the scale, all resolved against the packet. Two run-1 marks were corrected in reconciliation (C1.2 and C2.7, both partial → yes). Full comparison in the reconciliation log.
- Evidence-discovery mode: tool-enabled discovery
- Evidence tier: **Baseline verified**
- Live-write method and safety: controlled live — operator authorization recorded in-session before any write; all writes confined to two `APITEST-DELETE` fixture contacts created for the test; no money-moving, notifying, bulk or irreversible operation against real data; **cleanup could not be verified** (see below), so the abort rule was invoked and write testing stopped
- Minimum live-test battery: **complete** — steps 1–6 run; step 7 (idempotency) N-A because the API documents no idempotency mechanism; step 8 (webhooks) N-A because the API offers no webhooks or events
- Live tests performed: authenticate; read and page a core collection; incremental `lastModifiedDateTimeStart` query; four distinct deliberate-error probes; response-header inspection for rate-limit and traceability signals; create a core resource (201); update it (200); identical-create duplicate observation; delete attempts with both supplied credentials
- Live tests not possible: delete/lifecycle execution — every `DELETE` in the API is gated behind an opt-in beta this tenant is not enrolled in, and both supplied credentials were refused
- Documentation-graded checks (baseline verified): **C1.3** (lifecycle/delete actions) — documented but not observable; the live evidence establishes only that the operations are unavailable to this tenant's credentials
- Access route note: this session's cloud container is blocked from `*.propertyware.com` by egress policy, so every live call was issued as an authenticated same-origin `fetch()` from Claude's built-in browser on the operator's PC. Credentials were supplied by the operator; no account or key was created by the grader.

### Cleanup status — disclosed
Two fixture contacts, **[test contact id withheld]** and **[test contact id withheld]**, remain in org [org id withheld] because the API refused `DELETE` for both supplied keys. Both were left inert: no email, no phone, `emailAlerts:false`, unattached to any lease, portfolio, vendor or prospect, and commented `APITEST-DELETE - safe to delete (API grader fixture 2026-09-14)`. The operator was notified in-session with both IDs. Buildings (8) and units (20) returned to their pre-test baseline; contacts stand at 32 against a baseline of 30. A pre-existing fixture from an earlier run, contact [test contact id withheld] `APITEST DELETE`, was found and deliberately left untouched.

## Final evidence packet manifest
- https://app.propertyware.com/pw/apidocs/ — Open API, powered by Propertyware (1.0); Redoc portal, publicly reachable without login; embedded OpenAPI 3.0.0 document (124 paths, 177 operations, 141 schemas) with "Download OpenAPI specification" emitting `swagger.json`
- The embedded spec's `info.description` (46,775 chars) — Introduction, Account Configuration, Enabling the API, API Keys, Creating API Keys, Keeping API Keys Safe, How to Make a Request, API Overview, Base URL, API Versioning, Releasing Changes to the API, Authentication, Rate Limiting (FUTURE), Bulk Request Options, Pagination, Sorting Results, Response Codes, API Date Format, Support, Changelog
- https://app.propertyware.com/robots.txt — `User-agent: *` / `Disallow: /pw/`
- https://www.propertyware.com/pricing/ — package pricing and the "For Enterprise/API: Add $1 per unit/per month to any package" footnote
- https://www.propertyware.com/open-api/ — Open API marketing page
- https://www.propertyware.com/robots.txt — no `llms.txt` directive; `/llms.txt` and `/llms-full.txt` return 404 on both domains
- https://status.propertyware.com/ and https://status.propertyware.com/uptime — Atlassian Statuspage with a dedicated "Open API" component, daily incident history, monthly uptime percentages
- https://support.propertyware.com/s/article/Who-do-I-contact-to-request-a-sandbox-testing-account-for-API — sandbox/testing account obtained by emailing Sales Ops Support (published 2024-05-13)
- Live observations against `https://api.propertyware.com/pw/api/rest/v1`, org [org id withheld], 2026-09-14 — recorded in the run's evidence log and write log

## Evidence-amendment log
- **C3.5** — support article "Who do I contact to request a sandbox/testing account for API?" added during the controlled verification pass. Changed the provisional mark from `N-A` (no sandbox evidenced) to `partial`: a separate test environment does exist, but only by request, and nothing first-party documents credential or data isolation.
- **C1.4 / C2.8** — targeted first-party search for Propertyware webhook or event documentation returned nothing on either domain, confirming the spec term scan (`webhook`, `subscription` = 0 occurrences). C1.4 confirmed partial on the polling clause; C2.8 confirmed N-A.
- **C4.3** — `llms.txt` and `llms-full.txt` checked on both `app.propertyware.com` and `www.propertyware.com` during the verification pass; all four returned 404. Confirmed `no`.
- No other sources were added, and no source was removed after provisional scoring.

## API eligibility
- Qualifying API: **yes**
- API operator: Propertyware (RealPage, Inc.) — docs portal "Open API, powered by Propertyware", contact `support@propertyware.com`; support articles carry the RealPage, Inc. proprietary notice
- Access or credential issuer: the evaluated vendor — account-level API keys are created by an administrator inside the product at Administration Setup > API Keys (docs, "Creating API Keys")
- Eligibility basis: a first-party REST interface at `https://api.propertyware.com/pw/api/rest/v1` exposing Propertyware's own property-management functions across 177 operations, with an openly published OpenAPI 3.0.0 document and vendor-issued credentials. Confirmed live: `GET /portfolios` returned HTTP 200 with the operator's data on 2026-09-14.

## Context
- Software category: **Accounting/PMS**
- What the API is for and its core objects and workflows: the Propertyware Open API is the programmatic interface to a single-family and small-multifamily property management platform, exposing the records a management company runs on — portfolios, buildings, units, leases, contacts, vendors, work orders, bills and the accounting ledger. Its core objects are properties, units, leases, tenants/contacts, lease ledgers (charges, payments, journal entries, adjustments, refunds), the general ledger and its chart of accounts, bills and bill payments, and work orders. Its primary workflows are reading core records, posting ledger charges and payments, creating and updating leases, and creating and progressing work orders.

## Provider and property-management fit
- What this product is: a property management software platform for single-family and small-multifamily operators, with an add-on REST API for two-way data exchange — https://www.propertyware.com/open-api/
- Bank status, when relevant: **not a bank** — no first-party evidence identifies Propertyware or RealPage as a bank or as a provider of regulated banking services; the API's "bank accounts" are general-ledger accounts of `accountType: "Bank"` inside the accounting system, not deposit accounts
- Who provides any bank account or regulated banking service: **none** — the API records bank accounts, deposits and payments as accounting entries; no first-party material in the evidence packet identifies a bank partner for an API-provisioned account
- What the customer actually receives: a software subscription to the Propertyware platform plus, for an additional per-unit fee, credentialed API access to that account's own data (https://www.propertyware.com/pricing/)
- Property-management fit: **PM-specialized** — property management is the product's central purpose and the API's object model is property-management-native throughout (portfolios, buildings, units, leases, lease ledgers, work orders, owner draws)
- Documented PM-specific workflows: lease creation and update with move-in/move-out, notice-given and scheduled-move-out dates and `reasonForLeaving` (`POST`/`PUT`/`PATCH /leases`, `GET /leases/statuses`); posting lease charges, payments, journal entries, adjustments, discounts, credit memos and tenant refunds (`/leases/charges`, `/leases/payments`, `/leases/journalentries`, `/leases/adjustments`, `/leases/discounts`, `/leases/creditmemos`, `/leases/refunds`); owner draws and owner contributions (`/accounting/ownerdraws`, `/accounting/ownercontributions`); management fee rules and property managers per building (`/buildings/{id}/managementfees`, `/buildings/{id}/managers`); work orders with tasks, vendor assignment fields and close (`/workorders`, `/workorders/{id}/tasks`, `/workorders/closeworkorder/{id}`); prospects with statuses and campaigns; inspections (read-only); late-fee rules and auto-charges per lease
- Trust or fiduciary workflow support, when relevant: **limited** — the API exposes security-deposit fields (`secDepAmount`, `secDepChargeDate` on lease create) and a portfolio-level `defaultSecurityDepositBankAccountID` distinct from the operating account (observed live on `GET /portfolios`), which is the shape of client-fund segregation. But no first-party material documents trust accounting, escrow or three-way reconciliation as an API workflow, and there is no reconciliation object at all. Deposit segregation is configurable; verifying it is not an API capability.
- Operational role and dependencies: Propertyware is the system of record and its API is the two-way pipe into that record; an operator building on it still needs their own scheduler and datastore for syncing (there are no webhooks), a separate reconciliation process outside the API, and the paid API add-on plus, for any delete or close operation, enrolment in Propertyware's opt-in beta program.

## Coverage classification (fixed before inspection)

Adopted unchanged from the methodology's default Accounting/PMS classification; no deviation, and it was recorded before the API surface was inspected.

| Object or workflow | Class | Weight | Present / read-only / absent |
|---|---|---|---|
| Properties (buildings) | critical | 3 | Present — `GET`/`POST`/`PUT`/`PATCH`/`DELETE`(beta) `/buildings`, plus bulk create |
| Units | critical | 3 | Present — `GET`/`POST`/`PUT`/`PATCH`/`DELETE`(beta) `/units`, plus bulk create |
| Leases | critical | 3 | Present — `GET`/`POST`/`PUT`/`PATCH`/`DELETE`(beta) `/leases` |
| Tenants | critical | 3 | Present as Contacts — `GET`/`POST`/`PUT`/`PATCH`/`DELETE`(beta) `/contacts`, with `namedOnLease`, `type`, `/leases/{id}/contacts` |
| Lease ledgers / transactions | critical | 3 | Present — charges, payments, journal entries, adjustments, discounts, credit memos, refunds |
| General ledger | critical | 3 | Present, read-only by design — `GET /accounting/generalledger`; chart of accounts writable at `/accounting/glaccounts` |
| Bank accounts | critical | 3 | Partial — only as GL accounts of `accountType: "Bank"` (observed); no dedicated bank-account resource, balance, register or statement |
| Owners | important | 2 | Present — owner-typed contacts, `portfolios[].owners`, owner draws and contributions |
| Bills | important | 2 | Present — `/bills` full CRUD plus credits, payments, vendor checks, bulk |
| Payments | important | 2 | Present — lease payments, bill payments, vendor checks, owner draws, bank deposits |
| Applicants | important | 2 | Partial — Prospects only (`GET`/`POST`/`PATCH`, statuses, campaigns); no application or screening object |
| Work orders / tasks | important | 2 | Present — `/workorders` and `/workorders/{id}/tasks`, plus bulk create and close(beta) |
| Reconciliation | important | 2 | **Absent** — no reconciliation resource, no cleared/uncleared state, no statement matching |
| Files | optional | 1 | Present — `/docs` upload, update, download, delete(beta) |
| Communications | optional | 1 | Present — conversations on seven entity types; creatable on leases and prospects, read-only elsewhere |
| Custom fields | optional | 1 | Present — `/customfields/{entityType}/definitions`, `PUT …/customfields` on seven entity types (observed live) |
| Associations | optional | 1 | Present, largely read-only — `attachedLeases`, `attachedPortfolios`, `attachedVendors`, `attachedProspects` (observed); set indirectly via parent fields |
| Inventory | optional | 1 | **Absent** — no asset or inventory endpoints (though `Asset` and `ServiceAgreement` appear as custom-field entity types) |
| *Workflow:* read core records | critical | 3 | Present — all 34 collections with filters, pagination, sorting |
| *Workflow:* post ledger charges and payments | critical | 3 | Present — `POST /leases/charges`, `POST /leases/payments`, `POST /leases/journalentries`, bulk charges |
| *Workflow:* create and update leases | critical | 3 | Present — `POST`/`PUT`/`PATCH /leases` |

## Functional coverage map
- **Core objects:** 16 of 18 classified objects are present; bank accounts and applicants are materially partial; reconciliation and inventory are absent. Weighted object coverage **32.5 / 38 = 85.5%**, no critical object absent.
- **Primary operational workflows:** read core records ✔; post ledger charges and payments ✔; create and update leases ✔; create and progress work orders ✔. Weighted mutable coverage **85.2%** after reconciliation, as scored in C1.2 below (run 1's original objects-only construction gave 28.5 / 35 = 81.4%; see the reconciliation note under C1.2).
- **Principal lifecycle changes:** lease move-out and status change ✔ (full field set); charge/payment void and reversal partial (delete is beta-gated, credit memos read-only); work-order completion beta-gated; bill, building, unit, contact, document, deposit and owner-contribution deletes all beta-gated; prospect status change ✔. Weighted lifecycle coverage **10.5 / 17 = 61.8%**, no critical lifecycle action absent.

## Category 1: Functional Coverage and Usefulness: 11.3/15
- **C1.1 Object coverage: yes** — weighted coverage = 85.5%, no critical object absent. Presence and operations cited per object in the coverage classification above; every collection was additionally confirmed live on 2026-09-14 (`X-Total-Count`: portfolios 6, buildings 8, units 20, leases 3, contacts 30, vendors 12, work orders 11, bills 69, GL accounts 59, lease charges 4, lease payments 4, owner draws 1, prospects 1, inspections 0). [app.propertyware.com/pw/apidocs — paths inventory; live GETs against api.propertyware.com/pw/api/rest/v1, 2026-09-14]
- **C1.2 Core operational actions: yes** — weighted coverage = 85.2% over the predetermined mutable objects **and** mutable workflows (the "read core records" workflow and the computed general-ledger transaction object are N-A within this write sub-map). All three predetermined critical write workflows are present and documented, and none is absent. *Reconciliation note: run 1 originally scored this `partial` at 81.4% using an objects-only sub-map; runs 2 and 3 independently included the workflows, as the methodology's phrase "mutable workflows/objects" directs, and both cleared the 0.85 threshold. Resolved to `yes`. The sub-map's membership is underspecified in methodology v1.1 — see the disagreements section.* Deductions within the map: `POST /buildings` returned HTTP 500 on five distinct payloads, including values mirrored exactly from existing records in the same tenant, while the same credential created a contact successfully — a server-side fault, not a permission failure; conversations are creatable only on leases and prospects; associations are effectively read-only; reconciliation and inventory have no write path. Create and update were observed live: `POST /contacts` → 201 (id [test contact id withheld]), `PATCH /contacts/[test contact id withheld]` → 200 with the change confirmed by read-back. [live write log, 2026-09-14; `POST`/`PUT`/`PATCH` operations per the spec]
- **C1.3 Delete or lifecycle actions: partial** — weighted coverage = 61.8%, no critical lifecycle action absent. Lease lifecycle is genuinely well covered: `PATCH /leases` accepts `status`, `moveOutDate`, `noticeGivenDate`, `scheduleMoveOutDate` and `reasonForLeaving`, with `GET /leases/statuses` enumerating the states. Everything else is throttled by one gate: **all 18 `DELETE` operations plus `PUT /workorders/closeworkorder/{id}` carry "Write access is only available to customers who have opted in to our beta program. Please reach out to support if you'd like to be included."** — 25 of the 99 write operations in the spec. Live-observed: `DELETE /contacts/{id}` returned 404 for both the write key and the delete key, on a record confirmed present by a 200 GET. **Documentation-graded and flagged** — the capability is documented, but its behaviour when enabled could not be observed. [spec operation descriptions; live write log, 2026-09-14]
- **C1.4 Change notification: partial** — no webhooks, events or subscriptions exist anywhere in the API (`webhook` and `subscription` each appear 0 times across the full spec; no events tag; targeted first-party search during the verification pass found no webhook documentation on either domain). The only mechanism is incremental polling, and it is a good one: all 34 collection endpoints carry an identical `lastModifiedDateTimeStart` / `lastModifiedDateTimeEnd` pair with `offset`/`limit` and `orderby`, with zero exceptions. Verified live on `/leases`: unfiltered `X-Total-Count` 3; `lastModifiedDateTimeStart=2026-01-01` → 2, earliest `lastModifiedDateTime` 2026-07-10T22:45:49Z; `…Start=2030-01-01` → 0. This detects creates and updates but **not deletes** — a removed record simply disappears, with no tombstone or deleted-since filter. [spec parameter inventory; live queries 2026-09-14]

Score math: earned 3.0 of 4 applicable checks (C1.1 1 + C1.2 1 + C1.3 0.5 + C1.4 0.5); unrounded fraction = 0.75; category points = 0.75 × 15 = 11.25 → **11.3/15**; verification coverage = 4/4 = **100%**

**What this means for you:** almost everything your business runs on is reachable, and you can write to it, not just read it — leases, charges, payments, bills, work orders, owners, tenants. Two real gaps: there is no reconciliation object at all, so bank reconciliation stays a manual job in the product, and there is no way to be *told* when something changes. Every integration you build will be a scheduled poll, and it will silently miss deletions. The bigger practical brake is that deleting or closing anything requires Propertyware to enrol you in a beta program first — as your own two keys demonstrated, without that enrolment your automation can create and update but can never clean up after itself.

## Category 2: API Design, Reliability, and Operability: 5.9/10
- **C2.1 Modern API conventions: yes** — resource-oriented REST over HTTPS with standard verbs (GET 78, POST 41, PUT 30, PATCH 10, DELETE 18), JSON payloads, standard status codes, and a published OpenAPI 3.0.0 document. Docs: "The Propertyware API is built upon standard REST conventions. It's designed to use consistent resource-oriented URLs, accept and return JSON-encoded messages, and use standard HTTP status codes and verbs." Confirmed live throughout the battery. [spec `info.description` § API Overview; live calls 2026-09-14]
- **C2.2 Consistent typing: partial** — core financial and identity fields are properly typed and matched live reads: `ChargeDTO.amount` and `PaymentDTO.amount` are `number/double`, ids are `integer/int64`, dates are `string/date`, `LeaseDTO.active` is a real boolean. The limitation is a small set of non-core inconsistencies: `BuildingDTO.targetDeposit` is a **string** while its replacement `targetDepositAmount` is `number/double` (the spec itself marks the former deprecated); `SaveBuildingDTO.publishedForRent` is a **string enum `["Yes","No"]`** where a boolean is meant, alongside real booleans like `active` on the same object; `managementFeeType` (string) is deprecated in favour of `managementFeesType`; and `GET /health` declares `content-type: application/json` but returns the bare non-JSON literal `Success`. [spec component schemas; live `GET /health` 2026-09-14]
- **C2.3 Structured errors: partial** — a structured envelope is declared (`ErrorResponse` = `{errorCode, userMessage, errors[]}`) and referenced by 400/401/403 on all 177 operations, and at its best it is genuinely good: `POST /contacts` with a missing address returned 400 with field-level entries `{"key":"City","message":"City is required"}`. Four exact limitations: (1) **`errorCode` was the constant `"1001"` on every structured error observed** — missing auth headers, not-found, missing required parameter, invalid date range, validation failure and a 500 all returned 1001, so it carries no machine-actionable information; (2) `GET /leases?limit=notanumber` returned **400 with a completely empty body** (`content-length: 0`, no content-type); (3) a wrong-case path returned a **6,591-byte HTML page**, not the JSON envelope; (4) `DELETE` on a record the caller may not delete returned **404 with `userMessage: null`**, byte-identical to the response for a non-existent id, rather than the 403 the spec declares for that operation. A documentation contradiction was also observed: the docs state that missing credentials return 401, but the API returned 400. [spec `ErrorResponse`; live error probes 2026-09-14]
- **C2.4 Duplicate prevention: no** — `idempot` appears **0 times** in the entire spec; no idempotency key, no request-identifier mechanism, no documented natural idempotency on consequential writes. Observed live: a byte-identical `POST /contacts` sent twice created two distinct records (ids [test contact id withheld] and [test contact id withheld]); contact count went 30 → 31 → 32. Financially consequential creates — `POST /leases/charges`, `POST /leases/payments`, `POST /bills/payment` — carry no protection against a retried request posting twice. [spec term scan; live write log 2026-09-14]
- **C2.5 Graceful handling under load: partial** — 429 is documented in the Response Codes table with recovery advice ("Too many requests against the API too quickly. We recommend an exponential backoff of your requests"), which is qualitative, not numeric. There is **no `Retry-After` header** (`Retry-After` = 0 occurrences in the spec, and none observed on any live response) and no rate-limit headers of any kind on the responses observed. The concrete numbers — "10 concurrent requests per second", "retry after a short interval (~200ms)" — exist only inside an **HTML comment** in the spec's description under a heading marked "Rate Limiting (FUTURE)", so they do not render on the public documentation page. [spec `info.description` § Response Codes and the commented § Rate Limiting (FUTURE); live response headers 2026-09-14]
- **C2.6 Pagination for large collections: yes** — `limit` (default 100, maximum 500, larger values coerced to 500) and zero-based `offset` are documented, with the full result count returned in the `X-Total-Count` header and `orderby=field asc|desc` available on every collection for deterministic ordering. Verified live: `GET /buildings?limit=2&offset=0&orderby=id asc` and `offset=2` returned disjoint, correctly ordered id sets against a consistent `X-Total-Count: 8`. [spec § Pagination and § Sorting Results; live paging 2026-09-14]
- **C2.7 Bulk or incremental export: yes** — the check's `yes` clause names "documented incremental sync via updated-since plus pagination" as a qualifying mechanism, and that is exactly what is documented: a `lastModifiedDateTimeStart`/`End` window with `offset`, `limit` and `orderby` on **all 34 collection endpoints with zero exceptions**, a total-count signal in `X-Total-Count`, all set out under a heading called "Bulk Request Options", and live-verified on `/leases`. A full dataset is retrievable without per-record calls. *Reconciliation note: run 1 originally scored this `partial` on the competing clause "no dedicated bulk/export path"; runs 2 and 3 independently applied the `yes` clause. Resolved to `yes`.* Two real ceilings remain and are recorded here rather than in the mark: there is no async export job or dedicated export endpoint (the `/bulk` endpoints are create-only), pages cap at 500 records, and the general ledger — the dataset most likely to be warehoused — **rejects any date window longer than 30 days** (`GET /accounting/generalledger` without a range returned 400 "last modified date range / post date range is invalid or Date range more than 30 Days"; a 25-day window returned 200). A full-year GL extract therefore requires at least twelve sequential windowed pulls per filter. [spec § Bulk Request Options and `/accounting/generalledger` parameters; live queries 2026-09-14]
- **C2.8 Webhook security and delivery reliability: N-A** — the vendor offers no webhooks or events; their absence is penalised in C1.4 and is not double-counted here. [spec term scan: `webhook`, `subscription` = 0; verification-pass search found no first-party webhook documentation]
- **C2.9 Concurrency and conflict control: no** — no optimistic concurrency of any kind: `ETag` and `If-Match` each appear 0 times in the spec, no version or revision field exists on any DTO, and no 409 conflict semantics are documented (the single occurrence of "409" in the spec is one member of a generic HTTP-status enum inside a DTO, not a declared response). No concurrency limits or behaviour are documented. Two integrations updating the same lease will silently overwrite each other. [spec term scan and response inventory]
- **C2.10 Versioning and backward compatibility: yes** — an explicit major version is carried in the path (`/pw/api/rest/v1`) and is mandatory: "Any request submitted without the version in the URL path will result in a `404` error response code." The compatibility policy is written out and enumerated on both sides — backward-compatible changes (new resources, new optional parameters, new response properties, property reordering) apply to the current version, while backwards-incompatible changes (removing or renaming a property, adding a required parameter, changing enum values) trigger a new version with "full reference documentation and an upgrade guide". Notice is committed to: "We'll provide advance notice for all API releases–regardless of the type of modifications being made", and field-level deprecations are marked in the schemas themselves. [spec `info.description` § API Versioning and § Releasing Changes to the API]
- **C2.11 Request traceability: no** — no request or correlation identifier is documented (`X-Request`, `requestId`, `correlation` = 0 occurrences in the spec) and none was present on any live response. Headers observed across every 2xx and 4xx were limited to `content-type`, `content-length`, `date`, `server-timing`, `strict-transport-security`, `x-content-type-options`, `vary`, and `x-total-count` on collections. The only per-request token is the Akamai `ak_p` value embedded in `server-timing` — a CDN diagnostic, not a documented support identifier. When a call fails you have nothing to hand Propertyware support but a timestamp. [spec term scan; live response headers 2026-09-14]
- **C2.12 Service availability and status transparency: yes** — https://status.propertyware.com/ is a public Atlassian Statuspage carrying a **dedicated "Open API" component** alongside the platform, with per-day incident history and a subscribe option; https://status.propertyware.com/uptime publishes monthly uptime percentages (July, August and September 2026 each 100%). [observed 2026-09-14]

Score math: earned 6.5 of 11 applicable checks (C2.1 1 + C2.2 0.5 + C2.3 0.5 + C2.4 0 + C2.5 0.5 + C2.6 1 + C2.7 1 + C2.9 0 + C2.10 1 + C2.11 0 + C2.12 1); C2.8 is N-A and excluded; unrounded fraction = 6.5/11 = 0.590909; category points = 0.590909 × 10 = 5.909091 → **5.9/10**; verification coverage = 11/11 = **100%**

**What this means for you:** the shape of the API is fine — it is proper REST, it pages predictably, the version contract is clear, and there is a real status page that calls out the API separately. The operability layer underneath it is where this loses most of its points, and the three misses compound. Nothing stops a retried payment from posting twice. Nothing stops two integrations from overwriting each other on the same lease. And when something does go wrong there is no request ID to give support. For a read-and-report integration none of that matters much; for anything that writes money into your ledger, it means you have to build the safety rails yourself — your own de-duplication keys, your own write serialisation, your own logging.

## Category 3: Access Control and Safe Automation: 4.5/5
- **C3.1 Read-only credentials: yes** — "You can restrict a key to particular Propertyware entities or to read-only access (GET resources only)." [spec `info.description` § Keeping API Keys Safe, Recommended Practices]
- **C3.2 Scoped credentials: yes** — fine-grained resource *and* action scoping. Key creation is explicitly per-resource: "choose which pieces of Propertyware data you want this API key to have access to by selecting the corresponding radio buttons", and the read-only restriction above scopes by action. The spec documents a required permission on every operation individually — for example `GET /accounting/generalledger` "Required permission: GENERAL LEDGER – Read", `POST /buildings` "BUILDINGS – Write", `DELETE /contacts/{id}` "CONTACTS – Delete" — so the permission model is per-resource-per-verb. Corroborated live: the operator's write key created and updated a contact successfully but was refused on `DELETE`, while the same key read every collection. [spec § Creating API Keys and § Keeping API Keys Safe; per-operation descriptions; live write log 2026-09-14]
- **C3.3 Multiple keys: yes** — keys are created individually with a name and description precisely so you can tell them apart: "Enter a clear, memorable name and description for your API key. It'll make it easier to locate the right key when you make a request." Confirmed live: the operator supplied two distinct credential pairs and both authenticated independently against the same org. [spec § Creating API Keys; live calls with both keys 2026-09-14]
- **C3.4 Rotation and revocation: yes** — self-serve, inside the product, with no support ticket: keys are created and deleted by an administrator at Administration Setup > API Keys, revocation is immediate and enforced ("try to use information that's linked to a deleted key, the API will return a `401` response code"), and regular rotation is a documented recommended practice ("Establish a process to regularly recreate your client IDs and secrets from your Propertyware account"). The limitation worth knowing is that rotation is delete-and-recreate rather than rotate-in-place, so there is no overlap window — but the check measures whether rotation and revocation are self-serve, and they are. [spec § API Keys, § Creating API Keys, § Keeping API Keys Safe]
- **C3.5 Test and production isolation: partial** — a separate test environment does exist: "Who do I contact to request a sandbox/testing account for API? … You may reach out to Propertyware Sales Ops Support: salesopssupport@propertyware.com" (published 2024-05-13). The exact limitation is that this is the *only* first-party evidence of it. The API documentation portal never mentions a sandbox; the sole documented base URL is described as "the base URL for production environment API requests"; there is no documented test-versus-live credential scheme, no test-mode flag, and nothing describing how test data is isolated. A sandbox you have to email sales operations to obtain, and that no developer-facing document acknowledges, is a materially weaker separation than a documented one. [support.propertyware.com sandbox article; spec § Base URL]

Score math: earned 4.5 of 5 applicable checks (1 + 1 + 1 + 1 + 0.5); unrounded fraction = 0.9; category points = 0.9 × 5 = 4.5 → **4.5/5**; verification coverage = 5/5 = **100%**

**What this means for you:** this is the strongest part of the API and the part that matters most if you are going to point an AI agent at your data. You can mint a key that can only read, or only touch certain records, hand it to a vendor or an agent, and delete it the moment you want the access gone — all yourself, in the product, without calling anyone. The one soft spot is testing: a sandbox exists, but you have to email Sales Ops Support to get one and no developer document explains how it is kept apart from live data, so plan on proving that for yourself before you trust it.

## Category 4: Documentation and AI-Agent Readiness: 2.5/5
- **C4.1 Complete self-serve reference: partial** — the portal at app.propertyware.com/pw/apidocs is public, needs no login, and is genuinely example-rich: all 124 paths and 177 operations, 141 component schemas, per-operation parameters, request and response examples, per-endpoint permission requirements, and a full narrative guide covering authentication, versioning, pagination, sorting, response codes and date formats. The exact limitations, each found by live testing rather than by reading: (1) **required fields are under-declared** — `SaveContactDTO.required` lists only `firstName` and `lastName`, yet `POST /contacts` rejected a payload with those two fields, demanding Country, then Address, City, State and Zip, none of which the spec marks required; (2) the `GET /accounting/generalledger` description instructs you to filter with `lastModifiedDateStart`/`lastModifiedDateEnd`, but the declared and working parameter names are `lastModifiedDateTimeStart`/`lastModifiedDateTimeEnd`; (3) the general ledger's 30-day range ceiling is enforced but documented nowhere; (4) the rate-limiting section is commented out of the rendered page; (5) `SaveBuildingDTO.type` is required but has no enum and only a prose list of examples. A developer can build from this, but several core flows require trial-and-error against the live API to discover what the reference does not say. [spec; live probes 2026-09-14]
- **C4.2 Reliable machine-consumable integration path: yes** — a complete, maintained OpenAPI 3.0.0 specification covering all 177 operations and 141 schemas is published and downloadable from the documentation portal, suitable for code and tool generation; it is complete enough that a third party generates and maintains a full SDK from it. No official SDK and no MCP server were found, but one strong mechanism is sufficient for this check and no extra credit accrues for having more. [app.propertyware.com/pw/apidocs — embedded OpenAPI 3.0.0 document and "Download OpenAPI specification" control]
- **C4.3 AI-readable documentation: no** — no qualifying resource exists. `llms.txt` and `llms-full.txt` return 404 on both `app.propertyware.com` and `www.propertyware.com`; there is no per-endpoint Markdown, no downloadable plain-text or Markdown documentation corpus, and no equivalent first-party format structured for retrieval. The situation is worse than merely absent: `app.propertyware.com/robots.txt` is `User-agent: * / Disallow: /pw/`, which places the *entire* documentation portal off-limits to crawlers — an attempt to fetch the reference through a standard retrieval tool during this run was refused on exactly that basis — and the specification itself has no stable URL, existing only as JavaScript state on a client-rendered page that a human must click a button to export. The OpenAPI document is credited in C4.2 as the machine-consumable integration path and is not counted a second time here. [robots.txt on both domains; 404s on all four llms paths; blocked retrieval attempt 2026-09-14]
- **C4.4 Kept current: partial** — a real changelog exists and is unusually detailed, running from 2022-09-21 through 2026-05-10 with roughly fortnightly dated entries naming new endpoints, new fields, deprecations and bug fixes, and deprecations are additionally marked inline in the schema field descriptions. The exact limitation is that the cadence has lapsed: after years of entries about two weeks apart, there is **nothing in the four months** between 2026-05-10 and this run on 2026-09-14, so a reader cannot currently tell whether the API has been stable or whether changes have simply gone unrecorded. [spec `info.description` § Changelog, entries enumerated 2022-09-21 … 2026-05-10]

Score math: earned 2.0 of 4 applicable checks (0.5 + 1 + 0 + 0.5); unrounded fraction = 0.5; category points = 0.5 × 5 = 2.5 → **2.5/5**; verification coverage = 4/4 = **100%**

**What this means for you:** a developer can sit down with this documentation and build, and the OpenAPI file means your tooling can generate most of the client code for you. Two things will cost you time. The reference under-declares what the API actually requires, so expect a round of trial-and-error on every create endpoint — we hit it immediately on contacts. And if you plan to point an AI coding assistant at these docs, it cannot read them: the whole documentation site is blocked to crawlers and the spec has no fetchable address. You will have to download the file by hand and feed it to the tool yourself.

## Category 5: Accessibility and Cost: 11.3/15
- **C5.1 Self-serve API key: yes** — once an account is entitled to API access, an administrator creates credentials themselves with no sales call, support ticket or approval step: "On the API Keys page, click Create API Key… Once finished, click GENERATE KEY. You have successfully created an API key!" The only prerequisite is an administrator role with access to Administration Setup > API Keys. The docs offer a support request as a fallback "if you are having issues", not as a required approval gate. [spec `info.description` § Enabling the API and § Creating API Keys]
- **C5.3 Not commercially gated: partial** — two distinct gates, neither of which is a premium-tier lock. First, the API is **not included** in any package: the pricing page's footnote reads "For Enterprise/API: Add $1 per unit/per month to any package", a surcharge that lands between 50% and 100% on top of the $1.00–$2.00 per-unit subscription depending on tier. It is at least tier-neutral — the add-on is available on Basic as readily as on Premium, so the API does not require a premium plan. Second, and not a pricing matter at all, a meaningful slice of API capability sits behind an opt-in beta: **25 of the 99 write operations**, including every one of the 18 `DELETE` endpoints and `PUT /workorders/closeworkorder`, carry "Write access is only available to customers who have opted in to our beta program. Please reach out to support if you'd like to be included." That gate was confirmed live — both of the operator's keys, one of them provisioned specifically for delete access, were refused on `DELETE` with a 404. Neither gate is identity or regulatory verification, so neither is excluded from scoring. [www.propertyware.com/pricing/ footnote; spec operation descriptions; live write log 2026-09-14]

Score math: earned 1.5 of 2 applicable checks (1 + 0.5); unrounded fraction = 0.75; category points = 0.75 × 15 = 11.25 → **11.3/15**; verification coverage = 2/2 = **100%**

**What this means for you:** getting a key is genuinely easy — you make it yourself in the product in about a minute, and you can make several. The cost is the honest catch: the API is a paid add-on at a dollar per unit per month on top of whatever you already pay, which on the Basic package doubles your per-unit price. And "having API access" turns out not to include being able to delete or close anything; that needs a separate conversation with support to join a beta. Budget for the add-on, and ask about beta enrolment in the same conversation, because without it your integrations can only ever add and amend.

## Total
- Raw: **35.41 / 50** (11.25 + 5.909091 + 4.5 + 2.5 + 11.25, summed unrounded)
- Normalized before rounding: **70.82 / 100**
- Published numeric score: **71 / 100**
- Letter grade: **C-**
- Evidence tier: **Baseline verified**
- Overall verification coverage: **100%** (26 of 26 applicable checks verified; 1 check N-A; 0 unverified. Gate: no category Unable to verify — category coverages are 100%, 100%, 100%, 100%, 100%; overall ≥ 80% ✔; minimum battery complete ✔)
- Partial-result flag: **yes** — C1.3 is documentation-graded because every delete and close operation is gated behind an opt-in beta this tenant is not enrolled in. Enrolling the account in that beta and re-running steps 6–8 against a disposable fixture would resolve it and could move the run to Fully verified — controlled live. Separately, fixture cleanup could not be completed through the API; two inert test contacts must be removed in the product UI.
- Inter-run agreement: **22 of 27 checks unanimous across three independent runs (81%)**. All five disagreements were 2–1 and one step on the scale; none pitted a `yes` against a `no`. Pre-reconciliation totals were 66, 70 and 68.
- Unresolved evaluator disagreements, after resolving each against the frozen packet:
  1. **C1.2 sub-map membership — a methodology defect, not a vendor fact.** The three runs agreed on every underlying fact and still produced 81.4%, 85.2% and 87.2% weighted coverage, straddling the 0.85 threshold, purely because each built the C1.2 sub-map differently from the phrase "the predetermined mutable workflows/objects": objects only (Σw 35), objects plus all workflows (Σw 47), and objects plus the mutable workflows with "read core records" marked N-A (Σw 44). Resolved to `yes` because two of three constructions include the workflows, as the phrase directs. Reverting to the objects-only construction would give `partial` and a published score of **67 (D+)**. Methodology v1.2 should state the sub-map's membership explicitly.
  2. **C1.1 (object coverage)** — held at `yes` 2–1 (85.5% / 88.2% / 80.3%; all three agree no critical object is absent). The spread is item-level judgment on bank accounts (exposed only as GL accounts of type "Bank", with no balance, register or statement resource) and applicants (Prospects cover the lead; there is no application or screening object). One evaluator landed below the 0.85 line. Marking it `partial` would give a published score of **67 (D+)**.
  3. **C2.11 (request traceability)** — held at `no` 2–1. One evaluator read the `partial` clause, "an identifier is present but undocumented or not usable for support", as literally describing the Akamai `ak_p` token inside `server-timing`, which is unique per request. Resolved to `no` because that token is edge-CDN telemetry rather than an identifier Propertyware issues, documents or has been evidenced to honour in support. Marking it `partial` would give a published score of **72 (C-)**.
  4. **C4.2 (machine-consumable path)** — resolved to `yes` 2–1, on the evidence that a third party generates and maintains a complete SDK from the published spec, which is direct proof it does not require the "substantial manual correction" the `partial` clause describes. The dissenting `partial` would give **70 (C-)**.
  5. **C5.3 (commercial gating)** — unanimous `partial`, but independently named by every run as the single largest lever in the rubric, because Category 5 carries 15 points across only two checks. An evaluator treating a mandatory paid add-on as `no` would give **63 (D)**.
- Defensible range across all of the above: **63 to 72** — D to C-. The published 71 sits at the upper end and depends most on C1.1 and C1.2 both holding at `yes`. A reader comparing this vendor against others graded with this methodology should treat the score as "low C-range, with real dispersion" rather than as a precise 71.

## Bottom line for a property manager

Propertyware's API reaches nearly everything your business actually runs on and lets you write to it — leases, tenants, charges, payments, bills, owners, work orders — which is more than many competitors offer, and it does so with the best access-control story in this report card: you can mint a read-only or narrowly-scoped key yourself, hand it to a vendor or an AI agent, and revoke it just as fast. The documentation is solid enough to build from, the versioning contract is clear, and there is a real status page that tracks the Open API separately from the platform.

What holds the score down to a low C- is the operational plumbing underneath. There are no webhooks at all, so every integration you build is a scheduled poll that will quietly miss deletions. There is no idempotency, so a retried request can post a charge or a payment twice and nothing in the API will stop it. There is no concurrency control, so two integrations editing the same lease overwrite each other silently. And there is no request ID on any response, so when something breaks you have nothing but a timestamp to give support. Those four absences matter far more for a system that moves money through your ledger than they would for a reporting tool.

Two access facts belong in your budgeting. The API is a paid add-on at **$1 per unit per month on top of your subscription** — on the Basic package that doubles your per-unit cost. And "API access" does not include deleting or closing records: all 18 delete endpoints and the close-work-order call are gated behind an opt-in beta you have to ask support to join. We confirmed this live — your delete-access key was refused, which is also why two test contacts are sitting in the account waiting for you to remove them by hand.

On product fit: Propertyware is a property-management-specialised system of record and its API is genuinely PM-native, but it is **not a bank** and no bank provides anything here — the "bank accounts" in this API are general-ledger accounts inside the accounting system. Security-deposit segregation is configurable (there is a separate default deposit account per portfolio), but no first-party documentation describes trust accounting, escrow or three-way reconciliation as an API workflow, and there is **no reconciliation object at all**. Whatever you build on this, bank reconciliation stays a manual job inside the product, and a score of 71 says this API is genuinely workable for syncing and reporting but needs your own safety rails before you let it write money.
