Partner Prospect API
The Prospect API lets you push renters to Rhino directly. You send what you know, Rhino validates it synchronously and checks eligibility in the background, and once the prospect is eligible it exposes an enrollment URL you send the renter to.
Rhino's preferred ingestion path is Partner Webhook Events, where you emit your existing events and write no client at all. Reach for the API when you want to go live without waiting on Rhino to build an adapter for your schema, when you want data problems reported back as a 422 at the moment you send them, or when you want to read prospect state on demand. Reading is useful on any integration — the GET endpoint works even if you write through events.
The write endpoint is an upsert keyed on (source, source_prospect_id) — the same key events use, so the two paths address the same records and can be mixed.
See Authentication & Environments for base URLs and request signing.
Create or update a prospect
POST /partners/{owner_slug}/prospects
Content-Type: application/json
{
"prospect": {
"source": "acme",
"source_prospect_id": "your-prospect-123",
"email": "renter@example.com",
"phone": "5559876543",
"first_name": "Jordan",
"last_name": "Rivera",
"birthdate": "1990-04-12",
"lease_start_date": "2026-09-01",
"lease_end_date": "2027-08-31",
"monthly_rent_cents": 200000,
"deposit_amount_cents": 200000,
"screening_result": "approved",
"employment_status": "employed",
"yearly_income": 95000,
"has_ssn": true,
"social_security_number": "123-45-6789",
"pms_type": "yardi",
"pms_property_id": "prop-1",
"pms_unit_id": "unit-101",
"pms_prospect_id": "p0001234",
"terms_accepted_at": "2026-07-24T15:04:05Z"
}
}
Returns 201 when the prospect is created and 200 when an existing prospect is updated. Both return the full prospect object.
Only fields you include are updated. Omitting a field leaves the stored value untouched, so you can send partial updates as data becomes available.
Fetch a prospect
GET /partners/{owner_slug}/prospects/{source}/{source_prospect_id}
Returns the same prospect object. Use this to poll for eligibility if you are not subscribed to prospect.ready webhooks, or to read offered_products and latest_coverage after the renter has been through the enrollment flow.
Request fields
Three fields are enforced at write time. A request missing any of them is rejected with 422:
| Field | Notes |
|---|---|
source | Your partner code, assigned by Rhino. Must match a configured source. |
source_prospect_id | Your identifier for the renter. Unique per property owner and source. |
terms_accepted_at | ISO 8601 timestamp of when the renter accepted Rhino's terms in your flow |
Everything else is accepted as null, but the prospect stays incomplete and cannot enroll until the fields listed in Prospect Data Requirements are present.
All monetary values are integer cents (200000 is $2,000.00). Dates are YYYY-MM-DD; timestamps are ISO 8601.
Response
{
"status_code": 201,
"prospect": {
"id": 4821,
"source": "acme",
"source_prospect_id": "your-prospect-123",
"email": "renter@example.com",
"phone": "5559876543",
"first_name": "Jordan",
"last_name": "Rivera",
"birthdate": "1990-04-12",
"lease_start_date": "2026-09-01",
"lease_end_date": "2027-08-31",
"screening_result": "approved",
"monthly_rent_cents": 200000,
"deposit_amount_cents": 200000,
"effective_coverage_amount_cents": 200000,
"employment_status": "employed",
"yearly_income": 95000,
"has_ssn": true,
"pms_type": "yardi",
"pms_property_id": "prop-1",
"pms_unit_id": "unit-101",
"pms_prospect_id": "p0001234",
"status": "ready",
"percent_savings": 93.75,
"enrollment_url": "https://www.sayrhino.com/enrollment/embedded/acme-properties/acme/your-prospect-123?email=renter%40example.com",
"offered_products": [
{
"product_type": "security_deposit_insurance",
"upfront_premium_cents": 12500,
"monthly_premium_cents": 1400,
"upfront_enrollment_url": "https://www.sayrhino.com/enrollment/embedded/...&selected_product=security_deposit_insurance&cadence=upfront",
"monthly_enrollment_url": "https://www.sayrhino.com/enrollment/embedded/...&selected_product=security_deposit_insurance&cadence=monthly"
},
{
"product_type": "cash_deposit",
"cash_deposit_amount_cents": 200000,
"enrollment_url": "https://www.sayrhino.com/enrollment/embedded/...&selected_product=cash_deposit"
}
],
"application_submitted": false,
"latest_coverage": null
}
}
Every successful response is wrapped with a status_code key mirroring the HTTP status.
Note that id here is Rhino's numeric database ID. Webhook payloads use a prefixed string ID (tpp_…) for the same record. The stable identifier across both is your own source_prospect_id.
social_security_number, education_level, citizenship, and terms_accepted_at are accepted on write but are not echoed back in the response.
Response fields
| Field | Description |
|---|---|
status | incomplete, processing, ready, or error |
effective_coverage_amount_cents | Coverage Rhino will offer, derived from the property owner's rules rather than your deposit_amount_cents |
percent_savings | Savings versus the cash deposit, as a percentage. Empty until the renter has been underwritten in the enrollment flow. |
enrollment_url | Present only when the prospect is eligible for at least one product |
offered_products | Priced products with prefilled enrollment links. Empty until the renter has been underwritten in the enrollment flow. |
application_submitted | Whether the renter has submitted an application |
latest_coverage | The renter's existing policy or cash deposit for this unit, if any |
sdi_ineligibility_reasons | Human-readable reasons SDA was declined. Omitted when there are none. |
cd_ineligibility_reasons | Human-readable reasons cash deposit was declined. Omitted when there are none. |
Product objects
offered_products contains up to two entries:
security_deposit_insurance — upfront_premium_cents, monthly_premium_cents, upfront_enrollment_url, monthly_enrollment_url
cash_deposit — cash_deposit_amount_cents, enrollment_url
Prospect lifecycle
incomplete ──(all required fields present)──> processing ──(eligibility)──> ready
▲ │
└──────────────(a required field is cleared)──────────────────────────────┘
incomplete— required fields are missing. No enrollment URL.processing— Rhino has what it needs and is checking eligibility. This is asynchronous and typically takes seconds.ready—enrollment_urlis populated. Send the renter to Rhino.error— eligibility could not be determined. Contact your Partner Success Manager.
A prospect that is ready but ineligible for a given product carries the reasons in sdi_ineligibility_reasons or cd_ineligibility_reasons. If it is ineligible for both, enrollment_url is omitted.
Because processing resolves asynchronously, subscribe to the prospect.ready webhook rather than polling in a tight loop.
ready does not mean the prospect is priced. Underwriting needs a few data points Rhino collects from the renter during enrollment, so offered_products and percent_savings stay empty until the renter has been through the flow. Do not build a UI that quotes Rhino before then.
Errors
Validation failures return 422 with per-field messages keyed by attribute name:
{
"status_code": 422,
"error": "Prospect invalid.",
"prospect": {
"source_prospect_id": "Source prospect has already been taken",
"terms_accepted_at": "Terms accepted at can't be blank"
}
}
| Status | Meaning |
|---|---|
401 | Request signature missing, malformed, or older than five minutes |
404 | Unknown owner_slug, or the prospect does not exist on GET |
422 | Validation failure — see the prospect object for field-level detail |
Test data reset
In staging only, you can clear test records so you can re-run an enrollment with the same identifiers:
DELETE /residents/reset?email={email}&owner_slug={owner_slug}
Both parameters are required, and both go in the query string rather than the body. This clears the renter's Rhino user along with the prospects and policies attached to it, so the same email can go through enrollment again. It returns 404 in production.
Note the path: unlike the rest of the API, this one is not scoped under /partners/{owner_slug}.
Next steps
- Prospect Data Requirements — which fields drive completeness
- Embedded SDA Flow — using the returned enrollment URL
- Webhooks Overview — reacting to status changes