@voyant-travel/relationships) owns the canonical identity layer for Voyant. Before a sales pursuit, a program, or a booking can exist, there has to be someone (or some company) it belongs to. Relationships is where those identities live: the people an operator knows, the organizations they buy and sell through, the ways to reach them, and the history of every interaction.
It is deliberately the CRM core and nothing more. It does not model deals, commitments, or money. Other modules reference its records by plain id and never import its schema, which keeps identity decoupled from the pipeline and commitment chain that sit on top of it. When the proposals module stores a personId, or a booking snapshots a personId plus organizationId, the truth behind those ids is owned here.
Key concepts
canonical CRM identity
An individual contact known to the operator, the canonical CRM identity record. Avoid “customer”, “client”, or “contact” as entity names; the buyer on a booking is captured as a
personId snapshot, not a first-class customer record. See Person.legal entity
A company or legal entity that represents a buyer, supplier, agency, or other counterparty. A Person may belong to zero or more Organizations, and both can appear on quotes, programs, and bookings. See Organization.
way to reach an identity
An email, phone, or website attached to a Person or Organization. Contact points are normalized (a
normalizedValue column) so person search can match across formatting variations. See Contact Point.role on a transaction
A role-bearer on a quote, quote version, booking, program, or booking item (traveler, booker, decision-maker, finance). Participant is the broad role-on-a-transaction concept; a Traveler is the narrower person-who-actually-travels. The Participant role itself is stamped on the owning module’s records, while the identity it points at is a Person here. See Participant.
logged interaction
targeting list
A named list of People or Organizations grouped by criteria, used for targeting or bulk action. See Segment.
KMS-encrypted PII record
A passport, ID card, or similar identity document attached to a Person. Document numbers and other sensitive fields are encrypted through a KMS provider, and reads decrypt on demand. A booking traveler can pre-fill a travel snapshot from a person document so PII is captured once and reused.
behavioral event
A captured signal about a Person (kind, source, status) that downstream targeting, scoring, or automation can act on. Signals are emitted as events through the module’s
./events entry.What it owns
Relationships owns the identity graph and the runtime that protects its PII.- People and organizations (
./schema), with notes, relationships between people, and per-entity custom fields. - Contact points and addresses, attached polymorphically to either a Person or an Organization, with normalized values for search.
- Activities (
activities,activity_links,activity_participants) and the communication log, so interactions can fan out across multiple records. - Segments (
segments,segment_members) for targeting and bulk action. - Person documents and person payment methods, with KMS-backed encryption for the sensitive fields.
- Customer signals and their event taxonomy, emitted through
@voyant-travel/relationships/events. - The PII route runtime (
./route-runtime): a configurable factory that injects a KMS resolver so admin routes can decrypt person documents on demand. Voyant wires its Vault resolver in here.
Working with it
Standard modules are not registered by hand.@voyant-travel/relationships is part of the standard product graph, so its routes, services, subscribers, and jobs are resolved into the application at build time. See Configuration.
The service methods take a Drizzle database handle as their first argument, which keeps them transaction-friendly and free of hidden global state.
Reads and writes of person-document fields are governed by the KMS resolver wired into the route runtime. Decryption happens on demand at the admin route boundary, never eagerly across a list.
Links to other modules
- proposals references People and Organizations by plain id. A Proposal belongs to one Person and/or Organization but never imports relationship schema.
- bookings snapshots
personIdplusorganizationIdas client fields on each Booking, and can pre-fill a booking traveler’s travel snapshot from a Person document. - trips validates traveler parties against the identities that own the component bookings it groups.
- The finance and legal modules target an Organization or Person when they issue invoices, policies, and policy acceptances.
React package
@voyant-travel/relationships-react provides the client tier: TanStack Query hooks (usePeople, and the organization, activity, document, and signal equivalents), query keys, the VoyantProvider, and reusable UI under @voyant-travel/relationships-react/ui (PersonCard, PersonDialog, PersonList). Admin route contributions live under @voyant-travel/relationships-react/admin. Styled components require the optional @voyant-travel/ui peer. Quote panels surface on person and organization detail pages as extension slots, but the quote lifecycle hooks themselves ship from @voyant-travel/quotes-react.
Next steps
Proposals
The sales pipeline that turns a Person into a tracked pursuit and a proposal.
Bookings
The durable commitment that snapshots the buyer and carries the travelers.
Trips
Trip envelopes that group component bookings into one customer itinerary.
Glossary
The shared vocabulary: Person, Organization, Contact Point, Participant, and more.