Integrations
Each bank, email, sign-in, Slack, accounting and utility integration, what it really does in version 0.1.0, how to set it up, and its redirect URIs.
6 min readUpdated Oct 17, 2026Applies to Subsweep 0.1.0
On this page
Every integration is optional and switches on when its environment variables are set (Configuration). In production, a missing key means that feature says it is not available; the sample data used in development is never shown.
Status at a glance
| Integration | Status in 0.1.0 | Needs |
|---|---|---|
| Plaid (US, Canada) | Works: 12-month import, then an hourly sync | PLAID_CLIENT_ID, PLAID_SECRET, PLAID_ENV |
| TrueLayer (UK) | Imports 12 months once; later syncs bring in nothing once the access token expires | TRUELAYER_CLIENT_ID, TRUELAYER_CLIENT_SECRET |
| Salt Edge (EU) | Return flow fixed, but not tested against a live Salt Edge account | SALTEDGE_APP_ID, SALTEDGE_SECRET; test before offering it |
| Setu Account Aggregator (India) | Not reachable from the interface | Leave unset |
| Gmail receipts | Works for a scan at connect time | Google OAuth client |
| Outlook receipts | Works for a scan at connect time | Microsoft app registration |
| Google sign-in | Works | GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET |
| SMTP or Resend email | Works | See Outbound email |
| Stripe Billing | Works, with caveats | See Plans, billing and selling it as SaaS |
| Slack | Connects; alerts need a manual switch | SLACK_CLIENT_ID, SLACK_CLIENT_SECRET |
| QuickBooks Online, Xero | Connect works; Export now does not reach live data | Leave unset for now |
| Google Workspace, Notion, Zoom usage | No screen, no data fetched | Leave unset |
| Open Exchange Rates | Works | OPEN_EXCHANGE_RATES_APP_ID |
| Cloudflare R2 | Upload request is not signed correctly | Leave unset |
| Sentry (browser) | Works | NEXT_PUBLIC_SENTRY_DSN |
| PostHog | Does not initialise | Leave unset |
| Inngest | Sends one event per hourly run | INNGEST_EVENT_KEY |
Bank feeds
The bank provider is chosen from the workspace's country:
| Country | Provider |
|---|---|
| United States, Canada | Plaid |
| United Kingdom | TrueLayer |
| EU member states | Salt Edge |
| India and anywhere else | None: statements, email and manual add instead |
Onboarding and Settings offer five countries: the United States, Canada, the United Kingdom, Germany (for the EU) and India. Bank connections are a Pro feature.
Plaid
- Create an app in the Plaid Dashboard with the Transactions product, and copy the client ID and secret for the environment you are using.
- Set
PLAID_CLIENT_ID,PLAID_SECRETandPLAID_ENV(sandbox,developmentorproduction). Production use needs Plaid's approval. - Connect bank opens Plaid Link in the browser. On success, Subsweep imports up to 12 months, and the hourly job then pulls the last 48 hours on every run.
Good to know: Plaid amounts are imported as positive values whether money went out or came in; the detection engine filters out most non-software rows. PLAID_REDIRECT_URI is passed to Plaid Link, but there is no page that resumes Link after an OAuth bank's redirect, so banks that require that flow may not complete.
TrueLayer
Set TRUELAYER_CLIENT_ID, TRUELAYER_CLIENT_SECRET and, for live banks, TRUELAYER_ENV=production. Add <BETTER_AUTH_URL>/api/oauth/truelayer/callback as a redirect URI in the TrueLayer console.
The connection imports 12 months of transactions. TrueLayer access tokens are short-lived and Subsweep does not refresh them yet. Once the token expires, the hourly sync quietly brings in nothing while the source still looks connected. Treat it as a one-off import per connection for now: disconnect and reconnect to import newer transactions.
Salt Edge and Setu
Both return pages check who comes back. When a connection is started, Subsweep stores a single-use state for that workspace, valid for 20 minutes. The return page attaches the bank only if that state is found and the person returning is signed in as an Owner or Editor of its workspace; otherwise they land on Sources with an error.
- Salt Edge: the connect session asks Salt Edge to return the connection ID. The return page checks with Salt Edge that the connection belongs to the customer created for this workspace, then stores the connection ID, which is what transaction fetches use.
- Setu Account Aggregator: India workspaces have no bank provider, so nothing in the interface starts a Setu consent, even with
SETU_AA_ENABLED=true. Leave it unset. Its return page uses the consent ID stored with the state, and refuses a different ID orsuccess=false.
Warning
The Salt Edge flow has not been tested against a live Salt Edge account or sandbox. Connect a test bank with your own Salt Edge credentials, and check that transactions arrive, before you offer it to customers.
Email receipts
Both connections are read-only and a Pro feature. They scan when the mailbox is connected (and when someone presses scan again); the hourly job does not re-scan. See Email receipts for what is read.
Gmail
- In Google Cloud Console, enable the Gmail API and configure the OAuth consent screen with the
https://www.googleapis.com/auth/gmail.readonlyscope. - Create an OAuth client of type Web application with the redirect URI
<BETTER_AUTH_URL>/api/oauth/gmail/callback. - Set
GMAIL_CLIENT_IDandGMAIL_CLIENT_SECRET. If you leave them empty, the Google sign-in client is used, so that client then needs the Gmail redirect URI and scope as well.
gmail.readonly is a restricted scope: until Google has verified your app, connecting is limited (test users, or a capped number of people who see an unverified-app warning). See restricted scope verification.
Outlook
- Register an app in Microsoft Entra with the delegated permissions
Mail.Readandoffline_access. - Add the web redirect URI
<BETTER_AUTH_URL>/api/oauth/outlook/callbackand create a client secret. - Set
MICROSOFT_CLIENT_ID,MICROSOFT_CLIENT_SECRETand, for a single-tenant app,MICROSOFT_TENANT.
Note
Not in this version yet: token refresh for either mailbox. A scan more than about an hour after connecting quietly finds nothing new, because the stored access token has expired. Reconnect the mailbox from Settings → Integrations to scan again.
Google sign-in
Create a Google OAuth client (Web application) with the redirect URI <BETTER_AUTH_URL>/api/auth/callback/google, then set GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET. "Continue with Google" appears on the sign-in and sign-up pages once both are set. Sign-in only needs basic profile scopes, so it does not need the restricted-scope review that Gmail scanning does.
Slack
- Create a Slack app at api.slack.com/apps with the bot scopes
chat:write,channels:readandincoming-webhook. - Add the redirect URL
<BETTER_AUTH_URL>/api/oauth/slack/callback. - Set
SLACK_CLIENT_IDandSLACK_CLIENT_SECRET.
Connect from Settings → Integrations and pick a channel during Slack's install screen. Alerts are only posted once the per-alert Slack switch is on, and that switch is not in the interface yet: see Slack for the workaround.
QuickBooks Online and Xero
Settings → Integrations can connect either one over OAuth (redirect URIs <BETTER_AUTH_URL>/api/oauth/quickbooks/callback and <BETTER_AUTH_URL>/api/oauth/xero/callback), and the QuickBooks card has an Export now button that sends active subscriptions as bills.
Warning
Not ready for live use in 0.1.0. The QuickBooks export always calls Intuit's sandbox API with a placeholder company instead of the connected company's ID; the Xero export does not send the tenant header Xero requires; neither refreshes expired tokens; and responses are not checked, so the app reports success even when nothing was created. There is no category-to-account mapping and no scheduled export.
Usage insights
Routes exist to start OAuth with Google Workspace, Notion and Zoom (/api/oauth/usage/<provider>/start), but no screen links to them, and the callback only stores the authorisation code: no seat or activity data is fetched. Seat savings come from Offboarding instead.
Exchange rates
With OPEN_EXCHANGE_RATES_APP_ID set, every hourly run fetches the latest USD rates from Open Exchange Rates for USD, CAD, GBP, EUR and INR, roughly 720 requests a month. Check that your Open Exchange Rates plan allows that. Without a key, fixed fallback rates are used.
Utilities
| Service | Behaviour in 0.1.0 |
|---|---|
| Cloudflare R2 | Attachments are sent with the access key ID as a bearer token instead of a signed S3 request, so uploads fail silently. Leave it unset and attachments go to local disk. |
| Sentry | Browser errors are reported when NEXT_PUBLIC_SENTRY_DSN is set at build time. |
| PostHog | The loader calls PostHog before its script has loaded, so it never starts. |
| Inngest | Each hourly run sends a subsweep/jobs.tick event. No jobs run in Inngest. |
Redirect URIs and webhooks
Replace https://subs.example.com with your BETTER_AUTH_URL.
| Provider | URL |
|---|---|
| Google sign-in | https://subs.example.com/api/auth/callback/google |
| Gmail | https://subs.example.com/api/oauth/gmail/callback |
| Outlook | https://subs.example.com/api/oauth/outlook/callback |
| TrueLayer | https://subs.example.com/api/oauth/truelayer/callback |
| Slack | https://subs.example.com/api/oauth/slack/callback |
| QuickBooks | https://subs.example.com/api/oauth/quickbooks/callback |
| Xero | https://subs.example.com/api/oauth/xero/callback |
| Stripe webhook | https://subs.example.com/api/webhooks/stripe |
| Hourly job | https://subs.example.com/api/cron/tick |