Required — org-api forces at least one CONTROL person for
RouteFusion-enabled customers (Phase 1), so create() can be called
with relatedPeople from any construction site. Required (not
optional-and-asserted-at-use) specifically so the compiler enumerates
every construction site when this dependency is added, rather than a
caller finding out at runtime — after the org row + AiPrise verify()
have already committed — that nobody wired it in. Every consumer
package constructs a small OrganizationQueryService to hand to its
own RelatedPersonService instance (avoids a circular construction
against this class) — see organization-api/src/main.ts for the
canonical shape.
Protected ReadonlycacheProtected ReadonlydbStaticinstanceThe process default: built from each dependency's own instance().
Adopts people onto an org that already exists, skipping any it holds: the
retry of an onboarding whose create stopped partway through them.
Optionalemail?: stringRequired when the caller's contactPolicy is REQUIRED.
OptionalisMainOrg?: booleanMark this as the customer's PRIMARY organization
(organization.is_main_org) — the one every customer-scoped read resolves
to, and the replacement for the old customer.organization_id pointer.
Exactly one per customer, enforced by the partial unique index
organization_main_org_per_customer_idx; a second one surfaces as
DUPLICATE through the existing unique-violation catch. Set only by the
customer-onboarding flow (@cfxlabsinc/onboarding-services).
Optionalmetadata?: Record<string, string>Optionalphone?: stringAny common format; stored as E.164. Required like email.
The registered address. Required on create; older organizations may lack it, so the domain object keeps it optional.
OptionalrelatedPeople?: NewRelatedPerson[]Control persons / beneficial owners to create against this org right
after it's inserted (organization-api forces at least one CONTROL
person when the customer is RouteFusion-enabled — see
OrganizationController). Each is forwarded to relatedPersonService
the same way the standalone related-person endpoint does.
OptionalverificationProfileId?: stringAn existing AiPrise business profile to attach to this org. When supplied
the profile is FETCHED here and its current result/session are attached
to the org INSTEAD of kicking off a fresh async verify(). The
customer-onboarding flow pastes a profile the operator already validated,
often one the CRM created, so the profile is then relinked to this org
(client_reference_id and our callbacks) for later decisions to reach it.
Only the id is accepted: the verification outcome is always read from AiPrise (the source of truth), never trusted from the caller.
OptionaladoptedPeople?: AdoptedPerson[]People AiPrise already holds linked to verificationProfileId's
business profile, adopted with RelatedPersonService.adopt. Onboarding
only: a second argument, so no request-typed first argument can name it.
Whether this caller's organizations must have an email and a phone.
OptionalpreApproved?: trueDev sandbox only: skips AiPrise and treats its verdict as APPROVED, still through the activation predicate. Refused outside dev and local.
Resolve the organization's incorporation (formation) date from its AiPrise
business profile, for prefilling the RouteFusion virtual-account creation
form (RouteFusion's createBusinessEntity requires incorporation_date).
The date is not stored on the org record — it is read from AiPrise's
registration_info.formation_date (falling back to the profile's top-level
formation_date). Absence of a verification profile or a formation date is
not an error: the admin can enter the value manually, so this returns
{ incorporationDate: null } in those cases.
Organization external id
The URL to redirect to, after verification is completed
URL to start an identity document verification session
Clear each customer's membership tag and each record tag wherever cached,
and always this namespace's collection tag. keys are exact deletes in this
instance's key space: the only bust a null client honours.
ProtectedinvalidateThe bust every organization write makes, whatever it changed: its record
tag in every namespace projecting it, its customer's pages on both
surfaces, and the writer's own folded get keys.
ProtectedpageTags for a page: the scope it searched, each customer whose name its rows
copy, and the fields of records it may not hold that chose its rows.
OptionalcountriesOfIncorporation?: string[]OptionalcreatedAt?: DbTimestampCriteriaOptionaldescriptionLike?: stringPartial match on description
Optionalids?: string[]OptionalincludeDeleted?: booleanOptionallegalEntityTypeLike?: stringPartial match on legal entity type
OptionallegalEntityTypes?: (Exact match to these canonical legal-entity-type codes (US structures).
OptionalnameLike?: stringPartial match on legal entity name
OptionalorderBy?: DbOrderByCriterion<Ties break on the row id, in the last criterion's direction. name is
served by organization_*name_idx.
Optionalpage?: numberDefaults to 1
OptionalpageSize?: numberDefaults to 50
OptionalreferenceIdLike?: stringPartial match on reference ID
OptionalreferenceIds?: string[]OptionalrouteFusionEntityIds?: string[]Matches the pinned RouteFusion business-entity id on
data->'routeFusionEntity'->>'entityId'. Mirrors the admin-side filter so
Service/AdminService search signatures stay aligned.
Optionalstatuses?: (OptionalupdatedAt?: DbTimestampCriteriaOptionalverificationProfileIds?: string[]Organizations holding one of these AiPrise business profiles.
ProtectedsearchTags for a page: the scope its query searched (each customer's membership
tag, else the collection tag), so an empty page is bustable too, plus the
records it projects and the fields that chose or ordered its rows.
REQUIRED: an absent email or phone keeps the stored one, a blank one is
refused. OPTIONAL: a blank phone clears the stored one.
StaticadminThe exact key OrganizationAdminQueryService.get reads through, for its
includeDeleted: false callers.
The includeDeleted: true variant hashes differently and is reached only by
the collection tag, which every Valkey-backed environment has. That gap is
deliberate and pre-existing: it only matters in memory mode, where no admin
get runs with includeDeleted: true.
StaticconsumerThe exact key OrganizationQueryService.get reads through.
get folds onto search, so these args must stay identical to the ones it
passes ({ customerId, ids: [id], pageSize: 1 }) plus the defaults search
bakes into its own key — includeDeleted: false and page: 1. Every other
filter is undefined there, and serviceCacheKey canonicalizes with JCS,
which drops undefined properties. organizationCache.test.ts pins the
equivalence.
Unless need to directly act on an organization specifically, you should be using
EntityServiceinstead.The shared search cache is rooted at OrganizationCache, so this writer inherits it and self-busts via
invalidateWrite— one call that reaches both the customer and admin surfaces, since they share a namespace and a tag vocabulary, and every other namespace copying the organization's columns.