Bridge lets a housing organization — a recovery residence network, transitional housing program, or reentry program — embed Convivial's apartment search inside its own app.
sort-bridge-api.yaml
v1.0.0-draft and is shared for partner and engineering review. Endpoints ship with the
Bridge release; sandbox access comes with your partner agreement.
When a resident is ready for a place of their own, they browse Convivial network communities where events, neighbor matching, and local perks are part of the listing — and their expressed interest is tracked all the way to a signed lease. Your team follows progress from your own system; the resident's history stays private.
Your backend creates a short-lived, signed session for a resident and renders the returned
embed_url in a WebView or iframe. Everything inside that surface is built and
hosted by Convivial. Your integration is the thin layer around it: create sessions, receive
webhooks, read reports.
embed_url good for 24 hours.lead.status_changed and friends fire back to you, keyed to your reference, carrying no PII.One placement, end to end. The only things that ever cross out of your systems are an opaque reference and housing preferences. Contact information exists only between the resident, Convivial, and the one property they chose — delivered as a standard Convivial lead with the source masked.
Server-to-server, with your API key. partner_resident_ref is an opaque ID from
your system — never a name, email, or phone number. Requests that look like they contain
personal information are rejected.
# sandbox curl https://sandbox.api.sortresidential.com/bridge/v1/embed/sessions \ -H "Authorization: Bearer sk_test_…" \ -H "Content-Type: application/json" \ -d '{ "partner_resident_ref": "res_8f3d2c9a", "preferences": { "budget_max_cents": 125000, "bedrooms": [0, 1], "areas": ["Houston, TX"], "move_in_after": "2026-11-01" }, "locale": "en" }' → 201 { "id": "ses_01j9x2", "status": "created", "embed_url": "https://embed.sortresidential.com/s/ses_01j9x2?t=…", "expires_at": "2026-09-15T16:03:22Z" }
The URL is signed and lives 24 hours. When it expires, create a new session with the same reference — saved listings and open leads follow the reference, so the resident continues where they left off.
let webView = WKWebView(frame: view.bounds) webView.load(URLRequest( url: URL(string: session.embedURL)!)) view.addSubview(webView)
webView.settings
.javaScriptEnabled = true
webView.loadUrl(
session.embedUrl)<iframe src="{{embed_url}}" title="Find your next home" style="width:100%; height:640px; border:0"></iframe>
Pass theme.accent_hex when creating the session and the embed picks up your
accent color, so the surface sits naturally inside your app.
Register a webhook endpoint once; every event that follows is keyed to your own
partner_resident_ref, so routing it to the right case file needs no lookup.
Verify signatures before trusting a delivery:
// Node — express with raw body import crypto from "node:crypto"; function verifySortWebhook(req) { const parts = Object.fromEntries( req.headers["sort-signature"].split(",").map(p => p.split("="))); const expected = crypto .createHmac("sha256", process.env.SORT_WEBHOOK_SECRET) .update(parts.t + "." + req.rawBody) .digest("hex"); const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300; return fresh && crypto.timingSafeEqual( Buffer.from(parts.v1), Buffer.from(expected)); }
The full contract, with schemas and examples, lives in sort-bridge-api.yaml.
The surface is deliberately small:
| Endpoint | What it does | Tier |
|---|---|---|
| POST/embed/sessions | Create a resident session; returns the signed embed_url. |
Core |
| GET/embed/sessions/{id} | Session status and activity counts for a case-manager view. | Core |
| GET/listings | Network availability with the community block, ranked by stated preferences. | Core |
| GET/listings/{id} | One listing in full. | Core |
| GET/leads | Lead status by session, reference, or stage. | Core |
| POST/leads | Create leads from your own native UI. Residents still consent through Convivial before any contact info is shared. | API tier |
| POST/webhook-endpoints | Register a delivery URL (list, delete, and test endpoints included). | Core |
| GET/reports/placements | Monthly funnel — sessions to signed leases — plus referral-fee lines when your agreement includes them. | Core |
| Event | Fires when |
|---|---|
| embed_session.opened | The resident opens their search for the first time. |
| lead.created | The resident submits interest in a listing, consent on record. |
| lead.status_changed | The lead moves: contacted, tour scheduled, toured, applied, approved. |
| lead.lease_signed | Placement complete. Includes the referral ledger entry when fees apply. |
| embed_session.expired | A session lapsed without activity — a nudge opportunity for the case manager. |
Deliveries are signed (HMAC-SHA256, Sort-Signature header), retried with
backoff for 24 hours, and disabled after 7 failing days. Acknowledge with any
2xx within 10 seconds.
Bridge exists for people at a sensitive moment, so these five rules are part of the API contract — enforced by schema and server behavior, not left to good intentions:
Sessions carry an opaque reference and housing preferences. Schemas are closed; unknown
fields are rejected, and references that look like emails or phone numbers return
422 pii_suspected.
There are no fields for program enrollment, treatment, or history — nothing to fill in, nothing to leak. Free text is limited to housing notes and filtered server-side.
They enter it themselves, inside Convivial's surface, behind a consent screen that names the one property that will receive it.
Leasing teams see a standard Convivial lead. Your organization's name, category, and existence are never shown to the property; attribution lives only in your reports and the referral ledger.
Ranking uses budget, bedrooms, area, move-in date, pets, and accessibility — never a protected-class attribute. This supports everyone's fair-housing obligations; many Bridge residents are protected under the Fair Housing Act's disability provisions, and the design assumes it.
Commercial terms — including whether placements carry referral fees — live in your partner agreement, and both sides should have counsel review it.
Sandbox keys (sk_test_…) work against
sandbox.api.sortresidential.com with fictional inventory, and
POST /webhook-endpoints/{id}/test sends a signed sample event. Before
switching to live keys:
partner_resident_ref is generated, not derived from names or contact details.
Sandbox access and the full sort-bridge-api.yaml spec come with your partner
agreement. Tell us about your program and we'll get you a key.