Skip to content

Onboarding Self-Check Answers

Reference answers for the self-check questions in the Onboarding Guide. Use these to verify your understanding after completing each phase.

What is the business value of integrations for advisors?

Section titled “What is the business value of integrations for advisors?”

Financial advisors’ clients (Households) typically hold assets across multiple financial institutions — bank deposits, brokerage accounts, insurance policies. Without integrations, advisors would manually enter all client data into RightCapital, a time-consuming and error-prone process.

Integration provides three core values:

  1. Rapid onboarding — Advisors link vendor accounts and import client data in minutes instead of hours
  2. Automated sync — Nightly Sync updates holdings daily without manual intervention
  3. Data accuracy — Data flows directly from financial institutions, eliminating manual entry errors

RightCapital currently integrates with 46+ vendors (27 API-based, 19 file-based), covering custodians, portfolio management platforms, CRMs, data aggregators, and insurance providers.

What are the main types of integrations (Data, SSO, CRM)?

Section titled “What are the main types of integrations (Data, SSO, CRM)?”
TypePurposeExamples
Data IntegrationSync financial data: accounts, positions/holdings, insurance policiesSchwab API, Fidelity (file), Orion, Addepar
SSO (Single Sign-On)Allow advisors to jump from vendor portals into RightCapital without re-authenticatingSchwab SSO, Fidelity SSO via SAML 2.0
CRM IntegrationImport household/contact information from CRM systemsWealthbox, Redtail, Smart Office

Additionally, the team manages Morningstar Security Master — the securities pricing and fundamentals data pipeline — which does not fit neatly into the above categories but falls under Integration Team’s responsibilities.

Data Integration is the core workload, accounting for the majority of development and operational effort.

What is the difference between File-based and API-based integrations?

Section titled “What is the difference between File-based and API-based integrations?”
DimensionFile-basedAPI-based
Data sourceVendor pushes files (CSV/Excel) via SFTPRightCapital calls vendor REST/SOAP APIs
Data flowVendor SFTP → Collector → S3 → Retail API → DBRetail API → Vendor API → DB
AuthenticationNone (files transferred via SFTP)OAuth 2.0 / JWT / API Key / SOAP / mTLS
Advisor setupPassive: Support configures Rep Code in Admin CenterActive: Advisor self-service OAuth authorization
Data freshnessDepends on vendor push schedule (usually daily)Near real-time via on-demand or webhook
ComplexityMedium (parsing, field mapping)High (state machine, error recovery, token refresh)
Count~19 vendors~27 vendors
ExamplesFidelity, Pershing, Schwab (file), SEIOrion, Schwab API, Addepar, Wealthbox

Some vendors support both modes (e.g., Schwab, LPL, Betterment).

Collector is a standalone service (repo: integrations/collector) that downloads data files from vendor SFTP servers, processes them, and uploads them to S3 for Retail API consumption.

It is the data ingestion entry point for all file-based integrations. Without Collector, Retail API would need to connect to each vendor’s SFTP individually.

Data flow: Vendor SFTP → Collector → S3 → Retail API

How does Collector communicate with Vendors (SFTP, etc.)?

Section titled “How does Collector communicate with Vendors (SFTP, etc.)?”

Collector connects to vendor SFTP servers using per-vendor configuration:

  • Connection: SFTP protocol with vendor-specific host, port, and credentials (password or SSH key)
  • File selection: Pattern matching rules to identify target files
  • Download strategy: Date/timestamp filtering to download only new files

Note: Some vendors push files to RightCapital’s self-hosted SFTP (e.g., Pershing), rather than hosting their own. Collector handles both directions.

What are the 4 processing stages? What does each stage do?

Section titled “What are the 4 processing stages? What does each stage do?”
StageResponsibilityInputOutput
1. DownloaderConnect to vendor SFTP and download files to local temp directoryFiles on vendor SFTPRaw files in local temp directory
2. ProcessorDecompress (ZIP/GZ), validate format, organize by typeCompressed files in tempClean, validated files
3. UploaderUpload processed files to S3 in organized directory structure, update timestamp.txt, clean up tempProcessed filesFiles on S3 at vendor/{rep_code}/{date}/
4. FinalizerCreate/update LATEST symlinks pointing to the most recent data filesDated files on S3LATEST_accounts.csv etc.

Pipeline: Vendor SFTP → [Downloader] → temp/ → [Processor] → clean files → [Uploader] → S3 → [Finalizer] → LATEST links

What is the final output (LATEST files on S3)?

Section titled “What is the final output (LATEST files on S3)?”

LATEST-prefixed files on S3 that always point to the most recent data for each vendor/rep-code combination:

s3://bucket/vendor-name/
├── rep_code_1/
│ ├── 2024-01-15/
│ │ ├── accounts.csv
│ │ ├── positions.csv
│ │ └── securities.csv
│ ├── 2024-01-16/
│ │ └── ...
│ ├── LATEST_accounts.csv ← Retail API reads this
│ ├── LATEST_positions.csv
│ └── LATEST_securities.csv
└── timestamp.txt

Retail API always reads LATEST files without needing to know which date directory contains the newest data. This decouples the file schedule from the sync schedule.

How do we establish connection with Vendors (OAuth, API key, etc.)?

Section titled “How do we establish connection with Vendors (OAuth, API key, etc.)?”

Supported authentication methods:

Auth MethodDescriptionVendors
OAuth 2.0Most common. Advisor authorizes in browser → access_token + refresh_token stored in integrations.credentialsSchwab API, Orion, Addepar, LPL
OAuth 2.0 + PKCEEnhanced security OAuthSchwab API
JWT (RS512)RSA private key signs JWT tokensYodlee (separate system)
API KeyStatic key assigned by vendorWealthbox, Redtail
mTLSMutual TLS certificate authenticationCommonwealth
WS-Security/SOAPXML-based SOAP authTamarac
HMACHash-based message signingWealth Access
RSARSA encrypted authenticationDST
Certificate-basedClient certificateAlbridge

Typical OAuth 2.0 flow (implemented in OauthConnector):

  1. Advisor clicks “Connect” → RC redirects to vendor’s authorize URL
  2. Advisor authenticates and grants permission on vendor’s page
  3. Vendor redirects back to RC with authorization code
  4. RC exchanges code for access_token + refresh_token
  5. Tokens encrypted and stored in integrations.credentials

Token refresh is handled by ThreadSafeRefreshAccessToken plugin for concurrent-safe token renewal.

What is the Connector/Integrator/Request architecture?

Section titled “What is the Connector/Integrator/Request architecture?”

Three-layer architecture built on the Saloon PHP library:

ComponentResponsibilityAnalogy
ConnectorTransport layer — manages vendor API connection: base URL, auth headers, rate limiting, loggingHTTP Client
IntegratorOrchestration layer — implements sync logic: calls Connector, transforms data, persists to databaseService / Use Case
RequestEndpoint definition — each specific API call: HTTP method, path, query params, error handlingAPI Endpoint Definition

Per-vendor code structure:

app/Integrations/[Vendor]/
├── Connector.php ← Transport + authentication
├── Integrator.php ← Sync orchestration
├── Requests/ ← Individual API calls
│ ├── GetAccountsRequest.php
│ └── GetPositionsRequest.php
├── Models/ ← Vendor-specific data models
└── Importer.php ← Household import logic

Call chain: Controller → Integrator → Connector → Request → Vendor API

How does API integration fetch Vendor data?

Section titled “How does API integration fetch Vendor data?”

Using Nightly Sync as example:

  1. Scheduler triggers sync command (after US market close)
  2. Command queries all active integration mappings, dispatches a queue job per mapping
  3. Worker processes each job:
    • Loads OAuth credentials from integrations table
    • Gets vendor’s Sync handler via Integration::getSynchronizer()
    • Sync handler instantiates Connector (with access_token)
    • Sends Requests to vendor API endpoints
    • Receives account/position/insurance data
  4. Integrator transforms vendor response models into RC internal models
  5. Persist creates/updates accounts, positions in database

For manual sync: Advisor clicks “Link Account” → same Integrator logic runs on demand.

What data do we typically fetch (households, accounts, positions)?

Section titled “What data do we typically fetch (households, accounts, positions)?”
Data TypeDescriptionmappable_type
HouseholdsClient family unitHOUSEHOLD
AccountsFinancial accounts (investment, bank, loan)ACCOUNT
Positions/HoldingsSecurities held (ticker, quantity, market value)Stored in positions table, linked to Account
InsurancePolicies (life, disability, LTC)INSURANCE
PersonsFamily membersPERSON
ContactsCRM contactsCONTACT
Notes/TasksCRM notes and tasksNOTE / TASK

Account and Insurance are core data integration types — IntegrationMapping::isDataIntegrationMapping() returns true only for these two.

How is Vendor data linked to RightCapital households?

Section titled “How is Vendor data linked to RightCapital households?”

Through the integration_mappings table, which forms a hierarchical tree:

Integration (Advisor ↔ Vendor connection)
└── IntegrationMapping (parent_id=NULL, mappable_type=HOUSEHOLD) ← root
├── IntegrationMapping (mappable_type=ACCOUNT) ← child
├── IntegrationMapping (mappable_type=ACCOUNT)
└── IntegrationMapping (mappable_type=INSURANCE)

Link Account flow:

  1. Advisor opens Client Portal → Profile → Net Worth → Link Account
  2. Selects a connected vendor
  3. System calls vendor API via Integrator, displays available households/accounts
  4. Advisor selects accounts to import
  5. System creates integration_mappings records:
    • Root mapping: mappable_type=HOUSEHOLD, parent_id=NULL, linked to RC Household
    • Child mappings: mappable_type=ACCOUNT/INSURANCE, parent_id → root mapping
    • Each record’s reference field stores the vendor’s entity ID

Key rule: Only root mappings (parent_id=NULL) can trigger synchronization. Child mappings inherit sync from their parent.

Manages integration lifecycle operations:

  • Create new integration (CREATE_INTEGRATION scenario) — OAuth flow initiation
  • Configure vendor connection settings
  • Disconnect integration (soft delete)

API endpoints:

  • POST /advisors/{advisor}/integrations — Create integration
  • DELETE /advisors/{advisor}/integrations/{integration} — Delete integration

Handles bulk import of household and client data, primarily for CRM integrations:

  • IMPORT_HOUSEHOLD — Batch import vendor households into RC
  • IMPORT_CLIENTS — Import client/contact data
  • LIST_TAGS — Retrieve CRM tags/categories for filtering

This is distinct from Data Integration (account/position sync). Import focuses on CRM data ingestion from vendors like Wealthbox, Redtail, and Smart Office.

Where does the Vendor data come from (S3 via Collector)?

Section titled “Where does the Vendor data come from (S3 via Collector)?”

File-based data comes from LATEST files on S3, uploaded by Collector from vendor SFTP servers.

Full chain:

Vendor → SFTP push → Collector downloads → Process (decompress/validate) → Upload to S3 → LATEST links
Retail API reads LATEST files → Parse → Save to database

Retail API uses the integrations-file-based package (packages/libs/integrations-file-based) to parse these files.

What data is typically in the files (accounts, positions, securities)?

Section titled “What data is typically in the files (accounts, positions, securities)?”
File TypeContentTypical FormatPurpose
AccountsAccount metadata (number, type, name, status)CSVCreate/update account records
PositionsHoldings (security, quantity, market value)CSVUpdate portfolio positions
SecuritiesSecurity master data (CUSIP, ticker, name)CSVSecurity matching
Tax LotsCost basis, purchase dateCSVTax lot tracking
TransactionsTrade historyCSVTransaction records

Format variations across vendors include different delimiters (comma, tab, pipe), column names, encodings (UTF-8 vs Latin-1), and date formats. Each vendor has a custom Parser to handle these differences.

How is file data linked to RightCapital households?

Section titled “How is file data linked to RightCapital households?”

Through Rep Code — the advisor’s unique identifier in the vendor’s system.

Flow:

  1. Support creates integrations record in Admin Center with advisor’s Rep Code in the reference field
  2. During sync, Retail API reads LATEST files from the S3 directory matching that Rep Code
  3. Parser extracts available accounts from the file
  4. Advisor uses Link Account in Client Portal to select accounts for import
  5. System creates integration_mappings child records linking to the appropriate Household

Key difference from API-based: File-based mappings require Support to manually configure the Rep Code, while API-based mappings are advisor self-service via OAuth.

Nightly Sync is a scheduled batch job that runs daily after US market close, fetching the latest data from all connected vendors and updating RightCapital’s database.

Data TypeSourceOperations
AccountsVendorCreate / Update / Mark stale
PositionsVendorFull replacement with latest holdings
HoldingsVendorUpdate quantity and market value
PricesMorningstarUpdate security prices
InsuranceVendor (where supported)Update premium and benefit amounts

A Holding (stored in the positions table) represents a specific securities position within an account.

Each holding record contains:

  • security_id — Which security (matched via Morningstar Security Master)
  • quantity — Number of shares/units held
  • cost_basis — Original purchase cost
  • reference — Vendor’s position ID
  • investment_account_id — The investment account that holds this position

Example: A client holds 100 shares of AAPL in their Schwab account with a cost basis of $15,000 — this is one Holding.

API-based sync:

  1. Load OAuth credentials for the mapping’s integration
  2. Call vendor API to get current positions list
  3. Compare with existing positions in database:
    • New positions → INSERT
    • Changed quantity/value → UPDATE
    • No longer returned by vendor → Mark stale or delete
  4. Update integration_mappings.last_completed_at

File-based sync:

  1. Read LATEST positions file from S3
  2. Parse CSV, filter by Rep Code
  3. Match securities (via CUSIP/ticker → Morningstar Security Master)
  4. Same INSERT/UPDATE/DELETE logic
  5. Update sync status

Holdings are typically full-replaced (not incrementally updated) to ensure data consistency.

By account type:

Account TypeHolding Characteristics
Investment AccountSecurities positions (stocks, bonds, mutual funds, ETFs)
Retirement Account (401k, IRA)Retirement investment positions
Bank AccountUsually only cash_balance, no securities positions
Loan AccountLiability, no holdings concept

By data source: The AccountSource enum (45 values) tracks origin — YODLEE, SCHWAB, FIDELITY, TWEAK (manual entry), and 41 other vendor-specific sources.

What does a row in integrations represent?

Section titled “What does a row in integrations represent?”

A row in integrations represents one advisor’s connection to one vendor.

Key fields:

  • advisor_id — FK → advisors table
  • type — IntegrationType enum (47 vendor types)
  • reference — Vendor-side identifier (e.g., Rep Code for file-based)
  • credentials — Encrypted JSON containing OAuth tokens or API keys
  • failed_since / failed_biz_days — Failure tracking; auto-disables after threshold

Constraint: Each advisor can have at most one active integration per vendor type.

What is the reference field in integrations?

Section titled “What is the reference field in integrations?”

The reference field stores the advisor’s identifier in the vendor’s system. Its meaning varies by integration type:

  • File-based: Rep Code — used to locate the correct files on S3 (s3://bucket/vendor/{rep_code}/)
  • API-based: May be empty (identity established via OAuth token) or store a vendor-assigned ID

What is the difference between credentials and reference?

Section titled “What is the difference between credentials and reference?”
FieldPurposeContent
referenceIdentity — who this advisor is at the vendorRep Code, vendor-assigned ID
credentialsAuthentication — how to access vendor APIOAuth tokens, API keys, JWT secrets

Analogy: reference is your employee badge number; credentials is your login password.

credentials is encrypted JSON. Typical content:

{
"access_token": "eyJ...",
"refresh_token": "abc123...",
"expires_at": "2024-01-15T10:00:00Z"
}

The integration_mappings table maps vendor external entities to RightCapital internal objects.

What is the reference field in integration_mappings?

Section titled “What is the reference field in integration_mappings?”

The reference field stores the vendor’s entity ID — the unique identifier for this specific entity (account, household, etc.) in the vendor’s system. Used for deduplication and re-sync.

What does parent_id being null vs non-null mean?

Section titled “What does parent_id being null vs non-null mean?”
  • parent_id = NULLRoot mapping: Represents a household-level link. Only root mappings can trigger synchronization.
  • parent_id ≠ NULLChild mapping: Represents an account, insurance, or other entity nested under a household. Inherits sync behavior from its parent.

Tree structure example:

Integration (Schwab API, advisor_id=123)
└── Mapping #1 (HOUSEHOLD, parent_id=NULL, reference="vendor_hh_001")
├── Mapping #2 (ACCOUNT, parent_id=1, reference="vendor_acct_001")
├── Mapping #3 (ACCOUNT, parent_id=1, reference="vendor_acct_002")
└── Mapping #4 (INSURANCE, parent_id=1, reference="vendor_ins_001")

What are the different mappable_type values and their meanings?

Section titled “What are the different mappable_type values and their meanings?”
mappable_typeMeaningNotes
HOUSEHOLDClient family unitRoot node, typically parent_id=NULL
ACCOUNTFinancial accountBank, investment, loan (core data type)
INSURANCEInsurance policyLife, disability, LTC (core data type)
PERSONFamily memberLinks to persons table
CONTACTCRM contactFrom CRM integrations
TARGET_CATEGORY_MIXTarget asset allocationInvestment portfolio target
NOTECRM noteFrom CRM integrations
TASKCRM taskFrom CRM integrations

ACCOUNT and INSURANCE are the two data integration typesIntegrationMapping::isDataIntegrationMapping() returns true only for these.

The SP (Service Provider) is the system that receives authentication assertions and provides the service. In Integration Team’s SSO implementations, RightCapital is the SP — we receive SAML assertions from vendors, validate them, and create user sessions.

The IdP (Identity Provider) is the system that authenticates users and issues identity information. In IdP-initiated SSO, the vendor is the IdP (e.g., Schwab, Fidelity) — they have already verified the advisor’s identity and transmit it to RC via SAML assertion.

The most common SSO pattern. Advisor starts from the vendor’s system and lands in RightCapital:

  1. Advisor logs into vendor portal
  2. Clicks “Open in RightCapital” link
  3. Vendor generates a SAML Assertion (signed XML document containing user identity)
  4. Vendor POSTs the SAML Assertion to RightCapital’s SSO endpoint
  5. RightCapital validates signature and assertion
  6. Looks up the corresponding Advisor via identifier in the assertion (email/advisor ID)
  7. Creates session, redirects to application

Advisor starts from RightCapital and authenticates through the vendor:

  1. Advisor clicks “Login with [Vendor]” on RightCapital
  2. RC redirects to vendor’s login page
  3. Advisor authenticates at vendor
  4. Vendor POSTs SAML Assertion back to RC
  5. RC validates and creates session

Less commonly used than IdP-initiated.

How is a SAML Response validated? What fields are validated?

Section titled “How is a SAML Response validated? What fields are validated?”
ValidationWhat is CheckedCommon Failure Cause
SignatureXML digital signature verified using vendor’s public X.509 certificateCertificate expired or rotated without notification
IssuerMust match expected vendor identifierConfiguration mismatch
AudienceMust match RC’s SP Entity IDConfiguration mismatch
NotBefore / NotOnOrAfterAssertion must be within valid time windowServer clock skew
SubjectContains user identifier (email or advisor ID)User not found in RC
ConditionsAdditional business rule checksVendor-specific constraints

Common issues:

  • User Not Found: Identifier in SAML does not match any RC advisor record
  • Invalid Signature: Vendor rotated their certificate without notifying RC
  • Expired Assertion: Clock drift between vendor and RC servers exceeds tolerance

Code location: api/app/Http/Controllers/Sso/ — each vendor has its own SSO controller.

Yodlee is the most complex integration and operates as a completely separate system from the standard Integration architecture:

AspectStandard IntegrationYodlee
InitiatorAdvisor configures connectionEnd-user (client) links their bank accounts
Connection levelAdvisor ↔ VendorClient ↔ Financial Institution
Storageintegrations + integration_mappingsDedicated yodlee_providers + yodlee_provider_accounts + account_yodlees tables
IntegrationTypeListed in enumNot in enum (separate system)
AuthenticationOAuth 2.0 (Saloon)JWT RS512 (direct Guzzle)
Sync modeNightly Sync auto-triggerPassive refresh (user-triggered)

Yodlee uses 30+ status codes (additional_status_code in account_yodlees):

Action CategoryMeaningStatus Codes
doneData retrieved successfully4, 5
get_refreshPoll Yodlee for status update1, 2, 3
putUser must re-authenticate via FastLink Edit11, 12, 26-28, 30
manual_refreshUser must provide MFA via FastLink Refresh9, 14
failUnrecoverable errorVarious others

See Yodlee vendor documentation for full details including test account credentials and FastLink widget modes.