1. Guides
Synci API
  • Overview
  • Guides
    • Authentication
    • Building OAuth apps
    • Managed apps
    • Webhooks
    • Pagination, filtering, and sorting
    • Errors
    • Rate limits
    • MCP: Connect AI assistants
    • Synci as a SimpleFIN provider
  • Synci API
    • Finance
      • Connections
        • List all financial connections
        • Create a new financial connection
        • Get financial connection
        • Delete financial connection
        • Disable financial connection
        • Reconfirm the consent of a financial connection
      • Accounts
        • List all financial accounts
        • Get financial account
        • Update financial account
        • Delete financial account
        • Bulk delete financial accounts
        • Sync financial account data
        • Reset financial account config to defaults
        • List all account balance entries
        • Get account balance entry
        • List account holdings
        • Get account holding
      • Transactions
        • List all transactions
        • Create a new transaction
        • Get transaction
        • Update transaction
        • Delete transaction
        • Bulk delete transactions
      • Institutions
        • Get supported countries
        • List all supported institutions
        • Get supported institution
    • Destinations
      • YNAB Connections
        • YNAB Budgets
          • YNAB Budget Accounts
            • List all YNAB budget accounts
            • Get YNAB budget account
            • Delete YNAB budget account
          • List all YNAB budgets
          • Get YNAB budget
          • Delete YNAB budget
          • Update YNAB budget
        • List all YNAB connections
        • Create new YNAB connection
        • Get YNAB connection
        • Delete YNAB connection
        • Update YNAB connection
        • Refresh YNAB connection
      • Lunch Money Connections
        • Lunch Money Accounts
          • List all Lunch Money accounts
          • Get Lunch Money account
          • Delete Lunch Money account
        • List all Lunch Money connections
        • Create new Lunch Money connection
        • Get Lunch Money connection
        • Delete Lunch Money connection
        • Update Lunch Money connection
        • Refresh Lunch Money connection
      • Google Sheets Connections
        • Sheets
          • List destination spreadsheets for a connection
          • Register a destination spreadsheet
          • Get a destination spreadsheet
          • Update a destination spreadsheet
          • Delete a destination spreadsheet
          • List the worksheets (tabs) of a spreadsheet
          • Create a new worksheet (tab) in a spreadsheet
        • List all Google Sheets connections
        • Create new Google Sheets connection
        • Get Google Sheets connection
        • Delete Google Sheets connection
        • Update Google Sheets connection
        • Refresh Google Sheets connection
      • SimpleFIN Connections
        • List all SimpleFIN connections
        • Create new SimpleFIN connection
        • Get SimpleFIN connection
        • Update SimpleFIN connection
        • Delete SimpleFIN connection
        • Regenerate setup token
        • Revoke access credentials
        • List accounts observed syncing
    • Transfer Links
      • List all transfer links
      • Create a new transfer link
      • Get transfer link
      • Update transfer link
      • Delete transfer link
      • Retry transfers for all transfer links
      • Retry transfers for a specific transfer link
    • Transfer Logs
      • List all transfer logs
      • Get transfer log
      • Undo a transfer
      • Undo multiple transfers
    • Rules
      • Create a new rule
      • List all rules
      • Get rule
      • Rule field catalog
      • Update rule
      • Delete rule
      • Attach rule to transfer link
      • Detach rule from transfer link
      • Reorder rules for a transfer link
      • Attach rule to a financial account
      • Detach rule from financial account
      • Reorder rules for a financial account
      • Preview a rule against transactions (dry-run)
    • Rule Logs
      • List all rule logs
      • Get rule log
      • Undo multiple rule actions
      • Undo a rule action
    • Webhooks
      • List all webhook events
      • List all webhook endpoints
      • Create new webhook endpoint
      • Get webhook endpoint
      • Update webhook endpoint
      • Delete webhook endpoint
      • Test webhook endpoint
      • Rotate webhook secret
    • User
      • Get the authenticated user
  • Schemas
    • AccountHoldingResource
    • BalanceCategoryEnum
    • BankDataTypeEnum
    • BalanceTypeEnum
    • BulkDeleteTransactionsRequest
    • BulkDeleteFinancialAccountsRequest
    • BulkUndoRuleLogsRequest
    • BulkUndoTransferLogsRequest
    • CreateGoogleSheetTabRequest
    • CreateFinancialConnectionRequest
    • CreateGoogleSheetsConnectionRequest
    • CreateLunchMoneyConnectionRequest
    • CreateTransactionRequest
    • CreateRuleRequest
    • CreateWebhookEndpointRequest
    • CreateTransferLinkRequest
    • CreateYnabConnectionRequest
    • DestinationTypeEnum
    • EnrichmentProviderEnum
    • FinancialAccount
    • FinancialAccountBalanceResource
    • FinancialAccountResource
    • GoogleSheetResource
    • FinancialConnectionResource
    • GoogleSheetsConnectionResource
    • HealthResource
    • HealthStatusEnum
    • InstitutionConfigMappedDateEnum
    • InstitutionConfigMappedTextEnum
    • InstitutionConfigResource
    • InstitutionResource
    • IntegratorEnum
    • LunchMoneyAccountResource
    • LunchMoneyConnectionResource
    • PreviewRuleRequest
    • ReauthorizeFinancialConnectionRequest
    • RetryTransfersAllLinksRequest
    • RetryTransfersRequest
    • RuleActionResource
    • RuleConditionResource
    • RuleActionTypeEnum
    • RuleLogResource
    • RuleOperatorEnum
    • SimpleFinAccountResource
    • RuleScopeEnum
    • RuleResource
    • SimpleFinAppEnum
    • RuleTypeEnum
    • SimpleFinConnectionResource
    • StoreGoogleSheetRequest
    • TransactionTypeEnum
    • TestWebhookRequest
    • StoreSimpleFinConnectionRequest
    • SyncFinancialAccountRequest
    • TransferLink
    • TransactionResource
    • TransferLinkSyncModeEnum
    • TransferLogStatusEnum
    • TransferLinkResource
    • TransferLogResource
    • UpdateGoogleSheetRequest
    • UpdateGoogleSheetsConnectionRequest
    • UpdateFinancialAccountRequest
    • UpdateLunchMoneyConnectionRequest
    • UpdateRuleRequest
    • UpdateTransactionRequest
    • UpdateSimpleFinConnectionRequest
    • UpdateYnabBudgetRequest
    • UpdateWebhookEndpointRequest
    • UpdateYnabConnectionRequest
    • UpdateTransferLinkRequest
    • WebhookEventResource
    • UserProfileResource
    • WebhookTypeEnum
    • YnabBudgetAccountResource
    • WebhookEndpointPayloadVersion
    • YnabBudgetAccountTypeEnum
    • WebhookEndpointResource
    • YnabBudgetResource
    • YnabConnectionResource
    • WebhookEventTypeEnum
  1. Guides

Managed apps

A Managed app lets you offer bank connections inside your own product to people who do not have a Synci account and never will. You register each of your users with Synci through a server-to-server API, send them to a hosted portal to connect their bank, and then read their financial data with a token scoped to that one person.
You pay for their connections out of your own Synci plan. They never see Synci branding beyond the portal, never create a password, and never get a bill.
If your users already have (or would happily create) their own Synci accounts, you want a Standard OAuth app instead: it is simpler, and they pay for their own connections.

How it fits together#

Two separate surfaces are involved, and it is worth keeping them straight:
SurfaceBase URLAuthenticationWhat it is for
Managed Users APIhttps://api.synci.io/managed/v1HTTP Basic (client_id:client_secret)Managing your users: register, list, update, delete, mint tokens, mint portal links
Data APIhttps://api.synci.io/api/v1Bearer token for one userReading and managing that user's financial data

Create the app#

In the Synci dashboard, go to Developers → Apps → New app and choose Managed users as the app type.
The choice is permanent. It decides who is billed for every connection the app creates, so it cannot be changed afterwards. If you pick wrong, create a new app.
You get a client ID and a client secret (shown once, prefixed oac_). Both are used as HTTP Basic credentials on the Managed Users API. Store the secret on your server; it is never safe in a browser or a mobile binary.
A Managed app has no redirect URIs; the field is rejected. Nobody is sent through an OAuth consent screen, so there is nowhere to redirect to. What you configure instead is a portal return URL (see App settings).

What a Managed app may request#

A Managed app is restricted to bank-data scopes. These eight and no others:
profile:read · accounts:read · accounts:write · transactions:read · transactions:write · sensitive:read · financial-connections:read · financial-connections:write
Rules, transfer links, destinations and webhooks are deliberately out of reach: those are consumer-product features that belong to a Synci account, and your users do not have one. See the scope table in Authentication.

Connect a user, end to end#

1
Authenticate to the Managed Users API
Standard HTTP Basic. Your client ID is the username, your client secret is the password.
ResponseMeaning
401 invalid_clientBad or missing credentials, or the app is not a Managed app. Deliberately indistinguishable, so this endpoint cannot be used to discover which client IDs exist.
403 client_suspendedThe app has been suspended by Synci.
Rate limits are 300 requests/minute per client and 600/minute per source address. Errors carry error and message keys.
2
Register the user
FieldRules
emailRequired. Valid address, max 255.
external_user_idOptional, max 255. Your own identifier for this person, and the easiest way to reconcile later.
nameOptional, max 255. Defaults to a title-cased version of the email local part.
connection_limitOptional integer, 0 to 20. A per-user cap on financial connections.
201 Created returns the user and their first token pair, unwrapped at the top level:
{
    "user_id": "9f1c2e64-1f3a-4c77-9b21-8a4c5d6e7f80",
    "external_user_id": "acct-9001",
    "email": "alice@example.com",
    "email_verified": false,
    "onboarded_at": null,
    "limits": {
        "financial_connections": null
    },
    // caps ALL connection types, not just bank
    "created_at": "2026-08-28T09:12:00+00:00",
    "token_type": "Bearer",
    "expires_in": 3600,
    "access_token": "eyJ0eXAiOi...",
    "refresh_token": "def50200..."
}
user_id is an opaque UUID with no relationship to Synci's internal identifiers, and it is scoped to your app. It is the id you use everywhere else. Asking for another developer's user returns 404, identical to asking for one that does not exist.
Registration is atomic: if the token cannot be issued, the user is not created.
StatuserrorWhen
409email_already_registeredYou already registered that address. Uniqueness is per app: another developer can register the same person, and an address belonging to a Synci account can still be registered.
409external_user_id_takenYou already used that external_user_id.
403testing_limit_reachedAn unreviewed app hit the registration cap. The response includes a limit key. Counted across all your unreviewed apps, not per app.
402subscription_requiredYour app is live but you have no Synci plan. Your users' connections draw on your plan's connection quantity, so there is nothing for them to draw on.
3
Send them to the portal
Your user connects their bank in Synci's hosted connection portal. You do not build this, and you never touch their bank credentials.
Mint a single-use link:
{
    "data": {
        "url": "https://app.synci.io/portal/session?token=...&app_client_id=...",
        "expires_at": "2026-08-28T09:22:00+00:00"
    }
}
Send the user to url. They arrive already signed in, with no password, no code and no Synci account.
The Synci connection portal, as your user sees it
Your app's name and logo appear here once it is approved; until then the portal tells the user it has not been reviewed yet and shows an initial avatar instead.
The link lives 10 minutes and is strictly single-use. Mint it at the moment you are about to redirect, not in advance. A replayed link is dead.
return_url is optional. Provide it to override the app default for this one visit, which is useful when you know which of your screens they left from. Omit it to use the app's configured portal_return_url.
An expired link degrades gracefully. The app id rides along in the URL, so a user who opens a stale link is offered an emailed sign-in code by the portal instead of hitting a dead end. Nothing for you to build; see When a link expires.
4
Read their data
Use the access token from registration. It is an ordinary Bearer token against the normal API:
The token reaches only that user's data, and only within your app's scopes. Everything outside those scopes returns 403; another user's data returns 404.
Unlike a Standard app (where the user picks which accounts to share on a consent screen), a Managed app's token sees every account that user connects, including ones connected later. There is no account picker in this lane; you registered them, and the grant is implicit in that.
Sensitive identifiers (IBAN, account and card numbers, account holder names) are redacted unless your app holds sensitive:read. This is enforced server-side and cannot be switched off by the caller.
5
Mint fresh tokens as needed
Access tokens last 1 hour, refresh tokens 30 days. Refresh normally, or mint a new pair at any time:
Equivalently, at the OAuth token endpoint:
A user_id that is not one of your users returns 400 with "error": "invalid_request"; the specific reason is in the hint field, not error_description.

When a link expires#

Portal links live 10 minutes, and people do not always click them in 10 minutes. Rather than showing a dead end, the portal offers to email that user a six-digit sign-in code, scoped to your app. They enter it and land in the same place the link would have taken them.
There is nothing to build and nothing to call. The portal owns this entirely: it decides when to offer a code, sends it, and handles the retries. Your part is already done when you mint the link, because the app id travels in the URL and is what lets the portal scope the code to your app rather than to the address in general.
Two things follow from it that are worth knowing:
A user who lets a link expire is not stuck. You do not need to detect it or mint a replacement, though minting a fresh link is always safe if your own UI is bringing them back anyway.
email_verified flips to true when someone redeems a code, and only then. A link you minted never sets it: you vouching for an address is not evidence that the person controls that mailbox, and the field would be worthless if it said otherwise. Read it as "has proven they can read this inbox", not as "we checked the address is valid".

Managing users#

EndpointWhat it does
GET /managed/v1/usersList your users. Filter by external_user_id, email, per_page (default 50, max 200).
GET /managed/v1/users/{user_id}One user, including connections_count (all connection types).
PUT or PATCH /managed/v1/users/{user_id}Update external_user_id or connection_limit. Email cannot be changed.
DELETE /managed/v1/users/{user_id}204. Hard-deletes them, cascades their connections, accounts and transactions, revokes every token, and frees their email address for re-registration.

See every connection at once#

Rather than polling each user, list every connection your app pays for. This is not bank-only: your users' brokerage and crypto-exchange connections come back here too, and the provider filter is how you narrow to one kind.
GET /managed/v1/connections
Filters: user_id, external_user_id, status, provider, expiring_within_days, per_page.
expiring_within_days is the one to build on. Under PSD2 a bank consent lapses every 90 to 180 days, so a nightly ?expiring_within_days=14 sweep tells you exactly who needs to re-authorize before anything breaks.

App settings#

All Managed-only, all in Developers → Apps → your app, and all take effect immediately: none are review-gated.
SettingDefaultWhat it does
Portal return URLnoneWhere "Continue to ‹App›" sends a user who has finished in the portal. No button is shown if unset.
Providersall we allowWhich bank-data providers your users may connect through: today GoCardless, Akahu and SnapTrade. A value can only narrow the set, never widen it.
CountriesallWhich countries your users may connect banks in (ISO-3166-1 alpha-2).
Default connection limitSynci defaultBank connections per user. A per-user connection_limit overrides it.
Email your usersonWhether Synci emails your users about expiring, failing and disabled connections.
Transaction enrichmentoffMerchant name, logo and category on transactions. Requires a Pro plan; no per-transaction charge.
History depthprovider defaultHow much transaction history a new connection requests, capped per institution.
Data retention365 daysHow long Synci keeps your users' transaction data.
The portal filters its bank picker on providers and countries, so a user never sees an institution they cannot connect.

Before you turn off "email your users"#

Synci's expiry emails are a large part of what you get here. A bank consent lapses every 90 to 180 days, and someone has to tell the user and give them somewhere to re-authorize. Off means that is yours to do.
Be aware of what it costs today: there are no connection-lifecycle webhooks yet, so "handle it yourself" means polling GET /managed/v1/connections?expiring_within_days=... and building your own re-consent prompt. Left on, your users get the mail, it names your app, and the link drops them straight into the portal to fix it.

Billing#

You pay for a Managed app the same way every other Synci customer pays: a Basic or Pro subscription on your own account, priced per bank connection. There is no separate developer invoice and nothing to reconcile.
Your subscription quantity is a single pool, shared between your own personal bank connections and every connection your users create across every Managed app you own. Buy 50, and your users can hold 50 between them.
Registering users is free. You only pay when one of them actually connects a bank.
Up to 200 connections are self-serve. Beyond that, or if you would rather be billed monthly on actual usage than prepay a quantity, get in touch: those are commercial agreements rather than something you can set up yourself.
When a connection is refused, the code tells you which ceiling you hit:
codeMeaning
no_subscriptionYou have no active plan. Not "full", but never started.
trial_limit_reachedYou are on a free trial; unreviewed apps share a small allowance.
capacity_reachedYou have used the connections you subscribed for. Raise your plan quantity.

Review and going live#

StateWhat it permits
TestingReal banks, real data, capped: a small number of registered users and, while you are on a free trial, 2 live connections across all your unreviewed apps. Counted per developer account, because creating another app is free.
Pending reviewUnder review by Synci.
ApprovedCaps lifted; the portal stops showing your users an "unreviewed" notice and starts showing your logo.
RejectedEdit and resubmit; a reason is given.
To submit for review you need developer_name, homepage_url and privacy_policy_url set, and an active Synci plan. Paying lifts the trial cap immediately, reviewed or not: you bought the slots.
Until you are reviewed, the portal tells your users so and falls back to an initial avatar rather than your logo. An unvetted image never reaches a user.

Your users are yours#

Deleting a user is real, and it frees their email address for re-registration.
They can revoke a bank connection themselves from the portal at any time.
They are not a Synci account. They cannot log in, cannot be reached by Synci sales, and see no Synci dashboard.
If your app is suspended or removed, Synci emails them once to say your app can no longer access their bank connections.

Common errors#

StatusWhen
401Bad Basic credentials on the Managed Users API, or an expired or revoked Bearer token on the Data API.
402No active Synci plan. Subscribe before registering users.
403Connection capacity or trial ceiling reached, app suspended, or a scope your app does not hold.
404The user or resource does not exist, or is not yours.
409Email or external_user_id already used by your app.
422Validation failed; the body lists per-field errors.
429Rate limit exceeded.

Support#

Email support@synci.io. Include your client ID (never your secret) and, where relevant, the user_id. Both are safe to quote.
Modified at 2026-08-29 14:03:59
Previous
Building OAuth apps
Next
Webhooks
Built with