CRM & Data Integration — App Connect
Language: Java (JDK 25)
Framework: Spring Boot 4.0.6
Integration Engine: Apache Camel 4.20
Source: ~/IdeaProjects/NHDS/app-connect
Purpose
App Connect is the integration gateway between Nexivo and external business systems — CRMs, practice management platforms, ITSM tools, and any system with a REST API. It abstracts authentication, credential management, and route execution behind a unified API, allowing new integrations to be added without code changes.
Supported Integrations
| Platform |
Category |
Auth |
Notes |
| HubSpot |
CRM |
OAuth2 / API Key |
Contacts, deals, tickets |
| Dentally |
Practice Management |
API Key / Token |
Appointments, patients |
| ServiceNow |
ITSM |
MCP (Model Context Protocol) |
Incidents, service requests |
| Salesforce |
CRM |
OAuth2 |
Contacts, cases |
| Any REST API |
Generic |
Basic / API Key / OAuth2 / Token |
Via OpenAPI spec upload |
New integrations are registered by uploading an OpenAPI spec — no code deployment required.
Architecture
graph TB
AD[Agent Desktop] -->|REST| AC[App Connect Service]
AC -->|Camel Routes\nYAML-DSL| HubSpot
AC -->|Camel Routes\nYAML-DSL| Dentally
AC -->|MCP| ServiceNow
AC -->|Camel Routes\nYAML-DSL| Other[Any REST API]
subgraph AC[App Connect Service]
API[REST API\nControllers]
CM[Camel Route\nManager]
AF[Auth Filters\nBasic / ApiKey / OAuth2 / Token]
KK[Keycloak OAuth2\nClient Provisioning]
DB[(PostgreSQL)]
Cache[(Redis)]
end
Core Concepts
App
An App is a master template for an integration (e.g. "HubSpot"). It defines:
- Base URL (uri)
- Default authentication config
- OpenAPI spec for the external system
- Available resources and queries
- Optional MCP config (for MCP-based integrations like ServiceNow)
App Connector
An App Connector is a per-tenant instance of an App. Each tenant has their own connector with their own credentials and URI override. Multiple tenants can connect to the same App independently.
Resource & Resource Query
A Resource maps to an entity in the external system (e.g. contacts, appointments). A Resource Query is a specific operation on that resource (e.g. search-by-phone, create-appointment), defined as a Camel YAML route.
Data Model
apps — Integration templates
| Field |
Description |
uuid |
Unique app identifier |
name, description |
Display metadata |
url |
Default base URL |
auth_config |
JSONB — default auth (overridable per connector) |
openapi_spec |
OpenAPI 3.0 schema for the external system |
mcp_config |
JSONB — MCP server config (ServiceNow, etc.) |
app_connectors — Per-tenant instances
| Field |
Description |
id |
UUID primary key |
app_id |
FK → apps |
tenant_id |
Multi-tenant isolation |
uri |
Connector-specific base URL |
auth_config |
JSONB — overrides app-level auth |
status |
PENDING, ACTIVE, ERROR |
client_id, client_secret |
Keycloak OAuth2 credentials (auto-provisioned) |
resources — External entities
| Field |
Description |
alias |
Kebab-case identifier (e.g. contacts) |
schema |
JSONB — OpenAPI schema for this resource |
config |
Resource-level configuration |
resource_queries — Operations on resources
| Field |
Description |
alias |
Kebab-case identifier (e.g. search-by-phone) |
config |
Camel YAML route definition for this query |
mcp_servers — MCP integration instances
| Field |
Description |
tenant_id |
Multi-tenant isolation |
server_url, token_url |
MCP server endpoints |
access_token, refresh_token |
OAuth2 tokens |
allowed_tools |
JSONB — tool whitelist |
agent_id |
Associated AI agent |
Authentication
App Connect supports four authentication strategies, applied per connector:
| Type |
Class |
Mechanism |
| Basic Auth |
BasicAuthFilter |
Authorization: Basic <base64(user:pass)> |
| API Key |
ApiKeysAuthFilter |
Custom header (e.g. X-API-KEY) |
| OAuth2 |
OAuth2Filter |
Full OAuth2 flow with auto token refresh |
| Token |
TokenAuthFilter |
Static bearer token, custom header, or query param |
Auth config is stored as JSONB in app_connectors.auth_config. On connector creation, an OAuth2 client is automatically provisioned in Keycloak (AppConnectorCreatedListener).
Query Execution Flow
sequenceDiagram
participant Desktop as Agent Desktop
participant AC as App Connect
participant Camel as Camel Route
participant Ext as External System
Desktop->>AC: GET /app-connectors/{id}/resources/{alias}?q={queryAlias}¶m=value
AC->>Camel: ensure route loaded (direct:{resourceAlias}-query-{queryAlias})
AC->>Camel: ProducerTemplate.request(routeId, params)
Camel->>Camel: AuthUtils.resolveHeaders(authConfig)
Camel->>Ext: HTTP call with auth headers + params
Ext-->>Camel: Response
Camel-->>AC: Transformed response
AC-->>Desktop: JSON result
Camel routes are defined as YAML DSL in resource_queries.config. They are loaded dynamically at startup and reloaded on connector changes (ConnectorRoutesChangedEvent).
REST API
App Connectors
| Method |
Path |
Purpose |
GET |
/app-connectors |
List all connectors for tenant |
POST |
/app-connectors |
Create connector (multipart: config, openapi-spec, logo) |
GET |
/app-connectors/{id} |
Get connector details |
PUT |
/app-connectors/{id} |
Update connector config |
DELETE |
/app-connectors/{id} |
Delete connector |
PUT |
/app-connectors/{id}/openapi |
Upload / update OpenAPI spec |
POST |
/app-connectors/{id}/resources-refresh |
Reload Camel routes |
Resource Queries
| Method |
Path |
Purpose |
GET |
/app-connectors/{id}/resources |
List resources |
GET |
/app-connectors/{id}/resources/{alias} |
Execute default query |
GET |
/app-connectors/{id}/resources/{alias}?q={queryAlias} |
Execute specific query |
GET |
/app-connectors/{id}/resources/{alias}/schema |
Get OpenAPI schema for resource |
GET |
/app-connectors/{id}/resources/{alias}/queries |
List available queries |
MCP Servers
| Method |
Path |
Purpose |
POST |
/mcp-servers |
Register MCP server |
GET |
/mcp-servers |
List MCP servers for tenant |
DELETE |
/mcp-servers/{id} |
Delete MCP server |
GET |
/mcp-servers/discover?serverUrl=... |
OAuth discovery |
POST |
/mcp-servers/register-client |
Dynamic OAuth client registration |
Adding a New Integration
- Upload the external system's OpenAPI spec via
POST /apps or PUT /app-connectors/{id}/openapi
- Define resources (entities) and resource queries (operations) with Camel YAML route configs
- Configure authentication (Basic / API Key / OAuth2 / Token)
- Create a per-tenant connector via
POST /app-connectors
- Query the integration via
GET /app-connectors/{id}/resources/{alias}?q={queryAlias}
No code changes or redeployment required for new integrations.
Multi-Tenancy
tenant_id column on all connector and MCP server records
X-Tenant-ID request header resolved by HttpHeaderTenantResolver
TenantContextHolder enforces tenant scoping on all queries
- Each tenant's OAuth2 credentials are isolated in their own Keycloak organisation
External Dependencies
| Dependency |
Purpose |
| PostgreSQL |
Connector config, resources, queries, MCP servers |
| Redis |
Caching |
| Apache Camel |
Dynamic route execution for external API calls |
| Keycloak |
OAuth2 client provisioning per connector |
| Tool Management Service |
Syncs OpenAPI specs for AI tool registration |
| OpenTelemetry / Prometheus |
Observability |