API-based Integrations
API-based integrations call Vendor APIs directly to fetch data in real-time or near-real-time. This is the modern approach for integrations, offering more flexibility and fresher data compared to file-based methods.
Code Location: gitlab.rightcapital.io/web-service/api/-/tree/develop/app/Integrations
Architecture
Section titled “Architecture”flowchart LR
subgraph RightCapital
RC[Retail API]
DB[(Database)]
Cache[(Redis Cache)]
end
subgraph Vendor
VA[Vendor API]
end
RC -->|1. API Call with Auth| VA
VA -->|2. Response| RC
RC -->|3. Save| DB
RC -.->|Rate Limit State| Cache
Code Structure (New Skeleton)
Section titled “Code Structure (New Skeleton)”The new API integration architecture is built on Saloon:
app/Integrations/ApiBased/├── Connectors/│ ├── Connector.php # Base connector class│ ├── OauthConnector.php # OAuth-enabled connector│ ├── Middlewares/│ │ └── WriteFatalRequestToFileLog.php│ ├── Oauth/│ │ ├── AccessTokenAuthenticator.php│ │ └── AuthorizationCodeGrant.php│ └── Plugins/│ └── ThreadSafeRefreshAccessToken.php├── Integrators/│ ├── Integrator.php # Base integrator class│ ├── EntitySearchable.php # Search entities trait│ ├── ImportsHousehold.php # Household import trait│ └── TagListable.php # Tag listing trait├── Requests/│ ├── Request.php # Base request class│ └── NeedWriteToFileLog.php # Logging trait├── Plugins/│ └── HasErrorResponseHandler.php└── StashedOauthCredentials.phpKey Concepts
Section titled “Key Concepts”Connector
Section titled “Connector”The Connector handles communication with a Vendor’s API:
- Manages authentication (OAuth tokens, API keys)
- Configures base URL and headers
- Handles rate limiting
- Logs requests/responses
Integrator
Section titled “Integrator”The Integrator orchestrates the data sync process:
- Fetches data from Vendor via Connector
- Transforms Vendor data to RightCapital format
- Saves data to database
Request
Section titled “Request”Represents a specific API endpoint call:
- Defines HTTP method, path, query params
- Can have custom error handling
- Supports request-level logging
Authentication
Section titled “Authentication”OAuth 2.0 Flow
Section titled “OAuth 2.0 Flow”Most API integrations use OAuth 2.0:
sequenceDiagram
participant Advisor
participant RC as RightCapital
participant Vendor
Advisor->>RC: Click "Connect" in Advisor Portal
RC->>Vendor: Redirect to OAuth authorize URL
Advisor->>Vendor: Login and authorize
Vendor->>RC: Redirect with auth code
RC->>Vendor: Exchange code for tokens
Vendor->>RC: Access token + Refresh token
RC->>RC: Store credentials in DB
Token Refresh
Section titled “Token Refresh”- Access tokens expire (typically 1 hour)
- Refresh tokens are used to get new access tokens
ThreadSafeRefreshAccessTokenplugin handles concurrent refresh safely
Error Handling
Section titled “Error Handling”FatalRequestException
Section titled “FatalRequestException”Cannot connect to Vendor server (network issues, DNS, etc.):
- Converted to Integration’s
ConnectException - Handled in Connector base class middleware
RequestException
Section titled “RequestException”Request sent but Vendor returned error response:
HasErrorResponseHandlerplugin processes these- Can be customized per-Connector or per-Request
- Common errors: 401 (auth expired), 429 (rate limit), 404 (not found)
Custom Error Handling
Section titled “Custom Error Handling”// In Request class - handle specific error messagesclass GetHouseholdRequest extends Request{ use HasErrorResponseHandler;
protected function handleErrorResponse(Response $response): void { if (str_contains($response->body(), 'household_deleted')) { throw new VendorHouseholdDeletedException(); } }}Rate Limiting
Section titled “Rate Limiting”Saloon provides built-in rate limit handling:
How It Works
Section titled “How It Works”- Request Middleware: Before sending, check cache for rate limit state
- Response Middleware: On 429 response, cache the limit with retry time
- Automatic Retry: Subsequent requests wait until limit expires
Custom Retry Header
Section titled “Custom Retry Header”If Vendor uses non-standard header (not Retry-After):
class VendorConnector extends Connector{ protected bool $detectTooManyAttempts = false;
protected function resolveLimits(): array { return [ Limit::custom(function (Response $response, Limit $limit) { if ($response->status() !== 429) { return; } $limit->exceeded( releaseInSeconds: RetryAfterHelper::parse( $response->header('X-Retry-After') ), ); }) ]; }}Adding a New API Integration
Section titled “Adding a New API Integration”High-level steps:
- Create Connector - Extend
OauthConnectororConnector - Create Requests - Define API endpoints
- Create Integrator - Implement sync logic
- Add OAuth Controller - Handle authorization flow
- Configure Routes - OAuth callback, disconnect endpoints
- Add to Nightly Sync - Register for scheduled updates
Logging
Section titled “Logging”All API requests/responses are logged:
- File logs: Written via
WriteFatalRequestToFileLogmiddleware - Database logs: Stored for Admin Center viewing
- Logs are essential for debugging Vendor issues
Related
Section titled “Related”- Architecture - Overall system design
- Nightly Sync - Scheduled sync process
- Data Issues - Troubleshooting