Docs
Nexus Connection Intelligence

Nexus Connection Intelligence

How the platform talks to Nexus for matching, member search, outcomes and video rooms, what crosses the boundary in each direction, and what happens when Nexus is unreachable.

Nexus is the partner service that ranks matches, searches members semantically, hosts video rooms for office hours, and learns from outcomes. This page describes the hand-off from the platform's side and the contract on the Nexus side. The Nexus side of the contract is INTEGRATION.md in the repository, and the people who built the connection keep this page current.

Who Owns What

ConcernOwner
Identity, sessions, roles, member records, profilesThe platform
Match state, the review queue, notifications, calendarsThe platform
The semantic index, ranking, written reasons, video rooms, attendanceNexus
AirtableThe platform. Nexus never talks to Airtable

Nexus is never the system of record for a person. If the two disagree about a member, the platform is right and the fix is a re-sync. Nexus only knows what the platform pushes, so a member who was never synced cannot be matched or found, and that failure is quiet unless you look for it.

The Pieces

PieceWhereWhat it does
Clientlib/nexus/client.tsOne typed wrapper around the Nexus HTTP API, with the key sent as a header on every call. Returns a stub when no URL or key is configured.
Member synclib/nexus/sync.ts, sync-payload.tsBuilds the member payload (profile, business, needs, expertise, availability) and sends it when a member changes, and in bulk from the daily engine run or npm run nexus:sync.
Matching enginelib/matching/engine.tsThe daily sweep: hard filters first, then one /match call per ready founder, then inserts suggestions into matches.
Eligibility and gateslib/matching/eligibility.ts, gates.tsWho is ready, who has capacity, how long a supporter has to answer.
Outcomeslib/nexus/outcome-report.ts, app/api/cron/nexus-outcomesReports accepted, declined, completed and check-in results back to Nexus so ranking improves.
Searchapp/api/admin/members/nexus-search, app/api/directory/nexus-searchServer-side proxies for the directory's AI search and the Book a Session search. The member version removes hidden members before returning.
Roomslib/office-hours/room.ts, attendance.tsCreates a room per session, updates it on reschedule, removes it on cancel, pulls attendance afterwards.

The Nexus Side

Nexus holds a sealed Impact Hub tenant: a private group that is invisible on every public or guest surface of Nexus, containing only the members the platform pushes. The partner API is seven routes under /api/partner/impact-hub on the Nexus origin. The code for that side lives in Nexus's own repository, not this one.

RouteMethodWhat it does
/members/syncPOSTUpserts up to 200 members in one batch, keyed on the platform's user id. Every touched member is re-embedded before the batch reports success, so a completed sync means the search index is current.
/matchPOSTRanks candidates against one person and writes one reason per pair. Stateless: Nexus ranks and explains, the platform applies its score threshold and caps.
/searchPOSTRanks the whole tenant against a free-text question and returns ids, a display block and a reason per hit.
/roomsPOSTCreates a recorded two-person meeting for a session. Replaying the same session id returns the same room and the same join links, never a second room.
/rooms/:idPATCH, DELETEMoves a room to a new time, keeping the links, or cancels it.
/rooms/:id/attendanceGETWho joined and when. The platform pulls this on its own schedule; there is no webhook, by design.
/match-outcomesPOSTStores what became of an accepted pair. One row per match, replaced whole on every send.

Authentication. Every call carries the key in an x-partner-api-key header. It is not a bearer token. Nexus compares it in constant time and fails closed: if the key is unset on either side, every call is a 401. The key value never reaches a log line on either side.

Errors. One envelope for everything: a JSON body with error.code and error.message, plus retry_after_seconds on a 429. The platform surfaces the code because some codes have a remedy. member_not_found and unknown_external_id mean the platform has a sync gap, which is a bug on this side, not on Nexus.

Identity and Sync

The shared key between the two systems is the platform's users.id, sent to Nexus as external_id. Never email: it changes, and a supporter record deliberately carries two addresses. Applicants have a different id namespace and are never synced. Someone exists on Nexus only after an admin accepts their application and they sign in.

Who is synced. Every founder and supporter who is active, plus staff who hold a supporter capability, so an admin who mentors can be found and booked. Staff without a capability are pushed only when a room needs them. Nobody who is still invited, and nobody who is offboarded. A row with no email is skipped, because email is a required field on the Nexus side.

When a sync fires. At first sign-in from the Clerk webhook, and on every change that alters what a member looks like: a profile save, a business save, an assessment submission or regrade, a tag rename, a change of access on Members. The matching engine also syncs everyone before each daily run, so a missed trigger heals itself the next morning. The sync is idempotent end to end, and npm run nexus:sync is the report-first bulk tool for an operator.

What the sync carries. Only the fields named in the payload builder in lib/nexus/sync-payload.ts, which is the one place that decides what leaves the platform. A test beside it puts a sentinel into every private field and asserts none of them survives.

The Flow of a Match

  1. The member is synced to Nexus whenever their profile, business or needs change.
  2. At 12:30 UTC the engine lists founders who are ready and under their suggestion cap, and supporters with capacity.
  3. For each founder it calls /match with the candidates that passed the platform's own hard filters (shared language, same locality, open capacity, no existing pair).
  4. Nexus answers with a relevance score from 0 to 1 and one written reason per candidate, and lists any candidate it could not rank with a code: never synced, synced but not yet embedded, not searchable, or the subject themself.
  5. Each result above NEXUS_MATCH_MIN_SCORE becomes a row in matches with the score and the reason, and enters the review queue. The reason is what the supporter and the founder read, so it is written to be shown, never as feature weights.
  6. Decisions at each gate, the check-in answers, and session completion are reported back as outcomes.

Reasons come from a language model pass on Nexus. When that pass is unavailable, Nexus falls back to a reason built from the facets that actually overlap (industry, skills, stage), and marks the source. Either way, every sentence names something checkable in the two profiles.

The Outcomes Contract

The profile sync tells Nexus who people are. Outcomes tell it which of its suggestions turned into something, which is how ranking improves over time.

  • One record per match, sent whole each time anyone in the pair answers. Nexus keeps one row per match id and replaces it on every send, so sending twice is safe, and a failed send is superseded by the next.
  • The record carries the two external ids, when the supporter accepted, when the first session happened, each person's fit rating, the outcome, and the score.
  • outcome is one of four values: never_connected, met, ongoing, ended. The platform's own states Waiting to Hear and Not Asked Yet are sent as null, because to Nexus both mean no outcome yet. Nexus rejects a fifth value with a 400, so a new outcome is added on the Nexus side first.
  • score is the relevance Nexus itself returned when the engine suggested the pair, stored unchanged. It is null for a match a person made by hand, never zero.
  • Outcomes never become member-visible on Nexus and never feed its embeddings or its reasons.
  • The send never throws. It runs after the answer is already saved. GET /api/cron/nexus-outcomes with the cron secret resends every accepted match, and ?dry=1 lists what would go without sending.

Rooms in Practice

A Nexus room is minted at booking when the mentor's sessions are set to run on Nexus video, which is the default on My Availability. The room has two seats, the mentor and the founder, each with their own join link stored against the session. Staff therefore get no link for a Nexus session; they do for Google Meet and for a mentor's own link.

  • If Nexus reports a member it does not know, the platform syncs both people, staff included, and tries once more. If the room still cannot be created, the booking stands and the session simply has no Nexus link; the failure is in the server log.
  • A reschedule moves the room and keeps the links. A cancel removes it.
  • Sessions on Nexus video are recorded and transcribed, and the booking screen says so. The consent step itself sits in the Nexus lobby.
  • Attendance is pulled by the five-minute tick, not pushed. Nexus calls a room nobody joined not_held 24 hours after its end, so the platform keeps looking for up to 26 hours and then stops. Until then History shows the platform's assumption that both attended.

The directory's AI search and the Book a Session search both go through a server-side proxy that holds the key. Nexus ranks its whole tenant, which is wider than any one member's directory: it includes people hidden from the directory, people who have not joined, and demo rows. The proxy therefore removes anything the asking member may not see before the response leaves the server, against the same rules the directory list applies. Hiding a card in the browser was the earlier design, and it had already shipped the names.

Configuration

NEXUS_API_URL, NEXUS_API_KEY, NEXUS_OUTCOMES_PATH, and the tuning values NEXUS_MATCH_MIN_SCORE, MATCH_FOUNDER_RUN_CAP, MATCH_SUPPORTER_OPEN_CAP. See Environment Variables.

The key is one value held in two places: NEXUS_API_KEY on the platform and the matching partner key on Nexus. To rotate it, generate the new value, set it on Nexus, then on Vercel, and redeploy the platform. Between the two steps every Nexus call returns a 401, and the platform degrades rather than fails (see below), so do both steps in one sitting and then click Find matches on Admin, Matching to confirm the engine runs. The Nexus side of a rotation is done by the Nexus team: contact Casey Davies-Bell.

When Nexus Is Unreachable

The platform never makes a member wait on Nexus, and nothing a member saves depends on it.

WhereWhat happens
Profile and business savesSave first, sync afterwards. A failed sync is logged and the next daily run repeats it.
The daily engineContinues with whatever was synced before. A founder whose /match call fails is recorded in the run report with the error and tried again next morning.
AI searchThe search box returns the error Nexus gave, and the ordinary filters still work.
BookingThe booking completes. The session has no Nexus link; the mentor's Meet or own link path is unaffected.
OutcomesLogged, not sent. The next answer on the same match resends the whole record, and the resend cron covers the rest.

The client does not retry on its own. A 429 carries retry_after_seconds in its message, and the daily run or the next save is the retry.

What Is Never Sent

Private notes, admin ratings of mentors, decline reasons, the supporter's match email (the shared inbox for match correspondence, which is not an identity), and members who are deactivated or hidden from the directory. The payload builder is the one place that decides what leaves the platform; tests beside it pin that.

Extending It

  • A new signal for ranking: add it to the payload builder and its test, then to the Nexus side.
  • A new outcome type: add it to the outcome report and the cron route's dry-run listing, after Nexus accepts the value.
  • Resources and partners are ranked in the app today (lib/dashboard/suggestions.ts), not by Nexus; those two functions are the ones to swap when Nexus indexes them.

Running Without Nexus

With no URL and key, the client returns a stub ranker that scores on overlap of needs and expertise, creates no rooms, and logs outcomes instead of sending them. The stub is honest rather than fake: it ranks on facet overlap it actually computed and writes a reason naming the facets that matched, so nothing it produces could be mistaken for the real thing while being unfalsifiable. Everything else behaves the same, which is how development and tests run.