Portals & cross-workspace queries
If you're an accountant managing multiple clients, BankSync lets you organise each client into a Portal — a dedicated workspace they own, billed to you. This guide explains how to query data across your portals programmatically with the scope=family parameter.
What's a portal?
A portal is a child workspace under your parent workspace. Each portal has its own:
- Bank connections (the client's own, isolated from yours)
- Members (the client themselves, plus anyone they invite if you allow it)
- Feeds, integrations, jobs
- Soft-delete + 30-day cleanup lifecycle
Your portals roll up to your plan: one Stripe subscription pays for the parent and every portal under it. The parent's owner and admins have implicit owner / admin authority on every portal — no separate invitation needed.
Who can use cross-workspace queries
Family-scoped reads require admin or owner role on the parent workspace. Portal-direct members (clients you've invited) cannot use them — they only see their own portal's data.
The scope query parameter
Every list endpoint accepts an optional scope query parameter that controls what set of workspaces the query operates over.
| Value | Returns | Requires |
|---|---|---|
| self (default) | Just this workspace's rows | The endpoint's standard role |
| family | This workspace + every live portal under it. Each row gains workspaceId and portal | Admin+, top-level workspaces only |
Endpoints supporting scope=family today:
- GET /v1/banks
- GET /v1/feeds
- GET /v1/integrations
- GET /v1/jobs (internal API only for now)
Example: list every bank across all your portals
Your parent-workspace API key works for this — no need to mint a separate key per portal.
curl -X GET "https://api.banksync.io/v1/banks?scope=family" \-H "X-API-Key: bsk_your_api_key_here" \-H "Content-Type: application/json"
Response shape
Each row gains two fields when scope=family is requested:
{"success": true,"data": [{"id": "bnk_abc","name": "Chase Checking","workspaceId": "ws_parent","portal": null,...},{"id": "bnk_xyz","name": "Acme Co Operating","workspaceId": "ws_acme_portal","portal": {"id": "ws_acme_portal","name": "Acme Co","icon": "🏢"},...}]}
workspaceId— the workspace the row actually lives in. For your own rows this is the parent workspace; for portal rows this is the portal's id.portal— attribution badge.nullfor rows owned by the parent; a{ id, name, icon? }object for rows owned by a portal.
Targeting a single portal
Portals are workspaces — you can address one directly via its workspace id. Your parent-scoped API key is automatically authorised against every live portal you own; no per-portal key needed.
# List banks in one specific client's portalcurl -X GET "https://api.banksync.io/v1/banks" \-H "X-API-Key: bsk_your_api_key_here" \-H "X-Workspace-Id: ws_acme_portal"# Or use the workspace id in the URL path (depending on the endpoint).
The credential still has to come from your parent workspace (or be a Firebase session). A key minted on workspace A cannot reach into workspace B's portals — only into A's own.
Error responses
| Status | When |
|---|---|
| 400 | Unknown scope value (anything other than self / family), or scope=family was sent against a portal workspace |
| 403 | Your role on the parent workspace is editor or viewer — scope=family requires admin+ |
| 404 | The workspace id in the URL doesn't exist or was deleted |
Using portals with MCP
Connect an MCP client (Claude Code, Cursor, etc.) to your parent workspace once — your portals appear automatically.
list_workspacessurfaces every portal you manage withisInherited: trueand aparentWorkspaceId.- All workspace-scoped tools (
list_banks,list_feeds, …) accept any portal'sworkspaceIdyou own. - See the MCP setup guide for connecting your client.
For clients, not advisors
If you've been invited into a portal by an accounting firm, programmatic access is intentionally not available — your accountant manages API keys and MCP connections on your behalf. The portal Developer Settings tab is hidden for portal-direct members.
Internal API vs public REST API
Two surfaces ship the same scope=family contract:
- Public REST API (
api.banksync.io/v1/*) — the surface most third-party integrations use. Banks, feeds, integrations all accept?scope=family. - Internal API (
internal-api.banksync.io/api/*) — used by the BankSync web app and MCP server. Same scope semantics, plus a/jobsworkspace-aggregate endpoint.
See also
- Authentication — API keys, scopes, and bearer tokens
- API reference — every endpoint and its response shape
- MCP setup — connect Claude Code or other MCP clients
