PageSourceSearch

https://app.banksync.io/assets/apps-overview-DF3q_EGc.js

js banksync.io collected 2026-10-04 12:26:17 UTC 8,323 bytes, 74 lines download raw bytes

1import{j as e}from"./ui-C0aHyMyD.js";function a(t){const n={a:"a",code:"code",em:"em",h2:"h2",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",ul:"ul",...t.components},{Callout:s}=n;return s||r("Callout"),e.jsxs(e.Fragment,{children:[e.jsxs(n.p,{children:["A BankSync ",e.jsx(n.strong,{children:"app"}),` is your product, installed into someone else's BankSync
2workspace. They own the workspace. You hold a grant against the banks they chose
3to give you, you read only those, and — if you ask for it — the data arrives on
4your own endpoint as it syncs.`]}),`
5`,e.jsxs(n.p,{children:["The unit you build against is the ",e.jsx(n.strong,{children:"install"}),`: one app, in one workspace,
6holding one grant. Not a user, not a bank connection. Everything else in this
7section is a detail of getting an install created, keeping it working, and
8reading through it.`]}),`
9`,e.jsx(n.h2,{id:"is-an-app-what-you-want",children:"Is an app what you want?"}),`
10`,e.jsxs(n.p,{children:[`There are four ways data leaves BankSync, and picking the wrong one costs real
11time. The question that separates them is `,e.jsx(n.strong,{children:`whose workspace holds the data, and
12who is paying`}),"."]}),`
13`,e.jsxs(n.table,{children:[e.jsx(n.thead,{children:e.jsxs(n.tr,{children:[e.jsx(n.th,{children:"You want to…"}),e.jsx(n.th,{children:"Use"}),e.jsx(n.th,{children:"Whose workspace"})]})}),e.jsxs(n.tbody,{children:[e.jsxs(n.tr,{children:[e.jsxs(n.td,{children:["Read ",e.jsx(n.strong,{children:"your own"})," workspace from a script or an agent"]}),e.jsx(n.td,{children:"An API key"}),e.jsx(n.td,{children:"Yours"})]}),e.jsxs(n.tr,{children:[e.jsx(n.td,{children:"Push your own data into a tool you already use"}),e.jsx(n.td,{children:"A destination"}),e.jsx(n.td,{children:"Yours"})]}),e.jsxs(n.tr,{children:[e.jsxs(n.td,{children:["Let ",e.jsx(n.strong,{children:"your users"})," connect their banks and read their data"]}),e.jsx(n.td,{children:e.jsx(n.strong,{children:"An app"})}),e.jsx(n.td,{children:"Each user's own"})]}),e.jsxs(n.tr,{children:[e.jsx(n.td,{children:"Manage many clients' workspaces as their accountant"}),e.jsx(n.td,{children:"A client portal"}),e.jsx(n.td,{children:"A child of yours"})]})]})]}),`
14`,e.jsxs(n.p,{children:["An ",e.jsx(n.strong,{children:"API key"})," is a secret that gives programmatic access to ",e.jsx(n.em,{children:"your"}),` workspace
15over the REST API and the MCP server. It is the right tool when the data you
16want is already yours. It is the wrong tool for a product with users, because
17you would be handing every user the same credential to your own workspace.`]}),`
18`,e.jsxs(n.p,{children:["A ",e.jsx(n.strong,{children:"destination"}),` — Google Sheets, Notion, a database, a webhook — pushes a
19workspace's synced data out to a tool. Also yours, also not a product.`]}),`
20`,e.jsxs(n.p,{children:["A ",e.jsx(n.strong,{children:"client portal"}),` is a child workspace under your parent workspace, for an
21accountant or bookkeeper managing clients. Every portal bills to the parent's
22subscription, and the parent's owner and admins hold implicit authority over
23every portal beneath it. That is exactly right for an accountant and exactly
24wrong for a software product: your users would be paying through you, and you
25would hold administrative access to all of their bank data. If you are building
26something your users install, you want an app.`]}),`
27`,e.jsx(s,{type:"note",title:"Who pays",children:"The builder pays for the app. A user installing your app does not need a paid BankSync plan for you to read their data through the install — the plan gates the workspace-facing API, not an app acting on a grant."}),`
28`,e.jsx(n.h2,{id:"the-shape-of-it",children:"The shape of it"}),`
29`,e.jsx(n.p,{children:"Five steps, each with its own page."}),`
30`,e.jsxs(n.ol,{children:[`
31`,e.jsxs(n.li,{children:[e.jsx(n.strong,{children:"Register the app."}),` Its name, slug, branding, and the scopes it may
32request, plus one or more client credentials. See
33`,e.jsx(n.a,{href:"/docs/apps/registering-an-app",children:"Register an OAuth app"}),"."]}),`
34`,e.jsxs(n.li,{children:[e.jsx(n.strong,{children:"Declare a manifest."}),` What your app reads, what furniture it creates in the
35workspace (tables, feeds, enrichment steps), and where events should be
36delivered. See `,e.jsx(n.a,{href:"/docs/apps/apps-reference",children:"Building a BankSync app"}),"."]}),`
37`,e.jsxs(n.li,{children:[e.jsx(n.strong,{children:"Send users to the connect link."}
37)," ",e.jsx(n.code,{children:"https://app.banksync.io/connect/<slug>"}),`
38— BankSync shows them who is asking and for what, signs them in or signs them
39up, walks them through connecting their bank, takes their approval, and hands
40them back to you with an authorization code. See
41`,e.jsx(n.a,{href:"/docs/apps/connect-link",children:"The connect link"}),"."]}),`
42`,e.jsxs(n.li,{children:[e.jsx(n.strong,{children:"Read, and receive."})," Exchange the code for a token and read ",e.jsx(n.code,{children:"/v1"}),` on that
43user's behalf; take signed events on your endpoint as their data syncs.`]}),`
44`,e.jsxs(n.li,{children:[e.jsx(n.strong,{children:"List it"}),`, if you want to be found. See
45`,e.jsx(n.a,{href:"/docs/apps/app-listing",children:"Listing and publishing your app"}),"."]}),`
46`]}),`
47`,e.jsx(n.p,{children:`Your users never see a BankSync workspace, dashboard or navigation at any point
48in that flow. The connect page renders before anyone signs in, on purpose: the
49person following it has usually never heard of BankSync, and they should learn
50who your app is and what it will read before they are asked to make an account.`}),`
51`,e.jsx(n.h2,{id:"returning-users",children:"Returning users"}),`
52`,e.jsxs(n.p,{children:["A user who already installed you and needs to add a second bank does ",e.jsx(n.strong,{children:"not"}),` go
53back through the full flow. The connect link takes an `,e.jsx(n.code,{children:"intent"}),":"]}),`
54`,e.jsx(n.pre,{children:e.jsx(n.code,{className:"language-text",children:`https://app.banksync.io/connect/<slug>?intent=add_bank&return_to=https%3A%2F%2Fyourapp.example%2Fdone
55`})}),`
56`,e.jsxs(n.p,{children:[e.jsx(n.code,{children:"intent=add_bank"}),` skips the gate and the consent screen — they already installed
57you and already saw what you can read — runs the bank step, and returns them to
58your own page. `,e.jsx(n.code,{children:"intent=reauth"}),` does the same for one connection that has broken.
59Neither mints a new authorization code, because you already hold a grant.`]}),`
60`,e.jsx(n.p,{children:`Note there is no workspace id anywhere in that URL. A third party cannot know
61which workspace a browser is signed in to, and with the connect link it does not
62need to.`}),`
63`,e.jsx(s,{type:"warning",title:"One gap to design around",children:"The install path is built end to end and the platform is live in production. One gap worth knowing before you design around it: connection.created is delivered once per bank, the first time it reports healthy, and no event tells you a connection has RECOVERED from requires_reauth. Building a BankSync app marks which side of that line each feature is on."}),`
64`,e.jsx(n.h2,{id:"next",children:"Next"}),`
65`,e.jsxs(n.ul,{children:[`
66`,e.jsxs(n.li,{children:[e.jsx(n.a,{href:"/docs/apps/registering-an-app",children:"Register an OAuth app"}),` — the app record, its
67client credentials, and the redirect URIs every return URL must match.`]}),`
68`,e.jsxs(n.li,{children:[e.jsx(n.a,{href:"/docs/apps/apps-reference",children:"Building a BankSync app"}),` — the manifest contract,
69the install lifecycle, your endpoint and its signed events.`]}),`
70`,e.jsxs(n.li,{children:[e.jsx(n.a,{href:"/docs/apps/connect-link",children:"The connect link"}),` — every query parameter, the three
71intents, the `,e.jsx(n.code,{children:"return_to"})," rules, and the round trip back."]}),`
72`,e.jsxs(n.li,{children:[e.jsx(n.a,{href:"/docs/apps/app-listing",children:"Listing and publishing your app"}),` — review, and being
73found.`]}),`
74`]})]})}function i(t={}){const{wrapper:n}=t.components||{};return n?e.jsx(n,{...t,children:e.jsx(a,{...t})}):a(t)}function r(t,n){throw new Error("Expected component `"+t+"` to be defined: you likely forgot to import, pass, or provide it.")}export{i as default};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.