Integration.
BackPro reads from the systems the firm already runs and writes back to the firm's documents of record. Read connectors are read-only by default. Writes are gated by a named approver and signed. No connector ever caches data outside the tenancy.
Read freely. Write with consent.
Reading is asymmetric: connectors pull data in as soon as they're configured. Writing is symmetric in the legal sense: every write is attributed, approved, and signed. There is no write-on-behalf-of pattern without a human in the loop.
READ FLOW (default — automatic)
Source BackPro
┌──────────┐ ┌─────────────────────────────┐
│ SharePoint│──┐ │ │
│ OneDrive │ │ │ Connector pulls deltas │
│ Dropbox │ ├─▶│ Chunks + embeds │
│ Drive │ │ │ Stores in vector store │
│ Xero/MYOB │ │ │ Logs every fetch │
│ CRM ... │──┘ └─────────────────────────────┘
└──────────┘
WRITE FLOW (gated — never automatic for regulated docs)
BackPro draft Sign-off Destination
┌────────────────┐ ┌──────────────────┐ ┌──────────────┐
│ Draft created │ queued for │ Named approver │ │ Document │
│ (e.g. SoA, │──── approval ─────▶ │ reviews + signs │─────▶│ of record │
│ DDQ response) │ │ (e.g. paraplanner│ │ (Word, │
│ │ │ → adviser) │ │ SharePoint, │
│ │ │ │ │ CRM) │
└────────────────┘ └──────────────────┘ └──────────────┘
│ │ │
└───────────── audit log (signed at each transition) ─────────────┘What we connect to, and how.
The standard connector set as of this writing. Every connector is authenticated through the firm's identity provider where the platform supports it, or via a named service account otherwise. The auth method is recorded in the evidence pack at deployment.
| Category | Name | Auth | Mode | Notes |
|---|---|---|---|---|
| Document store | Microsoft SharePoint | OAuth 2.0 + Entra app | Read | Site- or library-scoped. Delta sync via Microsoft Graph. |
| Document store | Microsoft OneDrive (Business) | OAuth 2.0 + Entra app | Read | User scope; the connector impersonates only the named service account. |
| Document store | Dropbox Business | OAuth 2.0 | Read | Team folder scope; per-paper webhook for change notifications. |
| Document store | Google Drive (Workspace) | Service account + DWD | Read | Domain-wide delegation; scoped via OAuth scopes + shared-drive ACLs. |
| CRM | Worksorted | API token (service account) | Read + Write | V1 connector. Read fact-finds + risk profile. Write SOAs + fee consents. |
| CRM | intelliflo office | OAuth 2.0 | Read + Write | Read client, plan, holdings. Write SoA documents to the document tab. |
| CRM | Midwinter Hub | API key | Read | Read-only V1; writes via Midwinter's own document portal. |
| CRM | Iress XPLAN | SOAP API + AdviserNET token | Read | V1 read-only. Write surface dependent on Iress ISV certification (in progress). |
| Finance | Xero | OAuth 2.0 | Read | Read invoices, fee records, contact data. Read-only by design. |
| Finance | MYOB Business | OAuth 2.0 | Read | Same posture as Xero: financial context is read, never written. |
| Identity | Microsoft Entra ID | SAML 2.0 / OIDC | Read | Group + role mapping. JIT user provisioning supported. |
| Identity | Okta | SAML 2.0 / OIDC | Read | SCIM 2.0 for user lifecycle. Group claims mapped to BackPro roles. |
| Identity | Google Workspace | OIDC | Read | For firms standardised on Google identity. |
| Productivity | Microsoft 365 (Word, Outlook) | Graph API | Read + Write | Write SoA drafts back to Word/SharePoint as document of record. |
| Productivity | Slack | OAuth 2.0 | Read + Write | Notifications + named-approver workflow approvals via Slack actions. |
OAuth where possible. Service accounts when necessary.
OAuth is preferred for every connector that supports it, it gives the firm direct control over scopes and the ability to revoke without re-deploying BackPro. Service accounts are used where the platform's OAuth flow does not support headless operation (legacy CRMs, internal LOB apps).
In either case, BackPro never persists user credentials. OAuth refresh tokens live in the tenancy's secret store (Key Vault, Secrets Manager, GCP Secret Manager); access tokens are kept in memory only.
# Microsoft Entra application registration for the SharePoint connector
az ad app create \
--display-name "BackPro · SharePoint connector" \
--sign-in-audience "AzureADMyOrg"
# Permissions: read-only at the site collection scope
az ad app permission add \
--id $APP_ID \
--api 00000003-0000-0000-c000-000000000000 \
--api-permissions \
Sites.Selected=Role \
Files.Read.All=Role
# Grant admin consent
az ad app permission admin-consent --id $APP_ID
# Restrict to the named SharePoint sites only (Sites.Selected)
# Per site grant happens after deployment, scoped by the firm.Every write has a named approver.
A regulated document never reaches the system of record without a human in the chain. The state machine below covers the standard SoA / DDQ-response flow; the rules can be tightened (multi-approver, separation of duties), never loosened.
┌──────────────┐
│ DRAFT │ AI produces output with citations
└──────┬───────┘
│ submit
▼
┌──────────────┐
┌─────── │ IN REVIEW │ Named approver reviews + can edit
│ └──────┬───────┘
│ reject │ approve
│ ▼
│ ┌──────────────┐
│ │ SIGNED │ Approver signature recorded
│ └──────┬───────┘
│ │ deliver
│ ▼
│ ┌──────────────┐
│ │ DELIVERED │ Written to system of record
│ └──────────────┘
│
▼
┌──────────────┐
│ REJECTED │ Returns to draft author with comments
└──────────────┘Signed webhooks, never unsigned.
BackPro emits webhooks on workflow transitions (draft created, approved, delivered, failed). Every webhook is signed with an HMAC-SHA256 header so the firm's receiving system can verify origin. Replay protection via a strict 5-minute timestamp window.
import crypto from 'node:crypto';
// Constant-time signature verification + 5-minute replay window.
function verifyBackProWebhook(req) {
const ts = req.headers['x-backpro-timestamp'];
const sig = req.headers['x-backpro-signature'];
const secret = process.env.BACKPRO_WEBHOOK_SECRET;
const body = req.rawBody; // raw bytes, not JSON.parse'd
// Replay protection
const age = Math.abs(Date.now() / 1000 - Number(ts));
if (age > 300) throw new Error('Stale webhook (>5 min)');
// HMAC-SHA256 of "timestamp.body"
const expected = crypto
.createHmac('sha256', secret)
.update(`${ts}.`)
.update(body)
.digest('hex');
const ok = crypto.timingSafeEqual(
Buffer.from(sig, 'hex'),
Buffer.from(expected, 'hex'),
);
if (!ok) throw new Error('Bad webhook signature');
}