# API Report Card: RentEngine Public API

## Run metadata
- Methodology version: 1.1
- Evaluating model: Claude Opus 5 (claude-opus-5)
- Date run: 2026-09-03 (live observations 11:24–11:33 EDT / 15:24–15:33 UTC)
- Provisional evidence-packet version or ID: `RE-CLEANROOM-2026-09-03-P1`
- Final evidence-packet version or ID: `RE-CLEANROOM-2026-09-03-FINAL` (OpenAPI bundle SHA256 `3bea83b7b654ff7d85af29d334cd98ea3cbdd5d1a4a2242a670b8200076af560`, spec version 1.4.1; re-fetched at 11:32 EDT and byte-identical, so the packet was stable for the whole run)
- Evidence-discovery mode: tool-enabled discovery
- Evidence tier: **Baseline verified**
- Live-write method and safety: none — writes documentation-graded. No operator authorization for live-data writes was recorded for this session, and the stored credential note restricts use to read-only. No POST/PUT/PATCH/DELETE was sent to any RentEngine endpoint.
- Minimum live-test battery: steps 1–5 **complete**. Step 6 (create/update) and step 7 (idempotency) not run — no sandbox reachable and no recorded write authorization. Step 8 (webhooks) not runnable via the API at all: webhook subscriptions are created only in the developer portal UI, there is no webhook-management endpoint.
- Live tests performed: authenticate (`GET /accounts`); paginate `GET /units` across pages 0→1 following `X-Has-More`/`X-Next-Page-Number`; incremental `updated_after` and `updated_after`+`updated_before` filters; four deliberate error classes (401 / 400 / 404 resource / 404 unknown path); `X-Request-Id` echo and substitution; 45-request concurrent burst producing observed `429` + `Retry-After`; live type inspection of `Unit` and `MarketingListing`; ordering-repeatability check; existence probes on `/showings/{cancel,approve,confirm,create}`; staging-host probes.
- Live tests not possible: write-path steps 6 and 7 (no authorization, and no hard-delete verb exists to reverse a created record); step 8 (no webhook API).
- Documentation-graded checks (baseline verified): **C2.4** (idempotency), **C2.8** (webhook delivery/verification), **C3.1** (read-only credential behavior), and the write halves of **C1.2** / **C1.3**.

### Safety note recorded during this run
`GET /market-tool/comps` is documented as metered at **$0.50 per successful call**. It was deliberately never called. `POST /units` accepts an `after_upsert_action` of `cancel_scheduled_bookings`, which cancels real scheduled and confirmed showings; it falls under the protocol's hard exclusion list and was never sent.

## Final evidence packet manifest
- `https://docs.rentengine.io/` — documentation home (client-rendered; data endpoints used directly)
- `https://docs.rentengine.io/_bundle/openapi/openapi.json?download` — OpenAPI 3.1.0 bundle, `info.version` 1.4.1, 44 operations, 7 webhooks, 110 schemas
- `https://app.rentengine.io/openApi/api/public/v1/openApiSpec.yaml` — same spec, YAML
- `https://app.rentengine.io/openApi/api/public/v1/CHANGELOG.md` — entries 1.1.1 → 1.4.1 (latest dated 2026-09-03)
- `https://app.rentengine.io/openApi/api/public/v1/compatibility.md` — versioning and breaking-change policy
- `https://app.rentengine.io/llms.txt` — machine-readable overview (HTTP 200, `text/plain`)
- `https://status.rentengine.io` — Instatus status page: six components, 90-day uptime, notice history
- `https://www.rentengine.io/pricing` — plan and feature listing
- `https://www.rentengine.io/` — root navigation
- `https://docs.rentengine.io/mcp` — first-party documentation MCP server, `serverInfo.version` `2026-09-03`
- Live production observations against `https://app.rentengine.io/api/public/v1` using the operator-supplied token, 2026-09-03 11:24–11:33 EDT

## Evidence-amendment log
- **C2.7 / C1.4** — `updated_after` and `updated_before` on `GET /units` and `GET /prospects` were initially read as absent because the OpenAPI parameters are `$ref` objects. Resolving the references showed both parameters are declared on both endpoints; confirmed live. Corrected before provisional marks were fixed.
- **C4.1** — an initial count found media-type response examples on only 6 of 44 operations. Inspecting the render tree and nested row schemas showed the reference renders worked samples from schema-level `example` values (102 `code-sample` nodes, 51 `response` nodes; e.g. `ProspectCallRow` 15/15 properties carry examples). Mark corrected upward from `partial` to `yes`.
- **C2.6** — verification search added `GET /calls` ("newest first") as the one list endpoint documenting an order. Mark unchanged (`partial`).
- **C4.3** — verification added `https://docs.rentengine.io/mcp` as a live, maintained AI-retrieval mechanism covering the full endpoint surface. Mark raised from `partial` (llms.txt alone) to `yes`.
- No other source was added after provisional scoring, and no source was removed.

## API eligibility
- Qualifying API: **yes**
- API operator: RentEngine [OpenAPI `servers[0].url` = `https://app.rentengine.io/api/public/v1`, "Production environment"]
- Access or credential issuer: RentEngine, self-serve [OpenAPI `info.description` § "Obtaining API Tokens": developer portal → "Create New API Key"]
- Eligibility basis: a first-party OpenAPI 3.1 specification defines 44 authenticated operations over RentEngine's own leasing objects, and live calls with an operator-issued token returned RentEngine production data (`GET /accounts` → 200, account "RL Property Management").

## Context
- Software category: **leasing / screening tool**
- What the API is for and its core objects and workflows: RentEngine is a leasing platform for property managers, and its public API exposes the leasing funnel — units and their marketing state, prospects (leads), showings and lockbox access, leasing events, rental applications with screening status, and reporting. The API is built for pushing inventory in and pulling funnel and application data out. Its central workflows are syndicating and updating units, creating and tracking prospects, coordinating showings and lockbox codes, and reading application and screening outcomes.

## Provider and property-management fit
- What this product is: an AI-assisted residential leasing and showing-coordination platform for property managers [`https://www.rentengine.io/` — "AI-Powered Leasing Platform for Property Managers"]
- Bank status, when relevant: **N-A** — no banking or deposit product is offered or claimed in any reviewed first-party material.
- Who provides any bank account or regulated banking service: **none**
- What the customer actually receives: a software subscription to a leasing platform plus API credentials. RentEngine holds no operator or resident funds in any reviewed material. The one money-adjacent API behavior is a **charge to the customer**: `GET /market-tool/comps` is metered at $0.50 per successful call [`info.description` § Rate Limiting].
- Property-management fit: **PM-specialized** [pricing-page feature list — premium marketing and syndication, full leasing CRM, prescreening and ID verification, secure self-guided showings, PMS sync; and the API's own object model of units, prospects, showings, applications]
- Documented PM-specific workflows: unit marketing and syndication (`POST /units`, `GET /marketing/listings/{accountId}`); prospect and lead intake (`POST /prospects`); self-guided showing access via lockbox code generation (`POST /lockboxes/generate_code`); showing scheduling (`POST /showings/create`, `GET /showings/availability`); prescreening templates and prescreen outcomes (`GET /prescreening_templates`, `GET /reporting/prescreening`); rental application and screening review (`GET /rental_application_groups`, `GET /rental_applications/{id}`); leasing funnel reporting (nine `/reporting/*` endpoints)
- Trust or fiduciary workflow support, when relevant: **not documented** — no trust, escrow, security-deposit, or client-fund workflow appears anywhere in the specification. `security_deposit_amount` on a unit is a marketing figure, not a held balance. This is expected for a leasing tool and is not a defect.
- Operational role and dependencies: RentEngine fills and tracks the top of the leasing funnel. A property manager still needs a PMS or accounting system (RentEngine documents "PMS sync" as a product feature) for leases, ledgers, and trust accounting, plus a separate e-sign product, which RentEngine does not offer.

## Coverage classification (fixed before inspection)
Adopted **verbatim** from the methodology v1.1 default table for "Leasing/screening", with no deviation, and recorded to `coverage_classification_FIXED.md` before the API surface was analyzed.

| Object or workflow | Class | Weight | Present / read-only / absent |
|---|---|---|---|
| Applications | critical | 3 | Present, **read-only** |
| Screening / decision | critical | 3 | Present, **read-only** |
| Lease lifecycle | critical | 3 | Present, **materially read-only** (2 of 45 event types writable) |
| Applicants | important | 2 | Present, **read-only** |
| Documents / e-sign | important | 2 | Documents read-only (download); e-sign **absent** |
| Marketing / listings | optional | 1 | Present; feed read-only, control writable via `POST /units` |
| *Workflow:* submit an application | critical | 3 | **Absent** |
| *Workflow:* obtain a screening decision | critical | 3 | **Absent** |
| *Workflow:* advance a lease stage | critical | 3 | **Partial** (unit status only) |
| *Lifecycle:* application approve / reject | critical | 3 | **Absent** |
| *Lifecycle:* lease stage advance / status change | critical | 3 | **Partial** (unit status only) |
| *Lifecycle:* cancel / withdraw a scheduled step | important | 2 | Present |
| *Lifecycle:* archive / soft-delete | optional | 1 | Present |

## Functional coverage map
- **Core objects.** Applications (critical/3) — present, read-only: `GET /rental_application_groups`, `GET /rental_application_groups/{id}`, `GET /rental_applications/{id}`, `/documents/{documentId}`, `/income`; no create, update, or status write exists. Screening/decision (critical/3) — present, read-only: `RentalApplicationScreeningStatus` exposes `credit_status`, `criminal_status`, `eviction_status`; `GET /reporting/prescreening`; no decision can be recorded. Lease lifecycle (critical/3) — present but materially read-only: `LeasingEvent.event_type` carries **45** values including "Application Approved", "Lease Signed", "Moved In", but `POST /leasing_events` accepts only **2** ("Assign to User", "Application Received"). Applicants (important/2) — read-only (`RentalApplicantSummary`, `CoApplicant`, `PersonalDetails`). Documents/e-sign (important/2) — document download only; no e-sign capability exists in the product. Marketing/listings (optional/1) — `GET /marketing/listings/{accountId}` read-only, but the operational controls (`status`, `marketing_description`, `marketing_photos`) are writable via `POST /units`.
- **Primary operational workflows.** Writable: unit upsert, prospect creation, showing creation, lockbox create/transfer/code-generation, lockbox events, notes, follow-ups, floorplans, multifamily properties. Not writable: application submission, screening decision, application approval or rejection, lease-stage advancement, messaging, e-sign.
- **Principal lifecycle changes.** Writable: unit `status` (`Incomplete`, `Available`, `On Hold`, `Leased`, `Archived`, `Withdrawn`, `Withdrawal Requested`); `deleted` flag on floorplans and multifamily properties; `after_upsert_action: cancel_scheduled_bookings` to cancel scheduled showings. Not writable: any rental-application status transition. No `DELETE` verb exists anywhere in the API.

> **Context that does not change the score.** The fixed leasing classification governs C1.1–C1.4, and by it this API is largely observational. RentEngine does have a substantial write surface *outside* the classified core — units, prospects, showings, lockboxes, notes, follow-ups, floorplans, multifamily properties. That capability is real and useful, but the methodology fixes the classification before inspection precisely so it cannot be re-weighted toward whatever an API turns out to offer.

## Category 1: Functional Coverage and Usefulness: 5.6/15
- **C1.1 Object coverage: partial** — weighted coverage = **53.6%** (7.5 ÷ 14). Every critical object is present but each is read-only, so each scores 0.5: applications [`GET /rental_application_groups`], screening [`RentalApplicationScreeningStatus`], lease lifecycle [`LeasingEvent.event_type`, 45 values]; applicants 0.5, documents/e-sign 0.5, marketing/listings 1.0. No critical object is absent, and 53.6% falls in the 0.50–0.84 partial band.
- **C1.2 Core operational actions: no** — weighted coverage = **12.5%** (2.5 ÷ 20). Two critical write workflows are entirely absent: application submission and screening decision. "Advance a lease stage" scores 0.5 on `UpsertUnitRequest.status` alone. Below 0.50 **and** critical write workflows absent — either condition alone forces `no`. [`POST /leasing_events` description: "Currently supported event types: Assign to User… Application Received"; no POST/PUT/PATCH on `/rental_applications*`]
- **C1.3 Delete or lifecycle actions: no** — weighted coverage = **50.0%** (4.5 ÷ 9). Application approve/reject is a **critical lifecycle action and is absent**, which forces `no` regardless of the percentage. Present: unit status transitions [`UpsertUnitRequest.status`], showing cancellation [`after_upsert_action.cancel_scheduled_bookings`], soft-delete [`UpsertFloorplanRequest.deleted`, `UpsertMultifamilyPropertyRequest.deleted`]. Verified live: `POST /showings/cancel`, `/showings/approve`, and `/showings/confirm` all return **404** at 11:32 EDT on 2026-09-03, while the control `GET /showings/create` returns **405** (method not allowed), proving the probe distinguishes existing from absent routes.
- **C1.4 Change notification: yes** — seven documented webhooks cover the critical and important state changes: `LeasingEvents` (45 event types, including "Application Received", "Application Approved", "Application Rejected", "Lease Signed", "Moved In", "Withdrawn"), `RentalApplicationGroups` (`group.state_changed` with a `changes.status` transition object), `RentalApplications` (`application.state_changed`), plus `UnitsEvents`, `ProspectsEvents`, `LockboxEvents`, `MarketToolLeads`. Efficient incremental polling is independently available and was verified live (`updated_after` honored on `GET /units`).

Score math: earned 1.5 of 4 applicable checks; unrounded fraction = 0.37500; category points = **5.6/15**; verification coverage = 4/4 = **100%**

**What this means for you:** You can see everything and change almost nothing that matters most in a leasing tool. Reading is excellent — applications, screening status, the whole funnel, and rich webhooks that tell you the moment an application is approved or a lease is signed. But you cannot submit an application, record a screening decision, approve or reject an applicant, or move a prospect through a lease stage from your own code. RentEngine says this is deliberate and compliance-driven for screening, which is a fair reason, but the effect on what you can build is the same: your automations can watch and report, not decide and act.

## Category 2: API Design, Reliability, and Operability: 7.9/10
- **C2.1 Modern API conventions: yes** — resource-oriented JSON REST over HTTPS with a published OpenAPI 3.1.0 document [`openapi: "3.1.0"`, 44 operations]. Live-confirmed standard status semantics (200 / 400 / 401 / 404 / 405 / 429).
- **C2.2 Consistent typing: partial** — *Exact limitation:* `MarketingListing` types money and measurement fields as **strings** while the equivalent `Unit` fields are **numbers**, so the same concept carries two types across endpoints. Live-verified 2026-09-03: `Unit.bedrooms` = `3` (number) and `Unit.bathrooms` = `2` (number), while `MarketingListing.beds` = `"2"`, `baths` = `"2.5"`, `rentAmount` = `"1949"`, `depositAmount` = `"1949"`, `squareFootage` = `"1487"`, `applicationFee` = `"100"` (all strings) — yet `latitude` and `longitude` on that same object are numbers, so the inconsistency is also internal to the resource. Held at `partial` rather than `no` because the affected resource is classified **optional** in the pre-fixed map, the typing is accurately documented in the schema, and the core `Unit` contract is now type-correct and matches live responses. See "Unresolved evaluator disagreements".
- **C2.3 Structured errors: yes** — live-verified populated, stable machine codes with correct status semantics: `401` → `{"error":"Unauthorized","code":"unauthorized"}`; `400` → `{"error":"Invalid query parameters","details":[{…"must match format \"uuid\""}],"code":"validation_error"}`; `404` → `{"error":"Rental application group not found","code":"not_found"}`; `429` → `{"error":"Too Many Requests","retryAfter":5,"code":"rate_limited"}`. Documented code table at `info.description` § Errors. *Caveat, not scored down:* an unrouted path under `/api/public/v1` returns the framework's HTML 404 page rather than JSON, matching the documented "unmapped statuses and non-JSON errors may omit `code`".
- **C2.4 Duplicate prevention: yes** *(documentation-graded)* — optional `Idempotency-Key` on all Bearer POST endpoints, scoped to user + method + path, with 24-hour replay of the first 2xx; same key with a different body → `422`; concurrent retry in flight → `409`; store unavailable → `503` rather than risk a duplicate. Declared as a header parameter on the write operations and documented at `info.description` § Idempotency and in `llms.txt`. **Not observed live** — verifying it requires a POST, which was not authorized.
- **C2.5 Graceful handling under load: yes** — live-verified. A 45-request concurrent burst against `GET /accounts` produced 31 × 200 and 14 × 429; the 429 carried `Retry-After: 5`, `X-RateLimit-Remaining: 0`, `X-RateLimit-Reset`, and a structured body with `retryAfter: 5`. Published numeric limits: 30 req/5 s standard, 10/5 s strict, 40/24 h market tool.
- **C2.6 Pagination for large collections: partial** — *Exact limitation:* **no stable ordering guarantee is documented** for the paginated list endpoints. `limit`/`page_number` offset pagination and an end-of-data signal are both present and were verified live (`X-Has-More: true`, `X-Next-Page-Number: 1`; pages 0 and 1 returned disjoint ids `[4229, 5473]` and `[5474, 5475]`), and the newer envelope format exposes `page.has_more` and `page.next_page_number`. Order was repeatable across two immediate identical calls, but repeatability observed is not a guarantee published, and offset paging without a documented order can skip or duplicate rows while records are being written. Only `GET /rental_application_groups` offers `sort`/`direction`, and only `GET /calls` documents an order ("newest first").
- **C2.7 Bulk or incremental export: partial** — *Exact limitation:* incremental sync works on standard list endpoints but **there is no dedicated bulk or export path**, and `updated_after` reaches only three of roughly twenty list endpoints. Live-verified on `GET /units`: `updated_after=2099-01-01` → 0 rows with `X-Has-More: false`; `updated_after=2026-08-01` → all `updated_at` on or after that date; `updated_after` + `updated_before` bounding January 2026 → all rows inside the window. Also available on `GET /prospects` and `GET /rental_application_groups`. Everything else must be swept by full pagination.
- **C2.8 Webhook security and delivery reliability: partial** — *Exact limitation:* there is **no payload signature**. The only verification is an **optional, static** shared secret sent as `X-API-Key` and configured per subscription [`components.securitySchemes.ApiKeyAuth`]; the strings "signature" and "HMAC" appear **zero** times in the specification. A consumer therefore cannot verify payload integrity, only that a caller knew a secret. Retries are documented but thin — "QStash… retry delivery with exponential backoff" with no retry count, ceiling, or dead-letter behavior. Consumer replay guidance is present ("Implement idempotency in your webhook handlers"). Two of the three required elements are materially limited.
- **C2.9 Concurrency and conflict control: partial** — *Exact limitation:* **no optimistic concurrency exists** — no `ETag`, `If-Match`, `If-None-Match`, or version field anywhere in the specification, and no `ETag` on live responses. Concurrent upserts to the same unit are last-write-wins with no lost-update protection. Credited at partial only because conflict behavior tied to concurrency *is* documented and declared: `409` is declared on all 11 Bearer POST operations for a concurrent same-key retry, and concurrency limits are published as rate limits. One prong of the check is met and the other is absent. See "Unresolved evaluator disagreements".
- **C2.10 Versioning and backward compatibility: yes** — explicit `/api/public/v1` path version; `compatibility.md` defines what will not break on v1 (paths, method semantics, documented field names and types, auth, error shape, array-versus-envelope response type), what may be added without a new version, and that breaking changes ship under a new prefix; a breaking change to or removal of a production endpoint is announced **at least 30 days in advance**.
- **C2.11 Request traceability: yes** — live-verified. `X-Request-Id` returned on every response including errors; a client-supplied `apigrader-cleanroom-20260903-abc123` was echoed exactly, and an over-length value was correctly ignored and replaced with the server's own id. Documented as "the id in RentEngine logs" and quotable to support, with a support address (`eng@rentengine.io`).
- **C2.12 Service availability and status transparency: yes** — `https://status.rentengine.io` returns HTTP 200 and publishes six components (Application Availability, APIs, Databases, CDN, Scheduling Queues, Email Service), 90-day uptime figures, a notice history, and email / Slack / RSS / webhook subscription.

Score math: earned 9.5 of 12 applicable checks; unrounded fraction = 0.79167; category points = **7.9/10**; verification coverage = 12/12 = **100%**

**What this means for you:** This is the strongest part of the API and it is genuinely well built. Errors are machine-readable, rate limiting degrades cleanly with a `Retry-After` you can obey, every response is traceable to a log id you can quote to support, and the versioning promise is written down with a 30-day breaking-change notice. Three gaps will cost you engineering time: no documented ordering on paged lists, so a long sync while records change can miss or repeat rows; no bulk export, so a full extract means paging everything; and webhooks that are not cryptographically signed, so you cannot prove a payload is genuine or unaltered, only that the caller knew a static secret. Do not expose a RentEngine webhook receiver publicly without your own extra checks.

## Category 3: Access Control and Safe Automation: 4.0/5
- **C3.1 Read-only credentials: yes** *(documentation-graded)* — the developer portal offers a **Read-only** checkbox at key creation; such keys carry a `read_only` JWT claim, may call GET and HEAD, and return `403` with `code: "read_only_token"` on POST / PUT / PATCH / DELETE [`info.description` § Token Permissions; `CHANGELOG.md` 1.4.1, 2026-09-03]. Not observed live — the supplied credential is a full-access key and creating a second key is an operator action. *Limitation disclosed in the vendor's own text:* "Direct Supabase REST is not gated", so the read-only claim constrains the public API surface but not that separate path.
- **C3.2 Scoped credentials: partial** — *Exact limitation:* scoping is **role-level only, with no per-resource restriction**. A token inherits the creating user's account permissions, and the only additional axis is the binary read-only flag; there is no way to limit a key to one account, one property, or one endpoint. Account isolation is enforced by membership ("The token may only query accounts its user belongs to; requesting any other account yields 403"), which limits blast radius but is a property of the user, not a scope you choose per key.
- **C3.3 Multiple keys: yes** — the portal presents an API Keys section listing tokens in a table, and the documentation directs operators to "Use separate tokens for different integrations or environments" [`info.description` § Token Security Best Practices, § Invalidating Tokens].
- **C3.4 Rotation and revocation: yes** — self-serve revocation from the portal ("Click the 'Revoke Token' icon that looks like a trash can"); rotation is create-new-then-revoke-old, entirely self-serve. Revocation is irreversible by design.
- **C3.5 Test and production isolation: partial** — *Exact limitation:* a staging environment is declared (`servers[1]` = `https://staging-app.rentengine.io/api/public/v1`, "Staging environment") and the host is live, but **credential isolation is nowhere documented** — no first-party material explains how to obtain a staging account or token, or whether production tokens are rejected there. Live probing also showed staging is not at parity: `GET /accounts` on staging returned an HTML 404 (that endpoint shipped 2026-09-02 per the changelog) while `GET /showings/create` returned 405, so staging serves only part of the current surface. This matches the "separate environments exist but credential isolation is unclear" definition exactly.

Score math: earned 4.0 of 5 applicable checks; unrounded fraction = 0.80000; category points = **4.0/5**; verification coverage = 5/5 = **100%**

**What this means for you:** The basics are covered — you can mint several keys, hand an integration a read-only one, and kill any of them yourself in seconds. Two things to plan around. First, a key is only ever as narrow as the user who made it, so create API keys from a purpose-built limited user rather than from your own admin login. Second, staging exists on paper but is not documented well enough to trust as a rehearsal space, and it is running behind production, so treat production as your only real environment and test carefully.

## Category 4: Documentation and AI-Agent Readiness: 5.0/5
- **C4.1 Complete self-serve reference: yes** — public, no login required. Authentication is documented end to end (obtaining, using, securing, permissions, revoking); all 44 operations carry parameter definitions and descriptions; request bodies carry worked examples on 11 of 12 operations; response samples render from schema-level examples across the reference (102 `code-sample` and 51 `response` render nodes; `ProspectCallRow` 15/15, `NoteRow` 12/12, `ReportingUnitRow` 20/27 properties carry example values). Cross-cutting behavior — pagination in both response formats, rate limits, errors, idempotency, tracing, compatibility, webhooks, status — is documented in prose with tables and payload samples. Building against it required no reverse-engineering during this run.
- **C4.2 Reliable machine-consumable integration path: yes** — a complete, maintained OpenAPI 3.1.0 document, downloadable as both JSON and YAML, covering all 44 operations, 7 webhooks, and 110 schemas, suitable for code and client generation. *Noted, not additionally credited:* a first-party documentation MCP server also exists, and there are **no official SDKs**. *Minor defect:* the document's global `security` is `[{ApiKeyAuth: []}]` (the webhook key) rather than `BearerAuth`; harmless in practice because all 44 operations override it with `BearerAuth`, but a naive generator reading only the global default would emit the wrong auth.
- **C4.3 AI-readable documentation: yes** — `https://app.rentengine.io/llms.txt` (HTTP 200, `text/plain`) comprehensively covers auth, pagination, incremental sync, errors with the full code list, idempotency, tracing, and compatibility, and points to the machine-readable spec. Endpoint-level coverage is served by the first-party documentation MCP server at `https://docs.rentengine.io/mcp`, which exposes `listApis`, `getEndpoints`, `getEndpointInfo`, `getSecuritySchemes`, `getFullApiDescription`, and full-text `search`; it responded live and reports `serverInfo.version` `2026-09-03`. Between them the API is comprehensively represented for AI retrieval. *Noted:* `llms.txt` alone would be `partial`, since it does not enumerate endpoints; the MCP server is what makes this a `yes`.
- **C4.4 Kept current: yes** — `CHANGELOG.md` runs 1.1.1 → **1.4.1 dated 2026-09-03**, with dated entries for every change in the preceding week and precise descriptions of behavior; the served spec reports `info.version` 1.4.1, matching; the documentation MCP server reports a same-day version; and `compatibility.md` supplies deprecation and breaking-change guidance. Currency is graded here; the versioning contract itself is graded in C2.10 and is not double-counted.

Score math: earned 4.0 of 4 applicable checks; unrounded fraction = 1.00000; category points = **5.0/5**; verification coverage = 4/4 = **100%**

**What this means for you:** Documentation is this API's standout strength, and it is the reason a project here is predictable to scope. Everything a developer or an AI coding tool needs is public, current, and machine-readable — a real OpenAPI file you can generate a client from, an `llms.txt`, and a live documentation server an AI agent can query directly. The changelog was updated the morning this was run. If you hand this API to a contractor or to an AI coding assistant, they will not be guessing.

## Category 5: Accessibility and Cost: 15.0/15
- **C5.1 Self-serve API key: yes** — documented self-serve creation with no sales call, ticket, or approval step: log in to the developer portal, click "Create New API Key", name it, optionally tick Read-only, click Create, copy the token [`info.description` § Obtaining API Tokens]. Corroborated operationally: the operator holds a self-issued token that authenticated successfully during this run.
- **C5.3 Not commercially gated: yes** — "Open API" is listed as an included feature of the single RentEngine platform offering on the pricing page, with no tier structure and no premium or add-on gate on API access. The reviewed materials describe one platform plan plus a one-time onboarding fee, not a tiered ladder with the API at the top.

Score math: earned 2.0 of 2 applicable checks; unrounded fraction = 1.00000; category points = **15.0/15**; verification coverage = 2/2 = **100%**

**What this means for you:** No barrier at the door. If you are a RentEngine customer, the API is part of what you already pay for, and you can issue yourself a key in under a minute without asking anyone. This is the cleanest possible result on access, and it is worth noting that this category carries the same 15-point weight as functional coverage.

## Total
- Raw: **37.54 / 50**
- Normalized before rounding: **75.08 / 100**
- Published numeric score: **75 / 100**
- Letter grade: **C**
- Evidence tier: **Baseline verified**
- Overall verification coverage: **100%** — 27 of 27 applicable checks verified, 0 unverified, 0 N-A (gate: no category Unable to verify ✓; overall ≥ 80% ✓; battery steps 1–5 complete ✓)
- Partial-result flag: **yes.** Four checks are documentation-graded rather than observed (C2.4, C2.8, C3.1, and the write halves of C1.2 / C1.3). Resolving them requires either a sandbox with write access or recorded operator authorization for controlled live-data writes. Note that observing them could only *lower* the score, since each is already credited at its documented value.
- Unresolved evaluator disagreements:
  - **C2.2 (partial versus no)** — the methodology triggers `no` when "types vary across endpoints", which is literally true here (`Unit.bedrooms` number versus `MarketingListing.beds` string). It was held at `partial` because the affected resource is classified optional in the pre-fixed map and the typing is accurately documented. A second evaluator marking this `no` would move Category 2 from 7.9 to 7.5 and the published score from **75 to 74** (grade C either way).
  - **C2.9 (partial versus no)** — the documented `409` covers concurrent retries of the same idempotency key, not conflicting writes to the same resource, so it does not prevent the lost updates the check is aimed at. A second evaluator marking this `no` would move Category 2 from 7.9 to 7.5 and the published score from **75 to 74** (grade C either way). Both moving to `no` would give **73**, still grade C.
  - No other check was close to a boundary.

## Bottom line for a property manager
Today you can build reliable read-and-report automation on RentEngine and very little else: pull your units, prospects, showings, applications and screening outcomes, receive webhooks the moment an application is approved or a lease is signed, and push units, prospects, showings, lockbox codes and notes back in. What you cannot do from your own code is the part that decides anything — submit an application, obtain or record a screening decision, or approve, reject, or advance an applicant. RentEngine says the screening path is deliberately closed for compliance, which is a legitimate reason, but the practical result is that your automations can watch and report while a human still clicks the buttons that matter. The API's biggest strength is craftsmanship everywhere except coverage — genuinely excellent documentation, a current OpenAPI spec, machine-readable errors, honest rate limiting, request ids you can quote to support, a written 30-day breaking-change promise, and no cost or sales barrier to getting a key. Its biggest limitation is that it is observational at its core, compounded by unsigned webhooks and no bulk export.
