# API Report Card: ShowMojo API

## Run metadata
- Methodology version: 1.1
- Evaluating model: Claude Opus 5 (claude-opus-5)
- Date run: 2026-09-02
- Provisional evidence-packet version or ID: SM-EP-2026-09-02-prov1
- Final evidence-packet version or ID: SM-EP-2026-09-02-final1 (frozen before scoring; written out in full as `evidence-packet-SM-EP-2026-09-02-final1.md`)
- Evidence-discovery mode: tool-enabled discovery
- Evidence tier: **Baseline verified**
- **Independent grading runs: 3** (methodology step 12). Run 1 by the discovering evaluator; runs 2 and 3 by two cold-start graders that received the frozen packet and the methodology only, and were barred from reading run 1's output, from any web discovery, and from any API calls. Marks were compared check by check and disagreements resolved against the evidence, not averaged. See "Independent runs and reconciliation."
- Live-write method and safety: none — writes documentation-graded. The account owner declined live-data write testing for this session in writing, in advance, as final. No write, create, update, import, delete, webhook-registration, or CRM-push call was made at any point.
- Minimum live-test battery: Steps 1–5 complete. Steps 6, 7, 8 not run (operator declined live-data write testing).
- Live tests performed: authentication; core-resource read; pagination and page-size-cap behavior; sparse-fieldset filter; date-range-filtered bulk export (8 named reports, JSON + CSV); deliberate error triggering (401 / 404 / 422); rate-limit probe (60 rapid requests); response-header traceability capture; conditional-request (ETag / If-None-Match) check; credential-scope boundary check.
- Live tests not possible: Step 6 (create/update a core resource); Step 7 (duplicate-write idempotency); Step 8 (register webhook, trigger event, observe delivery).
- Documentation-graded checks (baseline verified): **C1.2, C1.3, C2.4, C2.8** — graded from first-party documentation only, per the operator's written instruction. The push half of C1.4 is also documentation-graded, since battery step 8 exercises it; its polling half was live-tested under step 3 and failed.
- Data-handling note: no credential value and no personally identifying data from the live account appear anywhere in this report. Live results are described structurally.

## Final evidence packet manifest

**API documentation (docs.showmojo.com)**
- `/` — documentation home
- `/docs/category/showmojo-api` — API overview; sandbox status statement
- `/docs/showmojo-api-public/legacy`, `/docs/legacy/showmojo-legacy-api` — Legacy (v3) introduction and auth schemes
- `/docs/legacy/listings` — Listings API overview and token auth
- `/docs/legacy/import-listings` — Import (POST), dedup rules, 409 concurrency behavior
- `/docs/legacy/list-listings` — GET /api/v3/listings, `fields` param, full response schema
- `/docs/legacy/get-listing` — GET /api/v3/listings/:uid
- `/docs/legacy/properties`, `/docs/legacy/list-properties`, `/docs/legacy/get-property` — Properties (read-only), pagination params
- `/docs/legacy/reports` — named-report table (8 reports)
- `/docs/legacy/export-report-json`, `/export-report-csv`, `/export-custom-report-json`, `/export-custom-report-csv`
- `/docs/legacy/leads-and-showings` — Leads/Showings webhook: auth, retry schedule, ordering guidance, 120+ event actions
- `/docs/showmojo-api-public/v4`, `/docs/v4/showmojo-api-v-4` — v4 introduction ("Comming soon"; active development)
- `/docs/zapier/zapier-integration-api`, `/authentication`, `/get-me`, `/webhook-subscriptions`, `/create-webhook-subscription`, `/delete-webhook-subscription`, `/webhook-payload`, `/showing-related-events`, `/listings`, `/create-zapier-listing`
- `/sitemap.xml`; `/blog` (single post, 27 May 2025); `/markdown-page` (unmodified Docusaurus template placeholder)
- `/llms.txt`, `/llms-full.txt` — both HTTP 404 (checked 2026-09-02)

**Vendor and operational sources**
- `https://hello.showmojo.com/` — product navigation
- `https://hello.showmojo.com/pricing/` — Pro and Ultra plans; no API tier or API add-on listed
- `https://hello.showmojo.com/faqs/` — "Does ShowMojo have an API?"
- `https://hello.showmojo.com/release-notes/` — monthly release notes, January–August 2026
- `https://hello.showmojo.com/terms-and-conditions/` — API usage-limit and service-change terms
- `https://status.showmojo.com` — public status page, uptime percentages and incident history
- `https://knowledgebase.showmojo.com/` + `/sitemap.xml`; `/showmojo-listings-import-api`; `/210535-automated-listing-import-frequency-and-running-an-immediate-listing-import`; `/listing-import-automation-from-third-party-source-systems`; `/take-imported-listings-off-the-market`
- `https://knowledge.showmojo.com/` — **dead**: returns HTTP 402 and renders Helpjuice "Account Canceled" (checked 2026-09-02). This host is the target of ShowMojo's own indexed support links.
- `https://mock-docs.showmojo.com/api/v3/listings` — documentation mock server

**Live production observations (2026-09-02, operator-supplied production token)**
- `GET https://showmojo.com/api/v3/listings` (and `?fields=`, `?page=`, `?per_page=`, and four undocumented incremental-filter probes)
- `GET https://showmojo.com/api/v3/listings/{uid}`
- `GET https://showmojo.com/api/v3/properties` (`?page=`, `?per_page=` including over-cap)
- `POST https://showmojo.com/api/v3/reports/{report_name}` for all 8 named reports, JSON and `.csv`, across three date ranges — read/export only, explicitly authorized by the operator during the run
- `GET https://showmojo.com/zapier/me` — credential-scope boundary probe

**Operator product-interface observation (2026-09-02)**
- ShowMojo account owner, reporting from the logged-in Settings > Import credential page: the operator can generate a new API token themselves, and **there is no read-only token option**.

## Evidence-amendment log
- **C4.4** — `https://hello.showmojo.com/release-notes/` added during the controlled verification pass. Initial discovery found only the docs-site blog (one post, May 2025). Mark moved from provisional `no` to `partial` after reconciliation (see below).
- **C5.3 / C2.5** — `https://hello.showmojo.com/terms-and-conditions/` added during the verification pass. Confirms API usage limits are reserved "in our sole discretion" with no published operational guidance (supports C2.5 `no`) and imposes no plan-tier condition on API access (supports C5.3 `yes`).
- **C5.1 / C5.3** — `https://hello.showmojo.com/faqs/` added during the verification pass ("ShowMojo has a public API…"), corroborating unrestricted availability.
- **C3.1–C3.4** — `https://knowledgebase.showmojo.com/showmojo-listings-import-api` added during the verification pass after the primary knowledge base (`knowledge.showmojo.com`) was found dead. It documents token generation at `showmojo.com/settings/import` but is silent on token lifecycle. Because the credential page is login-gated, the operator was asked to supply the gated evidence before any `no` was finalized, per methodology rule 4; the operator supplied a direct product-interface observation, which settled C3.1 and informed C3.4. C3.3 remains unverified.
- **C4.2** — Verified absence of a published specification: no spec at conventional paths, no download control rendered anywhere in the documentation UI, and no OpenAPI document in the site's main JavaScript bundle. First-party SDK/MCP search returned nothing.

*No sources were added during runs 2 and 3; per core rule 2 they graded the frozen packet only.*

## API eligibility
- Qualifying API: **yes** (unanimous across all three runs)
- API operator: ShowMojo — `docs.showmojo.com/docs/legacy/showmojo-legacy-api` publishes the interface under ShowMojo's own domain and copyright, with developer contact `dev-support@showmojo.com` (`/docs/v4/showmojo-api-v-4`, Contact).
- Access or credential issuer: ShowMojo, self-serve. `docs.showmojo.com/docs/legacy/listings` → Authentication: "The token is available in the customer's Settings > Listing import section." Corroborated by `knowledgebase.showmojo.com/showmojo-listings-import-api` ("generated at https://showmojo.com/settings/import") and by the operator's own account.
- Eligibility basis: Live-verified. An operator-issued production token authenticated against `GET https://showmojo.com/api/v3/listings` and returned that account's own listing records (HTTP 200, 2026-09-02). The interface exposes ShowMojo's own functions and is not a capability borrowed from another provider's interface.

## Context
- Software category: **Leasing tool** (leasing marketing and showing automation, with prospect pre-screening)
- What the API is for and its core objects and workflows: The ShowMojo API exists to keep rental listings synchronized from a property-management system into ShowMojo's marketing and self-showing engine, and to get the resulting lead and showing activity back out. Its core objects are listings, properties, leads/prospects, and showings; its core workflows are importing and updating listings (including taking them on and off market), receiving lead and showing events as they happen, and exporting prospect, showing, and performance data. Three surfaces exist: a Legacy (v3) REST API that carries essentially all real capability, a Zapier-only integration API, and a v4 that is announced but has no endpoints published.

## Provider and property-management fit
- What this product is: ShowMojo is rental-leasing automation software — it markets listings, captures and screens prospects, and schedules or self-serves property showings, including via its own lockboxes and smart locks (`hello.showmojo.com/solutions/`).
- Bank status, when relevant: **N-A** — no first-party evidence indicates ShowMojo offers, or partners to offer, any banking or regulated financial service.
- Who provides any bank account or regulated banking service: **none** — not applicable to this product.
- What the customer actually receives: a software subscription priced per unit or per listing (`hello.showmojo.com/pricing/`: Pro from $1.30/unit/yr or $50/listing; Ultra from $2.70/unit/yr or $110/listing), optionally with physical lockbox/smart-lock hardware. No account, balance, or ledger relationship exists.
- Property-management fit: **PM-specialized** — property management and rental leasing are the product's entire purpose (`hello.showmojo.com/solutions/`).
- Documented PM-specific workflows: listing import and two-way sync with AppFolio, Buildium, Rent Manager, Entrata, Yardi, and Tenant Cloud (`hello.showmojo.com/faqs/`); listing syndication; prospect pre-screening with conditional questions; self-show access-code distribution and lockbox access reporting (`digital_access` report, live-verified); showing scheduling, confirmation, cancellation, and no-show tracking (webhook action taxonomy); resident information reporting.
- Trust or fiduciary workflow support, when relevant: **N-A** — ShowMojo does not hold, move, or account for funds. No trust, client-fund, security-deposit, escrow, or equivalent fiduciary workflow is documented, and none would be expected of this software category. The API exposes security-deposit *terms* on a listing as marketing metadata only; this is not deposit handling and is not scored as such.
- Operational role and dependencies: ShowMojo is the top-of-funnel leasing layer — it takes listings from your PMS, markets them, books and runs showings, and hands qualified prospects onward; you still need a PMS or accounting system for leases, ledgers, and trust accounting, and an application/screening provider for the actual application decision.

## Coverage classification (fixed before inspection)

The default "Leasing/screening" classification was adapted, with the reason recorded **before** the API was inspected. Reason for deviation: ShowMojo is a leasing *marketing and showing* product, not an application/screening bureau or a lease-management system. Lease agreements and ledgers are not ShowMojo functions at all, so classifying "lease lifecycle" as critical would penalize the product for a capability with no legitimate use in its category (Scoring boundary 2). Correspondingly, listings — "optional: marketing/listings" in the generic default — are this product's central record and are classified critical. "Screening" is read as the pre-screening/showing-qualification decision ShowMojo actually performs.

**Both independent graders were invited to object to this classification and both declined**, each noting that the deviation is net *favourable* to ShowMojo rather than punitive — under the unmodified default, lease lifecycle would be a critical object scoring 0.0, which trips the "any critical object absent" override and forces C1.1 to `no` outright. Run 2 flagged one internal inconsistency for the record: applications are kept at important/weight 2 while lease lifecycle is demoted, though both are handed off to external systems. That item is load-bearing at the 50% boundary; it is disclosed under "Unresolved evaluator disagreements" rather than silently adjusted, since the classification may not be altered after inspection.

### Objects
| Object | Class | Weight | State |
|---|---|---|---|
| Listings | critical | 3 | Present with full operations (list, get, import/upsert, status change) |
| Leads / prospects | critical | 3 | Present but materially read-only (report export + webhook push only; no resource endpoint) |
| Showings | critical | 3 | Present but materially read-only (report export + webhook push only) |
| Pre-screening questions and outcomes | important | 2 | Present but read-only |
| Applications | important | 2 | Absent from the API (only an outbound `application_url` string) |
| Properties | important | 2 | Present but explicitly read-only |
| Digital access / lockbox codes | important | 2 | Present but read-only; code distribution is not exposed |
| Team members / agents | optional | 1 | Present only as denormalized fields; no resource |
| Listing groups | optional | 1 | Writable via import only; no read endpoint |
| Documents | optional | 1 | Absent (events reference documents; no object or endpoint) |
| Lease lifecycle | optional | 1 | Absent (not a ShowMojo function) |

### Mutable workflows
| Workflow | Class | Weight | State |
|---|---|---|---|
| Create / update listings, incl. on-market and off-market | critical | 3 | Present (Import API) |
| Create / update leads | critical | 3 | Absent |
| Schedule / confirm / cancel a showing | critical | 3 | Absent |
| Submit an application | important | 2 | Absent |
| Distribute a self-show access code | important | 2 | Absent |
| Assign a showing to an agent | optional | 1 | Absent |

*(Excluded from the C1.2 sub-map as not a mutable workflow: "obtain a pre-screening outcome," which is a read.)*

### Principal lifecycle changes
| Lifecycle change | Class | Weight | State |
|---|---|---|---|
| Take a listing on / off market | critical | 3 | Present (status on import) |
| Cancel a showing | critical | 3 | Absent |
| Confirm a showing | critical | 3 | Absent |
| Mark a no-show | important | 2 | Absent |
| Reschedule a showing | important | 2 | Absent |
| Blacklist or deny a prospect | important | 2 | Absent |
| Archive / hide a lead | optional | 1 | Absent |

## Independent runs and reconciliation

Three independent grading runs were performed on the same frozen packet. Runs 2 and 3 were conducted by cold-start graders that never saw run 1's marks, could not perform discovery, and could not call the API.

| | Run 1 | Run 2 | Run 3 | **Reconciled** |
|---|---|---|---|---|
| Published score | 62 | 51 | 54 | **51** |
| Letter grade | D− | F | F | **F** |

**21 of 27 checks were unanimous.** Six split, and run 1 was the outlier on five of them — in every case on the generous side. That is a systematic bias in the discovering evaluator's marks, disclosed rather than averaged away. Each split was resolved against the frozen evidence:

| Check | Run 1 | Run 2 | Run 3 | Resolved | Basis for resolution |
|---|---|---|---|---|---|
| C1.1 | partial | **no** | partial | **no** | Run 1 scored *Digital access / lockbox codes* 1.0 on the theory that reads are all its role requires. Both other graders scored it 0.5. Run 1's own fixed workflow map classifies "distribute a self-show access code" as an *important* mutable workflow, so a write **is** operationally expected on that object; internal consistency requires 0.5. That single weight-2 item moves coverage from 52.4% to **47.6%**, below the 0.50 floor. |
| C1.4 | **yes** | partial | partial | **partial** | Run 1 credited listing state-change detection at 0.5 via the date-filtered `detailed_listing_data` export. The rubric's polling standard is explicitly "updated-since plus filtering," and live testing proved `updated_since`, `updated_after`, `since`, `modified_since` and `status` are **all silently ignored** on listings. The report is a snapshot, not a change feed, and no listing webhook exists. Push coverage is **80%**, inside the partial band. |
| C2.9 | yes | yes | **partial** | **yes** *(disagreement stands)* | Run 3 argued both conjuncts come from one sentence, so crediting them separately double-counts. The integrity rule forbids double-counting a capability *across different checks*, not reading two facts from one sentence, and the documented 409 plus the documented serialization requirement do satisfy both limbs as written. Held at yes; run 3's objection is recorded below with its score effect. |
| C3.4 | **yes** | partial | partial | **partial** | The check grades "rotation and revocation." Self-serve *issuance* is directly observed, but the packet records that whether a new token invalidates the prior one is undocumented, and no revocation control is evidenced anywhere. A mechanism that may only add a key does not demonstrably accomplish rotation. Materially limited → partial. |
| C3.5 | partial | **N-A** | **N-A** | **N-A** | The check says mark N-A "when no sandbox or separate test environment exists." ShowMojo states its sandbox "is currently under development." The mock host is a documentation fixture backend, not an operator test environment: it validates no credential at all and returns identical canned data to any header. There is no test environment whose isolation could be graded. |
| C4.4 | **yes** | partial | partial | **partial** | Both graders found a direct counterexample run 1 missed: the release notes announce `listing_group_name` shipping in the Import API, and the field appears in live responses, **but it was never added to the published response schema**. That is affirmative evidence the reference is *not* maintained in step with the API — precisely what this check measures. Combined with no deprecation guidance and no changelog on the docs site itself. |

## Functional coverage map
- **Core objects:** Listings — present, full operations (critical/3, item score 1.0). Leads/prospects — read-only (critical/3, 0.5). Showings — read-only (critical/3, 0.5). Pre-screening — read-only (important/2, 0.5). Applications — absent (important/2, 0.0). Properties — read-only (important/2, 0.5). Digital access — read-only, distribution not exposed (important/2, 0.5). Team members — fields only (optional/1, 0.5). Listing groups — write-only via import, no read endpoint (optional/1, 0.5). Documents — absent (optional/1, 0.0). Lease lifecycle — absent (optional/1, 0.0).
- **Primary operational workflows:** listing import/upsert — present; lead create/update — absent; showing schedule/confirm/cancel — absent; application submission — absent; access-code distribution — absent; showing-to-agent assignment — absent.
- **Principal lifecycle changes:** listing on/off market — present; every showing-side and prospect-side lifecycle action — absent from the API and available only as an inbound notification.

## Category 1: Functional Coverage and Usefulness: 1.9/15

- **C1.1 Object coverage: no** — weighted coverage = **47.6%** (10.0 ÷ 21). Listings are fully covered: `GET /api/v3/listings` with a `fields` selector and a 40-plus-field typed schema, `GET /api/v3/listings/:uid`, and `POST` import; live-verified 2026-09-02, returning 24 listing records matching the published schema. Everything else is materially read-only or absent. Properties are explicitly "read-only access" (`/docs/legacy/properties`). Leads, prospects and showings exist only as report rows and webhook payloads — live-verified: `detailed_prospect_data` returned prospect/showing records carrying created-at, contact, showtime, showing method, no-show, current status, cancelled-by, lead source, team member, notes and custom pre-screening answer fields — but **there is no lead or showing resource endpoint**. Digital access is read-only reporting with no way to trigger code distribution. Applications, documents and lease lifecycle are absent. Below the 0.50 floor.
- **C1.2 Core operational actions: no** *(documentation-graded)* — weighted coverage = **21.4%** (3.0 ÷ 14). The only operator-callable write in the entire API is listing import: `POST /api/v3/listings` (and the legacy `POST https://showmojo.com/api/v1/listings`), up to 100 listings per request, upserting by `code`, `uid`, or `mls_id`. Two critical write workflows are absent: there is no endpoint to create or update a lead, and none to schedule, confirm, or cancel a showing — the product's namesake action. The Zapier surface adds `POST /zapier/listings`, but ShowMojo states it "is used exclusively by the Zapier integration and is not intended for direct use outside of Zapier," so it is not an operator-available action. Marked no on both triggers: below 0.50 and critical write workflows absent. Unanimous across all three runs.
- **C1.3 Delete or lifecycle actions: no** *(documentation-graded)* — weighted coverage = **18.8%** (3.0 ÷ 16). Listing on/off-market status is settable through import via `status` (`STATUS_ACTIVE` / `STATUS_INACTIVE`), documented in the import schema and in `knowledgebase.showmojo.com/take-imported-listings-off-the-market`. Every other principal lifecycle change — cancel, confirm, reschedule, no-show, blacklist/deny, hide/restore — exists in ShowMojo and is *reported* through the webhook action list, but no endpoint performs any of them. Marked no on both triggers. Unanimous.
- **C1.4 Change notification: partial** *(push half documentation-graded)* — weighted coverage = **80.0%** (12.0 ÷ 15). The Leads and Showings webhook fires "whenever a lead or showing is created, updated, or changed" and its published `event.action` reference enumerates 120-plus actions covering the showing- and prospect-side state changes: `confirm`, `confirm_automatically`, `cancel`, `uncancel`, `auto_cancel_no_reply`, `reschedule_request`, `set_no_show`, `undo_no_show`, `blacklisted_prospect`, `showing_denied_pets` / `_income` / `_move_in_date`, plus document-verification, pre-screening and lockbox-code events. Zapier adds `new_showing`, `cancel_showing`, `blacklisted_showing`, `denied_showing`. **Listing state changes are not covered and cannot be efficiently polled:** no listing webhook is evidenced anywhere, and live testing confirmed `updated_since`, `updated_after`, `since`, `modified_since` and `status` are all **silently ignored** on the listings endpoint, each returning the identical unfiltered 24 records. The critical listing-change item scores 0.0, landing coverage in the partial band.

**Score math:** earned 0.5 of 4 applicable checks; unrounded fraction = 0.125; category points = 0.125 × 15 = **1.9/15**; verification coverage = 4/4 = **100%**.

**What this means for you:** You can push listings into ShowMojo from your PMS, and you can pull activity data back out in bulk. Beyond that the API does not let you operate the product. Every showing action your staff performs all day — confirm, cancel, reschedule, mark a no-show — is something the API will tell you happened and will not let you cause. And because there is no updated-since filter on listings and no listing webhook, you cannot cheaply detect that a listing changed; you re-pull the whole collection or you run a dated report. If you were hoping to build a tool that reschedules showings or works leads programmatically, that tool cannot be built on this API today.

## Category 2: API Design, Reliability, and Operability: 6.7/10

- **C2.1 Modern API conventions: partial** — Resource-oriented REST over HTTPS returning JSON, with conventional collection and item paths, live-verified. Mixed conventions hold it below yes: report *reads* are `POST` with `application/x-www-form-urlencoded` bodies; listing create-and-update is a single overloaded `POST` upsert; no `PUT`, `PATCH` or `DELETE` verb appears anywhere in the API; and the import page advertises a stray "Second Endpoint" on `/api/v1/` inside the v3 documentation. Unanimous.
- **C2.2 Consistent typing: yes** — The published schema is precisely typed (`rent` integer, `plus_convertible` boolean, `lat` float, `available_date` date, `created_at`/`updated_at` date-time, plus closed enums for `status`, `general_type`, `housing_type`, `available_type`, `security_deposit_type`, `external_source`). Live reads matched exactly on 2026-09-02: numeric fields returned as bare numbers not strings, booleans as `true`/`false`, and every observed enum value drawn from the documented set. Report payloads carry no published schema, but their observed value typing is consistent, including nullable booleans. Noted but not scored here: report endpoints use inconsistent *key-naming* across reports (`snake_case` versus Title Case with spaces), which hurts machine consumption but is not a typing defect, and the missing report schemas are charged in C4.1 rather than double-counted here. Unanimous.
- **C2.3 Structured errors: partial** — Correct HTTP status semantics throughout, live-verified at 401, 404 and 422. JSON error paths return a consistent envelope with a usable human-readable message — `{"response":{"status":"error","errors":"Listing not found"}}` and `{"response":{"status":"error","errors":"Max date range is 1 week."}}`. Two limitations pin this at partial, and both partial triggers are met: **no populated machine-readable error code** exists anywhere — `errors` is free text an integration would have to string-match — and **error shapes vary across endpoints**, because the 401 returns `Content-Type: text/html` with a completely **empty body**. Unanimous.
- **C2.4 Duplicate prevention: yes** *(documentation-graded)* — The one consequential operator-facing write has documented natural idempotency: import matches on "one of provided IDs: `code`, `uid`, or `mls_id`" or an exact address match, and "If no match is found, a new listing is created," reinforced by the documentation home's "automatic deduplication by address, code, or MLS ID" and by a documented 409 guard against overlapping imports. The rubric expressly accepts natural idempotency. Limitations recorded: idempotency is client-dependent, holding only where the caller supplies a stable identifier, and no idempotency-key header exists as a fallback. Unanimous, though both graders noted this mark is the one most exposed by the write-test refusal, since it was never observed.
- **C2.5 Graceful handling under load: no** — No 429 is documented anywhere, no `Retry-After` guidance exists, and no numeric backoff advice is published. Live-verified: 60 rapid sequential requests all returned HTTP 200 with **no rate-limit headers of any kind**. The only related statement is contractual, not operational — ShowMojo "may set and enforce limits on your use of the API(s) … in our sole discretion" — which tells a builder a limit may exist but nothing about how it will surface or how to recover. Unanimous.
- **C2.6 Pagination for large collections: partial** — Where pagination exists it is done properly: `GET /api/v3/properties` documents `page` and `per_page` with a max of 100, and live-verified it returns a complete envelope — `{"page":1,"per_page":2,"total":0,"total_pages":1}` — giving both a total-count signal and a page count, and correctly clamps an over-cap request. But the primary collection is unpaginated: `GET /api/v3/listings` documents only a `fields` parameter, and live-verified it **ignores `page` and `per_page` entirely**, returning the full collection as a bare JSON array. Report exports state outright that "The API does not support pagination," and one returned 258 KB in a single call. No stable-ordering guarantee is published anywhere. Unanimous, with both graders noting the `no` band's "unbounded/inconsistent behavior" language fits uncomfortably well.
- **C2.7 Bulk or incremental export: yes** — A genuine bulk-export path that works. Eight named reports are documented with their endpoint names, each retrievable as JSON or CSV with `start_date`/`end_date`, plus `POST /api/v3/reports/custom`. Live-verified: all eight returned HTTP 200; the CSV variant returned `Content-Type: text/csv` with a proper header row; and **date filtering is genuinely honored** — `listing_and_showing_metrics` returned 227 rows for a 1-week window, 1,061 for 3 months, and 1,546 for 8 months. Full datasets are retrieved without per-record calls. Limitations noted: exports are unpaginated and synchronous with no async job mechanism, `detailed_prospect_data` is capped at a 1-week window, `listing_performance` ignores dates entirely, and the object endpoints have no updated-since parameter. Unanimous.
- **C2.8 Webhook security and delivery reliability: partial** *(documentation-graded)* — Retries are well documented: "Retries occur at: 5, 15, 45, 135, and 405 minutes" with "5 failed attempts = webhook is auto-disabled." Consumer guidance exists for ordering: "Out-of-order delivery is possible — use `event.id` to track sequence." What is missing is payload verification: authentication is a **static Bearer token**, not an HMAC signature over the body. A static shared secret is replayable and not tamper-evident — it proves the caller knows a secret, not that this payload is authentic and unmodified. The guidance also addresses sequence rather than explicit duplicate/replay handling, HTTPS is only "strongly preferred," and the Zapier path documents a **contradictory** policy of three attempts with polynomial backoff and no requeue. Unanimous.
- **C2.9 Concurrency and conflict control: yes** *(documentation-graded; disagreement stands — see below)* — Both prongs are documented for the write surface: explicit conflict semantics — "you'll receive an HTTP 409 Conflict response: `Another import is currently in progress`" — together with the documented concurrency behavior that imports must be serialized. Limitations recorded: this is a coarse account-level import lock, not per-record optimistic concurrency, and because import "replaces all existing listing information with the newly provided listing information," two integrators whose imports do not overlap in time will still silently clobber each other. Separately, live-verified on reads: responses carry an `ETag` and honor `If-None-Match`, returning HTTP 304 — useful for polling efficiency, though it is response caching rather than write-conflict control, and it is undocumented.
- **C2.10 Versioning and backward compatibility: partial** — An explicit version identifier exists in the path (`/api/v3/...`), the documentation is version-labelled (Legacy "Version: 1.0.0", v4 "Version: 4.0.0"), and a successor is announced. What is absent is the contract: no definition of breaking versus non-breaking changes, no deprecation window, and no sunset notice policy. The legacy introduction offers only an informal assurance, and the terms point the other way, reserving the right to "modify, amend, or cease providing the Services … at any time and without prior notice." Unanimous.
- **C2.11 Request traceability: partial** — Live-verified: **every** response carries `x-request-id` as a UUID, on success and on errors alike (a 200 listings read and a 404 both returned distinct identifiers), alongside `x-runtime`. The identifier is real and consistently present. It is partial rather than yes because it is **undocumented**: no page in the API documentation mentions `x-request-id`, and no guidance tells a builder to quote it to support. Unanimous.
- **C2.12 Service availability and status transparency: yes** — `status.showmojo.com` is a public status page carrying both required elements: per-component **uptime percentages** (observed 2026-09-02: Codebox API 99.97%, ShowMojo Application 100% over the trailing 7 days) and a genuine **incident history** with dated, timestamped, individually resolved entries including duration and affected components. Noted: the monitored components are "Codebox API" and "ShowMojo Application"; the public REST API is not broken out as its own component, so an operator cannot tell from the page whether the graded API specifically is up. Unanimous.

**Score math:** earned 8.0 of 12 applicable checks (yes ×5 = 5.0; partial ×6 = 3.0; no ×1 = 0); unrounded fraction = 0.6667; category points = 0.6667 × 10 = **6.7/10**; verification coverage = 12/12 = **100%**.

**What this means for you:** This is the strongest category and the parts that exist are mostly well built — clean typing, an honest status page, traceable responses, and an export that genuinely works with date ranges. The operational gaps are what will bite you in production: you have no idea what the rate limit is or what happens when you hit it, the main listings call returns everything in one unbounded response, a failed authentication hands your code a blank page instead of an error, and webhooks are authenticated by a replayable shared secret rather than a signature. Build your own backoff, handle empty 401 bodies, and verify webhook sources by more than the token alone.

## Category 3: Access Control and Safe Automation: 0.8/5

- **C3.1 Read-only credentials: no** — First-party product-interface observation, 2026-09-02: the ShowMojo account owner, reporting from the logged-in Settings > Import credential page, states there is **no read-only token option**. Consistent with the documentation, which describes exactly one token used across reads and the import write, and with the knowledge base, which describes only token generation and no credential types. Not N-A: the API has a write path, so a read-only credential is a meaningful capability here. Unanimous.
- **C3.2 Scoped credentials: no** — A single all-powerful credential. Live-verified: the one listing-import token was used successfully against listings, properties **and** all eight report exports. The documented alternative is **HTTP Basic auth using the operator's own ShowMojo login and password** — the account holder's full interactive credentials. No scope, permission, role or resource restriction is offered or documented for an operator-issued credential anywhere in the documentation, the knowledge base, or the terms. The only scope construct in any ShowMojo material is the OAuth2 `zapier` scope, which is issued to the Zapier application and is not something an operator can mint for their own integration; the production token returns 404 against `/zapier/me`. Unanimous.
- **C3.3 Multiple keys: unverified — could not access** — Whether an account can hold several distinct, separately revocable tokens is not stated in the API documentation, the knowledge base, or the terms, and the credential page is login-gated. The gated evidence was requested from the operator per methodology rule 4; the observation supplied covered read-only availability and token generation but did not address this. Circumstantial signals point both ways. Excluded from the category denominator and flagged rather than guessed. Unanimous.
- **C3.4 Rotation and revocation: partial** — Self-serve issuance is confirmed by direct product-interface observation: the account owner can generate a new token themselves, with no vendor involvement, support ticket or approval step. But this check grades *rotation and revocation*, and neither is established: **whether generating a new token invalidates the previous one is undocumented**, and no revocation or delete control is evidenced anywhere in first-party material. A mechanism that may only add a credential rather than retire one does not demonstrably accomplish rotation, so an operator cannot assume a compromised token dies when a new one is issued. Present but materially limited.
- **C3.5 Test and production isolation: N-A** — No sandbox or separate test environment exists to grade. ShowMojo states plainly: "A testing sandbox is currently under development — real API requests may not function as expected." The documentation's `mock-docs.showmojo.com` host is a fixture backend for the docs, not an operator test environment: live-verified, it **validates no credential whatsoever** — a deliberately invalid token, an arbitrary Basic header and the real production token all returned HTTP 200 identically, while omitting the header returned 401 — and it returns the same canned schema examples to everyone, holding no account data. Recorded as a real hazard even though the check is N-A: because it accepts a live production token and returns success, it invites operators to paste production credentials into a documentation tool.

**Score math:** earned 0.5 of 3 applicable checks (C3.4 partial = 0.5; C3.1 and C3.2 no = 0; C3.3 excluded as unverified; C3.5 excluded as N-A); unrounded fraction = 0.1667; category points = 0.1667 × 5 = **0.8/5**; verification coverage = 3 verified ÷ 4 applicable-excluding-N-A = **75%** (above the 0.70 floor, so the category is scored).

**What this means for you:** This is the weakest area and the one with real risk attached. There is exactly one kind of key, it can do everything the API can do including overwriting your entire listing portfolio, and you cannot make a limited version of it. So if you want to give a contractor, a vendor or an AI agent access to read your showing data, the only credential you can hand over is one that can also rewrite your listings. You can generate a fresh token yourself, which helps — but nothing documents that doing so kills the old one, so treat a leaked token as live until ShowMojo confirms otherwise, and treat the token itself like your account password.

## Category 4: Documentation and AI-Agent Readiness: 1.3/5

- **C4.1 Complete self-serve reference: partial** — A real, publicly accessible reference exists and is better than the category average in places: authentication is stated per resource group, endpoints carry full parameter and response schemas with per-field descriptions and examples, and every endpoint ships worked request samples in six languages plus a live request control. Live reads matched the published listing schema field-for-field on types. The limitations are material. **The report export — the primary bulk-data path — has no documented response schema at all**: none of the eight reports publishes its columns, so the field names in this report had to be discovered by calling the endpoints. The **v4 section is empty**, containing only an introduction reading "Comming soon" [sic] with no endpoints. Doc/live divergences were observed: `listing_group_name`, `showing_types`, `contact_info`, `agent_name` and `company` are returned live but absent from the published schemas, while the documented `application_url` and `scheduling_block` did not appear in list responses. Polish problems compound it — an unmodified Docusaurus template placeholder page, a lone docs-blog post containing untranslated Russian, and **the support knowledge base ShowMojo's own links point to is dead**, returning HTTP 402 and rendering an "Account Canceled" page. Unanimous, with both graders noting the `no` band's "reverse-engineering is required" language is arguably met for the report family.
- **C4.2 Reliable machine-consumable integration path: no** — None of the three qualifying mechanisms exists. **No published OpenAPI spec** — six conventional paths probed, all 404; the rendered documentation was programmatically scanned for any download/spec/openapi/swagger/.json/.yaml/postman control, returning **zero matches**; the 591 KB main JavaScript bundle contained zero occurrences of `securitySchemes` and no `"openapi":"3.x"` declaration. **No official SDKs** in any language. **No MCP server.** The six-language samples are per-endpoint copy-paste snippets, not a consumable interface definition — they require exactly the manual transcription this check excludes. Unanimous.
- **C4.3 AI-readable documentation: no** — `llms.txt` and `llms-full.txt` return 404 on `docs.showmojo.com`, `showmojo.com` and `hello.showmojo.com`. No per-endpoint Markdown, no downloadable corpus, no equivalent format. Compounded by the contract being client-rendered only: the server-delivered HTML contains `<div class="openapi-skeleton md">` placeholders where the parameter and schema tables belong, so fetching a documentation page returns prose without the API contract — precisely the retrieval pattern an AI coding tool uses. Unanimous.
- **C4.4 Kept current: partial** — Monthly release notes run January through August 2026, current as of this assessment, and carry genuinely API-specific entries: "Listing Group Assignment Now Supported in Import API," "Vacancy Announcement Emails Now Sent for API-Imported Listings," "New Lead Trigger For Zapier Integration." That is real, regular, first-party change communication. But this check asks whether the reference is *maintained as the API changes*, and there is a direct counterexample: the very field that release note announces, `listing_group_name`, **appears in live responses and was never added to the published response schema** — it remains documented only as an import input. Compounding it, the page is product-wide marketing rather than an API changelog, the documentation site carries no changelog of its own (its blog holds one post from May 2025), and no deprecation guidance exists anywhere. Currency information exists but is demonstrably incomplete.

**Score math:** earned 1.0 of 4 applicable checks; unrounded fraction = 0.25; category points = 0.25 × 5 = **1.3/5**; verification coverage = 4/4 = **100%**.

**What this means for you:** A developer can read the listings and properties documentation and build against it — it is genuinely clear, and the code samples help. But if you intend to point an AI coding assistant at this API and have it generate a working integration, expect real manual work: no spec file to feed it, no SDK to install, no AI-readable documentation, and the endpoint details do not even appear in the raw page source. The highest-value data path ships with no column documentation at all — you will discover the fields by calling it, exactly as this assessment had to — and at least one shipped field never made it into the docs, so treat the published schema as a floor rather than a contract.

## Category 5: Accessibility and Cost: 15.0/15

- **C5.1 Self-serve API key: yes** — Credential creation is self-serve with no sales call, support ticket, or approval step evidenced. The documentation states the token "is available in the customer's Settings > Listing import section" and directs users to generate one at ShowMojo Plugins Settings; the knowledge base gives the direct URL, "generated at https://showmojo.com/settings/import." Corroborated two ways: the operator confirms they can generate a new token themselves, and a token obtained this way authenticated successfully against production on 2026-09-02. Unanimous.
- **C5.3 Not commercially gated: yes** — API access is presented as a standard product capability, not a premium unlock. The pricing page itemizes every other add-on — AI Virtual Agent, Two-Way Prospect Messaging, lockbox and smart-lock hardware, per-device monthly fees — and **never lists the API** as a tier feature, add-on or line item. The public FAQ answers the question without qualification: "Yes, ShowMojo has a public API that supports programmatic listing management … Webhooks are also available." The terms condition API use on usage limits and acceptable use but impose no plan-tier requirement. All three sources that would naturally carry a gate were accessible and none does. Unanimous. Noted for transparency: no first-party source affirmatively enumerates API access as an included feature of the entry-level Pro plan; the mark rests on negative evidence across three sources that would each be expected to disclose a gate.

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

**What this means for you:** This is the part ShowMojo gets straightforwardly right, and it is worth more than it sounds. You do not have to call a salesperson, sign an addendum or upgrade your plan to start building — you log into settings, generate a token, and you are making live API calls the same afternoon. That is a real advantage over property-management vendors that treat API access as an enterprise upsell, and it is the single largest contributor to the score.

## Total
- **Raw: 25.63 / 50**
  - Category 1: 1.875 · Category 2: 6.667 · Category 3: 0.833 · Category 4: 1.250 · Category 5: 15.000
- **Normalized before rounding: 51.25 / 100**
- **Published numeric score: 51 / 100**
- **Letter grade: F**
- **Evidence tier: Baseline verified** — steps 1–5 of the minimum live-test battery completed against production; steps 6–8 not run because the operator declined live-data write testing in advance and in writing. C1.2, C1.3, C2.4 and C2.8 are documentation-graded and flagged at each check, as is the push half of C1.4.
- **Overall verification coverage: 96.2%** (25 of 26 applicable checks verified, excluding the one N-A). Per-category coverage: C1 100%, C2 100%, C3 75%, C4 100%, C5 100%. No category is below the 0.70 floor, so no category is Unable to verify, and overall coverage clears the 0.80 gate. Combined with a Baseline verified tier, the numeric score is publishable.
- **Partial-result flag: yes.** One check is unverified: **C3.3 (multiple keys)**, because the credential page is login-gated and the operator's supplied observation did not cover it. Resolution: confirming whether several distinct tokens can exist simultaneously in Settings > Import. If C3.3 resolved to yes, Category 3 would rise to 1.5/5 and the published score to 52; if no, Category 3 would fall to 0.6/5 and the score to 50. Either way the grade remains F.

### Unresolved evaluator disagreements

Reported explicitly rather than averaged, per methodology step 12.

- **C2.9 Concurrency and conflict control — run 3 marks partial; runs 1 and 2 mark yes.** Run 3's position is that the documented 409 and the "wait for the previous request" instruction are a single fact, and that crediting it against both limbs of the yes clause is double-counting; it also notes full-replace import semantics guarantee lost updates between non-overlapping importers. Resolved to **yes** because the integrity rule forbids double-counting a capability *across different checks*, not deriving two facts from one sentence, and because the check's written definition is satisfied. **Score effect if run 3 is right: 50/100, grade unchanged (F).**
- **C1.1 Object coverage — run 2 marks no at 47.6%; runs 1 and 3 mark partial at 52.4% and 50.0%.** All three graders agree on nine of the eleven item scores; the mark turns entirely on whether *Digital access / lockbox codes* scores 0.5 or 1.0, a single weight-2 item straddling the 50% band boundary. Resolved to **no** on the internal-consistency argument set out in the reconciliation table. **Score effect if partial is right: 55/100, grade unchanged (F).**
- **Classification sensitivity, raised by run 2 and disclosed rather than acted on.** The fixed classification demotes lease lifecycle to optional because ShowMojo hands leases off to external systems, but keeps applications at important/weight 2 although applications are handed off the same way. That item is worth 2.4 points of weighted coverage and C1.1 missed the partial threshold by 2.4 points, so it decides that mark. The classification may not be altered after inspection, so it stands as fixed; the sensitivity is recorded here.

Across every combination of these unresolved positions the published score moves within roughly **50–55**, and **the letter grade is F in all of them.** The three runs' independent totals were 62, 51 and 54; the reconciled figure is 51.

## Bottom line for a property manager

ShowMojo's API is a one-way street, and you should plan around that: you can push your listings in and pull your leasing activity back out — leads, showings, no-shows, pre-screening answers, lockbox access, performance metrics, all of it date-filterable in JSON or CSV — but you cannot make ShowMojo *do* anything. There is no way to confirm, cancel or reschedule a showing through the API, and no way to create or update a lead, so the automations most operators actually want are not buildable today. What you can build is good reporting, a warehouse sync, and real-time reaction to leasing events, because the webhook coverage of showing and prospect activity is genuinely thorough and the export path works exactly as documented.

Two limitations do most of the damage. First, access control: there is exactly one kind of key, it cannot be made read-only or scoped, and it can overwrite your entire listing portfolio — so handing it to a vendor or an AI agent means handing over write access to your listings whether you wanted to or not, and nothing documents that generating a fresh token retires the old one. Second, listings are hard to keep in sync efficiently: there is no updated-since filter, no listing webhook, and no pagination on the listings endpoint, so detecting that a listing changed means re-pulling everything. Against that, the single thing ShowMojo gets clearly right is access — no sales call, no upgrade, no approval — and that alone accounts for nearly two-thirds of the points it earned.

ShowMojo is not a bank, does not hold or move money, and documents no trust, escrow or security-deposit handling — the deposit fields in the API are listing marketing copy, not deposit accounting. It is not a PMS either. It sits in front of your PMS as the leasing and showing layer, and you will still need your property-management and accounting system for leases, ledgers and trust accounting, plus a separate application and screening provider for the actual rental decision. Graded strictly on how buildable it is for an operator, this is an F — a competent read-and-export API attached to a product whose central actions the API does not expose. That is a judgment about the API surface, not about whether the product does its job.
