@lunch-money/developer-docs 2.11.2 → 2.11.3-preview.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -45,10 +45,22 @@ const intro = fs.readFileSync(path.join(docsRoot, 'docs/getting-started.md'), 'u
45
45
  }
46
46
  ],
47
47
  "redirects": [ /* { from, to, status } */ ],
48
- "sidebar": { "docs": [ /* … */ ], "v1": [ /* … */ ] }
48
+ "sidebar": {
49
+ "docs": [
50
+ {
51
+ "section": "OAUTH",
52
+ "collapsed": true, // optional; initially collapse this section
53
+ "items": [ /* { label, path, external? } */ ]
54
+ }
55
+ ],
56
+ "v1": [ /* … */ ]
57
+ }
49
58
  }
50
59
  ```
51
60
 
61
+ Sidebar sections initially appear expanded unless their optional `collapsed`
62
+ property is `true`.
63
+
52
64
  ## Versioning
53
65
 
54
66
  The package version tracks the newest API version this content documents (e.g. `2.11.x`
@@ -45,7 +45,7 @@ A few common, low-friction ways to brand an integration:
45
45
 
46
46
  1. **README or docs** — a short "Built with Lunch Money" line plus the Powered by badge linking to <a href="https://lunchmoney.app/" target="_blank" rel="noopener noreferrer">lunchmoney.app</a>
47
47
  2. **App footer or about screen** — the Powered by badge next to your own branding
48
- 3. **Onboarding or connect flow** — official logo when prompting users to authorize or paste an access token
48
+ 3. **Onboarding or connect flow** — official logo when asking users to connect their Lunch Money account through OAuth. Applications intended for other users should not ask them to paste a personal access token; see the [OAuth overview](/oauth)
49
49
  4. **Community listings** — consistent naming ("My Tool for Lunch Money") so users can find and trust your project
50
50
 
51
51
  Keep your own product name primary. Lunch Money branding should signal the connection, not compete with your identity.
@@ -5,12 +5,12 @@ Welcome to the Lunch Money developer API! This API has enabled the Lunch Money u
5
5
  Most users interact with their Lunch Money data using the official web or mobile app. Making API calls is another way to interact with your Lunch Money data. You can make API calls that **get** your data for you, allowing you to do other things with it, like generating your own charts or simply archiving your data. You can also make API calls that **change** or **delete** your data. Any changes you make are permanent, just like they would be if you made changes using the web or mobile app.
6
6
 
7
7
  > [!WARNING]
8
- > Changes made via the API are permanent and cannot be undone. Always use a test budget when getting started.
8
+ > Changes made via the API are permanent and cannot be undone. Always use a test budget account when getting started.
9
9
 
10
10
 
11
11
  ## Create a test budget account
12
12
 
13
- Since changes to your data made by the API are permanent, the best way to begin interacting with the API is to <a href="https://support.lunchmoney.app/miscellaneous/unlimited-budgeting-accounts" target="_blank" rel="noopener noreferrer">create a new test budget account</a> in your existing Lunch Money account. As you start using the API, you can interact with this test account and ensure that your real data is not modified.
13
+ Since changes to your data made by the API are permanent, the best way to begin interacting with the API is to <a href="https://support.lunchmoney.app/miscellaneous/unlimited-budgeting-accounts" target="_blank" rel="noopener noreferrer">create a new test budget account</a> in your existing Lunch Money account. As you start using the API, you can interact with this test budget account and ensure that your real data is not modified.
14
14
 
15
15
  :::tabs
16
16
 
@@ -18,46 +18,48 @@ Since changes to your data made by the API are permanent, the best way to begin
18
18
  Click on the green circle in the upper right hand corner in the Lunch Money Web App
19
19
  ![Green Budgets Icon](/images/green_budget.png)
20
20
 
21
- @tab 2. Create a New Budget
22
- Click on the "Create a New Budget Account" link and give your new budget a name like "API Test Budget"
21
+ @tab 2. Create a New Budget Account
22
+ Click on the "Create a New Budget Account" link and give your new budget account a name like "API Test Budget"
23
23
  ![Create New Budget](/images/new_budget.png)
24
24
 
25
25
  @tab 3. Add Test Data
26
- Choose the option to "Set up Demo". This will add categories, accounts, tags and several months worth of transactions to your new budget.
26
+ Choose the option to "Set up Demo". This will add categories, accounts, tags and several months worth of transactions to your new budget account.
27
27
  ![Add Demo Data](/images/DemoData.png)
28
28
  :::
29
29
 
30
- ## Getting an access token
30
+ ## Getting a personal access token
31
31
 
32
- Lunch Money API requests are authenticated using the Bearer Token authentication method. In plain English, this means that you will pass a token associated with your budget account to Lunch Money. This is so we know which account to operate on and that the request came from someone who can access that account.
32
+ Lunch Money API requests are authenticated using the Bearer Token authentication method. In plain English, this means that you will pass a personal access token associated with your budget account to Lunch Money. This is so we know which account to operate on and that the request came from someone who can access that account.
33
+
34
+ This guide uses a personal access token so you can explore the API with your own budget account. If you are building an application that other people will connect to Lunch Money, start with the [OAuth overview](/oauth) instead.
33
35
 
34
36
  :::tabs
35
37
 
36
38
  @tab 1. Go to the Developers Page
37
39
 
38
- Navigate to <a href="https://my.lunchmoney.app/developers" target="_blank" rel="noopener noreferrer">the developers page in the Lunch Money web app</a>. Look at the green circle in the upper right hand corner and make sure that it is showing the test account that you just created. If you aren't sure which account is active, you can click on the green circle to bring up the "Switch Accounts" dialog. This will show you the full names of all the accounts associated with your account. Select the test budget account you created.
40
+ Navigate to <a href="https://my.lunchmoney.app/developers" target="_blank" rel="noopener noreferrer">the developers page in the Lunch Money web app</a>. Look at the green circle in the upper right hand corner and make sure that it is showing the test budget account that you just created. If you aren't sure which budget account is active, you can click on the green circle to bring up the "Switch Accounts" dialog. This will show you the full names of all your budget accounts. Select the test budget account you created.
39
41
  ![Green Budgets Icon](/images/green_budget.png)
40
42
 
41
43
  @tab 2. Request Access Token
42
- In the upper right hand corner of the page, give your access token a label and specify what you are using it for. When you're done, hit the "Request Access Token" button.
44
+ In the upper right hand corner of the page, give your personal access token a label and specify what you are using it for. When you're done, hit the "Request Access Token" button.
43
45
  ![Request Token](/images/RequestAccessToken.png)
44
46
 
45
47
  @tab 3. Save Access Token
46
- After you do this, the web page will show you a long alphanumeric string. This is your access token. Hit the copy button and save this token somewhere safe. It is only shown to you when you create it.
48
+ After you do this, the web page will show you a long alphanumeric string. This is your personal access token. Hit the copy button and save this token somewhere safe. It is only shown to you when you create it.
47
49
  :::
48
50
 
49
- Congratulations! You've got an access token and are ready to make an API request! Later, when you are more comfortable with the API, you can repeat this process to create a new access token associated with your real budget.
51
+ Congratulations! You've got a personal access token and are ready to make an API request! Later, when you are more comfortable with the API, you can repeat this process to create a new personal access token associated with your real budget account.
50
52
 
51
53
  > [!WARNING]
52
- > Treat your access tokens like passwords. Don't share them with anyone you don't trust.
54
+ > Treat your personal access tokens like passwords. Don't share them with anyone you don't trust.
53
55
 
54
- If you ever worry that your access token has been compromised, you can always come back to the Developers page and hit the Revoke button.
56
+ If you ever worry that your personal access token has been compromised, you can always come back to the Developers page and hit the Revoke button.
55
57
 
56
58
  > [!NOTE]
57
- > Give your access tokens descriptive names so you can easily identify and revoke them later if needed.
59
+ > Give your personal access tokens descriptive names so you can easily identify and revoke them later if needed.
58
60
 
59
61
 
60
- ## Using your access token
62
+ ## Using your personal access token
61
63
 
62
64
  When you start to write code that makes API calls, you will need to include this token in each request using an Authorization header. Here is an example curl request:
63
65
 
@@ -68,7 +70,7 @@ curl --location 'https://api.lunchmoney.dev/v2/me' \
68
70
 
69
71
  If this doesn't make sense yet, that's OK!
70
72
 
71
- There is an easier way to use your access token to make requests. Head over to the [API Reference Documentation](/v2/docs).
73
+ There is an easier way to use your personal access token to make requests. Head over to the [API Reference Documentation](/v2/docs).
72
74
 
73
75
  :::tabs
74
76
 
@@ -81,10 +83,12 @@ Scroll down to the section called "Get Current User" and hit the "Test Request"
81
83
  ![TestRequest.png](/images/TestRequest.png)
82
84
  :::
83
85
 
84
- This will pop up a dialog box that you can use to send an API request. This is a simple endpoint, so just hit the "Send" button. You should see a response that includes your name and the name of your test budget.
86
+ This will pop up a dialog box that you can use to send an API request. This is a simple endpoint, so just hit the "Send" button. You should see a response that includes your name and the name of your test budget account.
85
87
 
86
88
  From here, you can explore the rest of the Lunch Money API and start thinking about what you might do with it.
87
89
 
90
+ When you are ready to build an application that other Lunch Money users can connect to, [learn how OAuth grants access without asking users to share personal access tokens](/oauth).
91
+
88
92
  ## What can you build?
89
93
 
90
94
  The Lunch Money community has already built a wide variety of tools — automations, importers, dashboards, and more. Check out the <a href="https://lunchmoney.app/developers#community-projects" target="_blank" rel="noopener noreferrer">Developer Tools & Community Projects</a> list for inspiration, or drop by the <a href="https://discord.com/channels/842337014556262411/1134597088504729780" target="_blank" rel="noopener noreferrer">Show and Tell channel</a> on Discord to see what others are sharing.
@@ -10,13 +10,13 @@ The v2 API is available at:
10
10
  https://api.lunchmoney.dev/v2
11
11
  ```
12
12
 
13
- Get your access token from the <a href="https://my.lunchmoney.app/developers" target="_blank" rel="noopener noreferrer">Lunch Money app developers page</a> and include it as a Bearer token in every request:
13
+ To explore the API or work with your own budget account, create a personal access token on the <a href="https://my.lunchmoney.app/developers" target="_blank" rel="noopener noreferrer">Developers page</a>. If you are building an application for other Lunch Money users, [use OAuth](/oauth) instead. Both kinds of access token are sent as a Bearer token with each request:
14
14
 
15
15
  ```http
16
16
  Authorization: Bearer YOUR_ACCESS_TOKEN
17
17
  ```
18
18
 
19
- See the [Getting started guide](/getting-started) for a full walkthrough, including how to create a test budget and make your first request.
19
+ See the [Getting started guide](/getting-started) for a full walkthrough, including how to create a test budget account and make your first request.
20
20
 
21
21
  ## Trying the API without real data
22
22
 
@@ -59,5 +59,6 @@ Join the conversation in the <a href="https://lunchmoney.app/discord" target="_b
59
59
  **Useful links:**
60
60
  - <a href="/v2/docs">Interactive API Reference</a>
61
61
  - [Getting started guide](/getting-started)
62
+ - [OAuth overview](/oauth)
62
63
  - <a href="https://github.com/lunch-money/awesome-lunchmoney" target="_blank" rel="noopener noreferrer">Awesome Lunch Money Projects</a>
63
64
  - [Contact: devsupport@lunchmoney.app](mailto:devsupport@lunchmoney.app)
@@ -0,0 +1,110 @@
1
+ # Implement the authorization-code flow
2
+
3
+ After you [register an OAuth client](/oauth/register-client), your application uses its settings—including its client ID, client secret, if configured, and a registered redirect URI—to implement the authorization flow. As shown in the [OAuth overview](/oauth), your application sends the user to Lunch Money, Lunch Money returns an authorization code to your callback, and your application exchanges that code for tokens. Step 4 explains how to identify the Lunch Money user and budget account associated with the resulting connection.
4
+
5
+ This guide walks through that process for a **Confidential web client**. Your server handles the callback and token exchange and keeps the client secret and refresh tokens secure. If you are building a standalone mobile or desktop application, read the [native application guidance](/oauth/native-apps) instead. Expo developers can also follow the [native Expo sample](/oauth/sample-applications#native-expo-sample).
6
+
7
+ ## Choose an OAuth library
8
+
9
+ OAuth libraries handle much of the protocol work for you, including generating PKCE values, building authorization requests, validating callback state, and exchanging authorization codes. Choose a well-maintained OAuth 2.0 client library for your language and application framework. Look for support for:
10
+
11
+ - the authorization-code flow with Proof Key for Code Exchange (PKCE);
12
+ - OAuth authorization-server metadata discovery;
13
+ - secure `state` generation and validation; and
14
+ - confidential-client authentication with HTTP Basic.
15
+
16
+ For Node.js and TypeScript, [`openid-client`](https://github.com/panva/openid-client) is one maintained option. Despite its name, using it does not mean that Lunch Money supports OpenID Connect identity scopes, ID tokens, or UserInfo.
17
+
18
+ The [confidential Node.js sample application](/oauth/sample-applications#understand-the-confidential-teaching-code) shows this library in a complete Lunch Money flow. Begin with its [`src/oauth` guide](https://github.com/lunch-money/lm-oauth-confidential-node-example/blob/main/src/oauth/README.md), then follow the listed reading order from protocol types and discovery through authorization, callback validation, credential storage, API access, optional refresh, and revocation.
19
+
20
+ Lunch Money requires PKCE with the `S256` challenge method for every authorization-code flow, including confidential clients that also authenticate with a client secret.
21
+
22
+ ## Configure Lunch Money with discovery
23
+
24
+ Your OAuth library needs to know where to send the user for authorization, where to exchange codes for tokens, and which protocol features Lunch Money supports. Instead of configuring each value separately, give the library the Lunch Money issuer:
25
+
26
+ `https://api.lunchmoney.dev`
27
+
28
+ Then have it load the OAuth authorization-server metadata document:
29
+
30
+ `https://api.lunchmoney.dev/.well-known/oauth-authorization-server`
31
+
32
+ The metadata document is a machine-readable description of Lunch Money's authorization, token, and revocation endpoints, supported client-authentication methods, and PKCE method. Using it allows your library to obtain the current OAuth configuration without your application assembling endpoint URLs itself.
33
+
34
+ > [!TIP]
35
+ > The resolved endpoint paths are `/oauth/authorize`, `/oauth/token`, and `/oauth/revoke`. These are useful when debugging, but use metadata discovery when your library supports it.
36
+
37
+ ## 1. Start authorization
38
+
39
+ On your server, generate:
40
+
41
+ - a cryptographically random `state` value bound to the user's session;
42
+ - a PKCE `code_verifier`; and
43
+ - its `S256` `code_challenge`.
44
+
45
+ Redirect the browser to the discovered authorization endpoint with `response_type=code`, your `client_id`, exact `redirect_uri`, `state`, `code_challenge`, and `code_challenge_method=S256`.
46
+
47
+ Lunch Money handles sign-in, budget-account selection, and consent. It may reuse an existing browser login session, and the person may be able to continue with a different Lunch Money user from the authorization screen. A user may have multiple budget accounts, but this authorization is for the one they select. Keep the authorization attempt bound to the authenticated application user who started it; do not assume which Lunch Money user will complete it.
48
+
49
+ > [!NOTE] Scopes come from the client registration
50
+ > You do not need to send `scope`. If your OAuth library includes it, Lunch Money ignores the requested value and uses the client's complete registered scope set. An authorization request cannot narrow or expand that set; changing scopes requires a replacement client.
51
+
52
+ ## 2. Handle the callback
53
+
54
+ The callback receives either `code` and `state`, or an OAuth `error` and the original `state`. Lunch Money also returns `iss`, which identifies the authorization server that produced the response. Before exchanging a code:
55
+
56
+ 1. compare `state` with the one-time value stored in the initiating session;
57
+ 2. reject missing, mismatched, or reused state;
58
+ 3. compare the decoded `iss` value with the `issuer` value from authorization-server metadata using exact string comparison, and reject a missing or mismatched value;
59
+ 4. consume the stored state and PKCE verifier once; and
60
+ 5. remove authorization parameters from the visible browser URL before rendering a page.
61
+
62
+ Treat `access_denied` as a normal user decision. Do not log the callback query string.
63
+
64
+ ## 3. Exchange the code
65
+
66
+ Send a form-encoded request to the discovered token endpoint with `grant_type=authorization_code`, the code, exact `redirect_uri`, and original `code_verifier`. A confidential client authenticates with HTTP Basic using its client ID and secret. Do this server-side.
67
+
68
+ Keep the returned credentials in temporary server-side state while you identify the authorization. Do not make an unidentified credential set available to the application or commit it to the normal connection store yet. If a `refresh_token` is returned, treat it as a high-value credential even during this temporary step.
69
+
70
+ The token endpoint returns a JSON object with these fields:
71
+
72
+ | Field | Type | When present |
73
+ | --- | --- | --- |
74
+ | `access_token` | string | Always. Treat it as an opaque credential. |
75
+ | `token_type` | string | Always. Lunch Money returns `Bearer`. |
76
+ | `expires_in` | integer | Always. Number of seconds until the access token is expected to expire. |
77
+ | `scope` | string | Always. Space-delimited scopes granted to the token. |
78
+ | `refresh_token` | string | When the registered client includes `offline_access`. |
79
+
80
+ Lunch Money does not return an `id_token`. A successful refresh response has the same shape and includes a replacement `refresh_token`; persist the complete replacement set together.
81
+
82
+ ## 4. Identify and store the connection
83
+
84
+ Send the access token in the header:
85
+
86
+ ```http
87
+ GET /v2/me HTTP/1.1
88
+ Host: api.lunchmoney.dev
89
+ Authorization: Bearer YOUR_ACCESS_TOKEN
90
+ ```
91
+
92
+ The operation must be allowed by the token's scope and by the user's access to the selected budget account. See [OAuth scopes](/oauth/scopes).
93
+
94
+ Call `GET /v2/me` immediately after the token exchange, before normal credential persistence. It requires `me:read`. Strictly validate the response: `id` identifies the Lunch Money user, `account_id` identifies the selected budget account, and `budget_name` is its display name. When reconnecting, use this response to establish the account identity rather than assuming the user selected the same budget account. See [users, budget accounts, and access tokens](/oauth/concepts#users-budget-accounts-and-access-tokens).
95
+
96
+ Store the credential set under the authenticated application user's ID and the returned Lunch Money user `id` and `account_id`. Those three fields establish the owner and provider identity for the connection, even if your database also assigns an internal connection ID for routing. Atomically create a complete record when that combination is new, or replace the complete credential set when the same application user reauthorizes the same Lunch Money user and budget-account pair. Persist the access token, refresh token when present, scope, expiration metadata, Lunch Money user `id`, `account_id`, and `budget_name` together, then make that Lunch Money identity and connection active. Do not use `budget_name` as a key or try to derive identity by parsing the opaque access token.
97
+
98
+ If `GET /v2/me` fails validation or the complete record cannot be persisted, discard the temporary credentials and leave existing connections unchanged. A successful token exchange alone must not create an unidentified or partially saved connection.
99
+
100
+ Your application establishes ownership through its own authenticated session. When authorization starts, store the authenticated application user's internal ID in the server-side authorization attempt associated with `state`. After validating the callback and calling `GET /v2/me`, store the credentials with that application-user ID and the returned Lunch Money `id`/`account_id` pair.
101
+
102
+ For later requests, look up the selected connection using both its internal connection ID and the authenticated application user's internal ID. Then verify that its stored Lunch Money user `id` and `account_id` belong to the active identity before loading or decrypting credentials. Values supplied by the browser select only a candidate record; they do not establish ownership or provider identity.
103
+
104
+ When reauthorization completes, replace credentials only in the record matching the authenticated application user and the returned Lunch Money `id`/`account_id` pair. A different Lunch Money user authorizing the same shared `account_id` must create or update a separate owned connection. After an identity change, hide the previous Lunch Money user's connections and clear or partition displayed profile, configuration, and cached financial data. See [Associate credentials with your application user](/oauth/security#associate-credentials-with-your-application-user) for the storage and ownership rules.
105
+
106
+ ## 5. Continue or recover
107
+
108
+ Use the response's `expires_in` value to track the expected lifetime of the access token. You can refresh slightly before expiry as a proactive optimization, or wait for an API request to return `401` and refresh then. In either case, coordinate refreshes for each connection, retry the failed request at most once after a successful refresh, and reauthorize after a terminal refresh failure. Read [token lifecycle and recovery](/oauth/tokens) before implementing refresh, logout, or revocation.
109
+
110
+ Next: [Develop and test your OAuth application](/oauth/development).
@@ -0,0 +1,116 @@
1
+ # OAuth concepts
2
+
3
+ OAuth connects your application to one Lunch Money user and one budget account at a time. This page explains who participates in that connection, what each credential does, and how to keep multiple connections separate. You do not need to know OAuth terminology before you start.
4
+
5
+ ## Parties in the flow
6
+
7
+ - **User:** the Lunch Money user who authorizes a third-party application to make API requests on their behalf for one budget account. OAuth calls this person the *resource owner*.
8
+ - **Application:** the complete product or service the developer builds. It may contain many features unrelated to Lunch Money; only its Lunch Money OAuth client is managed in the Developer Portal.
9
+ - **Client:** the application's OAuth registration with Lunch Money: its `client_id`, type, redirect URIs, selected scopes, and—when applicable—client secret. The developer must register this client in the Developer Portal before the application can ask Lunch Money users to authorize API access.
10
+ - **Authorization server:** the Lunch Money-owned service that presents the Lunch Money sign-in and consent screens, records the user's decision, and issues authorization codes and tokens. It also handles token requests, including exchanging authorization codes and refresh tokens for new tokens. The user signs in directly with Lunch Money; the third-party application never receives or sees their Lunch Money login credentials.
11
+ - **Lunch Money API service:** the Lunch Money-owned V2 API, which receives the application's API requests, accepts access tokens, and checks that each request is permitted by the client's scopes.
12
+
13
+ ## Users, budget accounts, and access tokens
14
+
15
+ A **Lunch Money user** is the person who signs in to Lunch Money. One user can have access to multiple **budget accounts**, such as personal, household, and test budget accounts. Each budget account contains its own financial data. Here, “budget account” means that collection of data, not an individual bank or manually managed account within it.
16
+
17
+ During OAuth authorization, the user selects one budget account and approves your client's scopes. The resulting **OAuth access token is specific to that Lunch Money user and budget account pair**, for your client. It does not grant access to every budget account the user can access.
18
+
19
+ For example, Alex can access both “Household” and “API testing.” If Alex selects “API testing” during authorization, your application's access token operates on that budget account. It cannot use the same token to read or update “Household.” Scopes determine which operations the application may perform within the selected account; they do not add access to other budget accounts.
20
+
21
+ When applications store OAuth credentials, they should associate the authorization with the application user who started the flow and identify the connection by the Lunch Money user and budget account pair. Two Lunch Money users can both authorize access to the same shared budget account, so `account_id` alone is not a complete connection identity.
22
+
23
+ ### Identify the completed connection
24
+
25
+ Your application cannot know which Lunch Money user or budget account completed authorization from browser state alone. Lunch Money may reuse an existing browser login session, and the authorization screen may let the person continue with a different Lunch Money user. A fresh or ephemeral browser request is also only an intent, not proof of identity.
26
+
27
+ After every successful code exchange, call `GET /v2/me` with the access token and confirm that the response contains the expected identifiers. It requires the `me:read` scope and identifies both sides of the completed connection:
28
+
29
+ | Field | Meaning |
30
+ | --- | --- |
31
+ | `id` | The Lunch Money user's ID |
32
+ | `account_id` | The selected budget account's ID |
33
+ | `budget_name` | The selected budget account's display name |
34
+
35
+ Use `account_id` as the stable identifier for the budget account and `id` as the identifier for the Lunch Money user who completed authorization. Together they identify one Lunch Money connection. If your application has its own user accounts, save each connection only for the person currently signed in to your application. A standalone native application without its own account system can store multiple connections locally, using each `id` and `account_id` pair as a connection identity. This lets one Lunch Money user connect multiple budget accounts and switch between them in the application. If the returned `id` changes, treat it as a user-identity change, not merely another budget account, and isolate the previous user's connections and cached data before showing the new identity.
36
+
37
+ Show `budget_name` so the application user can see which budget account they are using, but do not use it as a key: two budget accounts can have the same name. When names collide, add a secondary label such as the account ID only where the UI needs to distinguish them. Do not infer this identity from browser cookies, callback fields, `budget_name`, the user who was active before authorization, or token contents. Access tokens are opaque credentials; obtain the connection identity from the API response rather than parsing the token.
38
+
39
+ ### Select or authorize a budget account
40
+
41
+ Applications that may retain more than one Lunch Money identity should show the active Lunch Money user and which saved connection is the **active budget account**. Provide two distinct actions:
42
+
43
+ - Select another saved connection belonging to the active Lunch Money user to switch the active budget account without starting OAuth again.
44
+ - Choose **Authorize another budget account** to start OAuth and connect a budget account that is not yet saved.
45
+
46
+ To connect to another Lunch Money user or budget account, send the user through authorization again and let Lunch Money handle login, user switching, and budget-account selection. Changing a locally stored account ID does not change which account an existing token can access. Refreshing a token renews access for its existing authorization; it does not select another budget account.
47
+
48
+ Applications do not need a separate client-side “use another user” action. The ordinary **Authorize another budget account** action starts authorization, but the application cannot know which Lunch Money user and budget account the person will select. Identify the result only after the code exchange by calling `GET /v2/me`.
49
+
50
+ ### Reconcile the authorization result
51
+
52
+ After authorization, use the `id` and `account_id` returned by `GET /v2/me` to reconcile the result. If your application has its own user accounts, compare the result only with connections owned by the person currently signed in to your application.
53
+
54
+ First, compare `id` with the active Lunch Money user:
55
+
56
+ - If there is no active Lunch Money user, make the returned user active.
57
+ - If `id` identifies a different Lunch Money user, switch to that user before handling the budget account. Clear or isolate the previous user's displayed information and cached data, and hide that user's saved connections so none of it is available to the newly active user.
58
+ - If `id` matches the active Lunch Money user, continue using that user's saved connections.
59
+
60
+ Then compare `account_id` with the active Lunch Money user's saved connections:
61
+
62
+ - If it matches an existing connection, treat the result as a reconnection. Save the new access and refresh tokens together, so the connection never contains part of the old token set and part of the new one. Finish saving the complete replacement before using it.
63
+ - If it does not match an existing connection, create a connection for the newly authorized budget account.
64
+
65
+ After reconciling the result, make the returned budget account active. It may be the user's first connection, another budget account they added, or an existing budget account they reauthorized. Authorizing another budget account does not revoke the user's other connections.
66
+
67
+ When the user chooses to switch between existing authorized budget accounts, select a saved connection only from the active Lunch Money identity and use that connection's stored access token to make API calls. All account-specific actions must use the active connection. Clear or partition cached financial data when the active connection changes. If the active connection is removed, select a documented remaining connection for the same Lunch Money user or enter a disconnected state. Never fall back to another user's hidden connection.
68
+
69
+ ### Optional: revoke a known connection before replacing it
70
+
71
+ Most applications do not need to revoke a connection before authorizing again. Use a separate revoke-first workflow only when the user deliberately chooses a known saved connection and wants to invalidate every copy of that authorization before replacing it. Send that connection's current refresh token to `POST /oauth/revoke`, then start authorization. See [Revoke access](/oauth/tokens#revoke-access).
72
+
73
+ Revocation disconnects the selected connection immediately, including when the user abandons authorization or selects a different Lunch Money user or budget account. When authorization completes, reconcile the result using the rules above instead of assuming it replaces the connection that initiated the flow. Never wait until after the exchange to revoke the previous refresh token, because that would also revoke the newly issued credentials when Lunch Money reused the same underlying authorization.
74
+
75
+ ### Retain connections for an inactive Lunch Money user
76
+
77
+ An application may retain credentials for a Lunch Money user who is not currently active when its application-user ownership model permits it. Switching identities should hide the previous user's connections and clear or partition that user's displayed data, but hiding a connection does not revoke it or delete its stored credentials. If that Lunch Money user authorizes again later, the application may make the user's retained connections visible again.
78
+
79
+ An application may instead delete stored credentials as part of its own sign-out or user-switch policy. Make that policy understandable in the interface. In particular, distinguish disconnecting or remotely revoking a budget-account authorization from forgetting its local credential without revoking remote access.
80
+
81
+ ## Client types and credentials
82
+
83
+ When a developer registers a client, they must choose a client type based on whether the application code can keep a client secret secure. This choice cannot be changed after the client is registered. The developer selects either **Confidential web client** or **Native or public client**.
84
+
85
+ - **Confidential web client:** Runs on a server and can securely store client secrets. In OAuth terminology, this is called a *confidential client*. Choose this type for a web application—or any other application architecture with a centralized server. Its client secret helps Lunch Money verify that a token request came from the application that registered the client. The client secret and refresh tokens must stay on the server. The application must protect access tokens wherever its architecture uses them, and associate each authorization with the correct application user and authorized Lunch Money budget account.
86
+ - **Native or public client:** Runs on a user-controlled device and uses Proof Key for Code Exchange (PKCE) without a client secret because installed application code cannot keep a secret secure. In OAuth terminology, this is called a *native public client*. Choose this type for a standalone mobile or desktop application. The app stores each user's tokens securely on that user's device—for example, in Keychain on Apple platforms or appropriate Keystore-backed storage on Android—and must avoid exposing them through backups, logs, or application data shared with other apps.
87
+
88
+ These client types are not interchangeable. A confidential server-side sample cannot be moved into a native application by embedding its client secret or server-held credentials; use a native-client flow and platform secure storage instead.
89
+
90
+ Every client receives a client ID that identifies its Lunch Money registration. The client ID is not secret. Both client types use PKCE to bind the authorization response to the application that started the flow.
91
+
92
+ ## Redirect URIs
93
+
94
+ A redirect URI is the callback address where the Lunch Money authorization server returns control to the application after the user approves or denies access. Lunch Money redirects the user's browser to that address with either a temporary authorization code or information about why authorization did not complete.
95
+
96
+ The developer registers each permitted redirect URI in advance. For a confidential web client, the URI sent at the start of authorization must exactly match one of those registered values, including its scheme, host, port, path, and query string. A native client's loopback redirect follows the same rule except that its ephemeral port is ignored. Fragments are not allowed. These checks prevent an authorization result from being redirected to an unexpected destination. The application's callback must also avoid acting as an open redirect to another location.
97
+
98
+ ## Scopes and least privilege
99
+
100
+ Scopes are named permissions that define what an OAuth client may do with Lunch Money data. Resource scopes such as `transactions:read` authorize a particular kind of action—such as reading or updating a resource—and only for the relevant API endpoints. Scopes are independent: for example, `transactions:update` does not also grant `transactions:read`.
101
+
102
+ The [V2 API reference](/v2/docs) identifies the OAuth scope requirements for each operation. An operation may require one scope, several scopes together, or additional scopes when a request enables broader behavior. Review the operation and relevant parameter descriptions, then use the [OAuth scopes](/oauth/scopes) page to select the scopes your application's features require.
103
+
104
+ Scopes are selected when the client is created and cannot be edited later. Every user who authorizes that client sees and grants all—and only—the scopes the developer selected. To change them, create and test a replacement client, complete review when applicable, update your integration, and ask existing users to authorize again. Existing authorizations and tokens do not move to the replacement client.
105
+
106
+ Select `offline_access` when your application needs to continue making API requests on a user's behalf for longer than the brief period covered by the initial access token. It allows the application to receive a refresh token, which it can use to maintain access without repeatedly asking the user to sign in and authorize the application. It does not add permission to read or change any Lunch Money data; the client still needs the relevant resource scopes.
107
+
108
+ ## Codes and tokens
109
+
110
+ - An **authorization code** is the short-lived, single-use value Lunch Money sends to the application's callback after the user approves access. The code cannot make API requests by itself. Every client exchanges it at Lunch Money's token endpoint using its PKCE verifier. A confidential client must also authenticate that request with its client ID and client secret. Lunch Money then returns an access token and, when applicable, a refresh token.
111
+ - An **access token** is a credential the application sends as part of each V2 API request. It is relatively short-lived. The token response includes `expires_in`, the number of seconds for which the access token is expected to remain valid, so the application can record when it will expire and obtain a replacement when needed.
112
+ - A **refresh token** is a longer-lived but non-permanent credential available only to clients that selected `offline_access`. The application sends it to Lunch Money's token endpoint to request a new access token and replacement refresh token without asking the user to authorize again.
113
+
114
+ An application can refresh its access token shortly before it expires or wait until an API response indicates that the access token is no longer valid. If the application does not have a refresh token—or if that token expires, is revoked, or otherwise stops working—it must ask the user to authorize again.
115
+
116
+ Next: [Register an OAuth client](/oauth/register-client).
@@ -0,0 +1,73 @@
1
+ # Develop and test an OAuth application
2
+
3
+ Use this guide after you have [registered a client](/oauth/register-client) and implemented the [authorization flow](/oauth/authorization-code). At this stage, your application should be able to send its owner to Lunch Money, receive an authorization code at its callback, exchange the code for tokens, and make an API request.
4
+
5
+ The goal now is to test that complete workflow—including its failure and recovery paths—before asking Lunch Money to approve the application for other users.
6
+
7
+ > [!NOTE] Development access is owner-only
8
+ > While an OAuth client is in development, only the Lunch Money user who owns it can authorize the application. Your team should have the developer doing most of the initial Lunch Money work create and own the client so they can test the complete flow with their budget accounts. Other team members cannot authorize the application until the client has been reviewed and approved.
9
+
10
+ Owner-only testing is enough to submit a client for review. You do not need beta-tester coverage before submission; test the complete flow and recovery behavior with the owner's Lunch Money user and test budget accounts.
11
+
12
+ ## Set up a safe test environment
13
+
14
+ Use a separate Lunch Money test budget account when exercising create, update, or delete operations. The [Getting Started guide](/getting-started#create-a-test-budget-account) walks through creating one and adding sample data.
15
+
16
+ Keep production client secrets, tokens, and user data out of test fixtures, logs, screenshots, and bug reports.
17
+
18
+ If you want a working reference flow for comparison, [try OAuth with the confidential Node.js sample](/oauth/sample-applications#try-oauth-with-your-lunch-money-account). Its `me:read` walkthrough helps validate client registration, exact local callback configuration, discovery, authorization, `GET /v2/me`, and revocation before you debug application-specific features. Add `offline_access` when registering the sample client only if you also want to exercise its optional refresh branch.
19
+
20
+ ## Local loopback callbacks
21
+
22
+ When you run a confidential web application on your own computer, its callback handler may not have a public HTTPS address yet. A loopback redirect lets the browser return the authorization result directly to the local process you are developing.
23
+
24
+ For a client in development, register an HTTP redirect using a supported loopback host and the port and path where your application listens:
25
+
26
+ - Hostname: `http://localhost:43821/callback`
27
+ - IPv4: `http://127.0.0.1:43821/callback`
28
+ - IPv6: `http://[::1]:43821/callback`
29
+
30
+ You may choose any available port, but the complete redirect URI sent during authorization must match the registered value exactly. `localhost` and `127.0.0.1` are different hosts, so register the form your application sends. Non-loopback HTTP hosts, embedded credentials, and URI fragments are not supported.
31
+
32
+ The user's browser—not Lunch Money's server—connects to the loopback listener. Keep PKCE and `state` protections in place just as you would for a hosted callback. Before requesting review, also register the HTTPS callback used by the deployed application; the loopback URI can remain available for local testing. After approval, you can add or keep a loopback redirect as long as that production HTTPS callback remains. See [after approval](/oauth/review-and-approval#after-approval).
33
+
34
+ > [!NOTE] Test an approved client with your development team
35
+ > After a client is approved, other team members can continue developing and testing the application's Lunch Money functionality. You can add or keep a loopback redirect on the approved client as long as the production HTTPS callback remains. If a loopback redirect is registered, each developer who can run the application locally may authorize access to their own Lunch Money budget account. The client ID is public, but developers working on a confidential web client also need access to its client secret through your team's secure credential-management process. Do not share access tokens or refresh tokens between developers.
36
+
37
+ ## Repeat or reset authorization
38
+
39
+ During development, the client owner can run authorization again whenever they want to repeat the sign-in, budget-account selection, consent, and callback flow. They do not need to revoke the current token first. The authorization server may reuse the current browser login or let the person continue with a different Lunch Money user. Treat the validated `/v2/me` user/account pair—not previous browser state—as the completed authorization's identity.
40
+
41
+ To test how the application responds after access is revoked, revoke the current token and make another API request with it. Lunch Money should reject the request, and the application should discard its saved credentials and begin authorization again. The [token revocation guide](/oauth/tokens#revoke-access) includes copyable `curl` requests for development testing.
42
+
43
+ ## Test the complete client
44
+
45
+ Test with one Lunch Money user authorizing multiple budget accounts, then with a second Lunch Money user authorizing through the same browser or device. For a client registered with `me:read`, strictly validate the returned `id` and `account_id` after every exchange. Store credentials under the authenticated application user and that Lunch Money user/account pair. For a standalone app without its own accounts, use the Lunch Money user/account pair as the local connection identity.
46
+
47
+ 1. Complete authorization. For each attempt, your application should create a random `state` value, save it with the application user's session, and verify that Lunch Money returns the same value to the callback. Rejecting a missing, changed, or reused value prevents a callback started elsewhere from being attached to the wrong session.
48
+ 2. Keep the PKCE verifier with that same authorization attempt and use it when exchanging the returned code. Confirm that an exchange with a missing or changed verifier fails; this prevents someone who intercepts the code from using it.
49
+ 3. Read the token response's `scope` field and confirm that it contains the complete scope set registered for the client. Do not inspect or parse the access token itself.
50
+ 4. Call every scope-dependent feature, including insufficient-scope cases.
51
+ 5. Test user denial, missing or changed `state`, callback errors, and interrupted authorization attempts. Confirm that denial or a failed `/v2/me` validation leaves all existing connections unchanged.
52
+ 6. Make `GET /v2/me` fail or return an invalid shape after a successful exchange. Confirm that the application saves no unidentified or partial connection.
53
+ 7. Reauthorize an already connected Lunch Money user/account pair. Confirm that the application atomically replaces that connection's credentials, makes it active, and does not create a duplicate.
54
+ 8. Authorize as a second Lunch Money user through the same browser or device. Confirm that the previously displayed profile, configuration, saved-connection choices, and cached financial data disappear, while any retained credentials remain hidden.
55
+ 9. Return to the first Lunch Money user. Confirm that only that user's retained connections become visible again and that the other user's connections cannot be selected.
56
+ 10. Have two different Lunch Money users independently authorize the same shared `account_id`. Confirm that the application stores separate credentials keyed by Lunch Money user `id` and `account_id`, without overwriting either connection.
57
+ 11. Give two visible budget accounts the same `budget_name`. Confirm that they remain separate by `account_id` and that the UI disambiguates them only where needed without using the name as a key.
58
+ 12. Attempt to select a connection belonging to an inactive Lunch Money user. Confirm that the request is rejected before credentials or cached data are loaded.
59
+ 13. Switch the active budget account and exercise API calls, refresh, revocation, and local deletion. Confirm that each action and every cache entry affects only the active user/account connection.
60
+ 14. Remove the active Lunch Money user's final budget account. Confirm that the application enters a disconnected state rather than exposing or selecting another user's hidden connection.
61
+ 15. Test remote disconnect and local-only credential deletion separately. Confirm local-only deletion, explain that the remote authorization remains active, and keep unrelated connections unchanged.
62
+
63
+ ## Test reauthorization before refresh
64
+
65
+ First make sure your application can recover without a refresh token. Record when the token response arrives and use `expires_in` to determine when the access token is expected to expire. After it expires, make a harmless API request and confirm that Lunch Money returns an authentication failure identifying the token as invalid.
66
+
67
+ Your application should discard the unusable token for that connection and start authorization again. Confirm that the same application user can complete the flow, select the intended budget account, and continue using that connection with the replacement token. This recovery path is required when a client does not have `offline_access`, and it remains the fallback when a refresh token expires, is revoked, or otherwise stops working. Keep other connections usable while this one recovers.
68
+
69
+ After reauthorization works, clients registered with `offline_access` can add and test refresh-token handling. Verify replacement-token storage, concurrent-refresh protection, revocation, and terminal refresh recovery before relying on unattended operation. See [token lifecycle and recovery](/oauth/tokens).
70
+
71
+ Finally, complete the [review checklist](/oauth/review-and-approval). A confidential web client needs a production HTTPS redirect before review. A native client may be submitted with an eligible native redirect and an accessible pre-release build.
72
+
73
+ Next: [Operate securely](/oauth/security).
@@ -0,0 +1,33 @@
1
+ # OAuth for Lunch Money integrations
2
+
3
+ OAuth lets your application access Lunch Money on behalf of a user without asking that person to copy and share a personal access token. **If you are building an application for anyone other than yourself, use OAuth.** It gives each user a familiar Lunch Money sign-in and consent experience while keeping their personal access token out of your application.
4
+
5
+ > [!NOTE] OAuth requires the v2 API
6
+ > OAuth access tokens are not supported by v1 endpoints. If an existing application uses v1, [migrate it to v2](/v2/migration-guide) before adding OAuth.
7
+
8
+ ![OAuth authorization-code flow across your application, the Lunch Money user, and Lunch Money. The application redirects the user's browser, Lunch Money handles sign-in and presents the budget-account and scope choices, the user approves access, Lunch Money returns an authorization code, and the application exchanges it for tokens, calls GET /v2/me, saves the identified connection, and calls the API for that account.](/v2/images/oauth-authorization-flow.svg)
9
+
10
+ ## Choose OAuth or a personal access token
11
+
12
+ | Question | Personal access token | OAuth client |
13
+ | --- | --- | --- |
14
+ | Whose data can it access? | One budget account belonging to the user who created the token | One budget account selected by each user who authorizes the application |
15
+ | Best fit | API exploration, one-off scripts, and short-lived work on your own account | Persistent integrations and applications intended for other users |
16
+ | How is access granted? | The user creates a token on the [Developers page](https://my.lunchmoney.app/developers) in the Lunch Money web app | The user signs in and approves the scopes requested by the application |
17
+ | Credential handling | The user keeps the token and sends it with their own API requests; third-party applications should use OAuth instead of asking users to share one | The authorization flow delivers application-specific tokens without asking for a personal token |
18
+ | Scopes | Full access to the Lunch Money APIs available for that budget account, with no fine-grained scope controls | Only the scopes selected when the OAuth client was registered |
19
+ | Lifecycle | Created and revoked by the user | Short-lived access tokens and optional finite refresh access; the user can revoke the application's access |
20
+
21
+ Personal access tokens remain useful for quick work on your own account. OAuth is the recommended model for applications and integrations that other Lunch Money users will connect to, as well as automations that need to run over time.
22
+
23
+ ## What limits access
24
+
25
+ An OAuth client is an application's registration with Lunch Money. When a developer registers the client, they select the scopes—the specific permissions—the application needs. Later, when a Lunch Money user authorizes the application, Lunch Money shows them that complete scope set before they approve access. Choose only the scopes the application's features require. A focused scope request is easier for users to understand and trust, and reduces the risk of the application reading or changing data it does not need.
26
+
27
+ One Lunch Money user can have multiple budget accounts and multiple connections to the same OAuth client. Each OAuth access token represents that user and the single budget account they selected during authorization. It does not provide access to all of their budget accounts. Read [users, budget accounts, and access tokens](/oauth/concepts#users-budget-accounts-and-access-tokens) for an example and how to identify the connected account.
28
+
29
+ ## Before you start
30
+
31
+ You need an active Lunch Money account to [register and manage an OAuth client](/oauth/applications) for your application. During development, only the client owner can authorize the application. Approval is required before other Lunch Money users can authorize it.
32
+
33
+ Start with [OAuth concepts](/oauth/concepts), [register a client](/oauth/register-client), and then [implement the authorization-code flow](/oauth/authorization-code). You can also run the [confidential Node.js sample application](/oauth/sample-applications) to see registration, authorization, an API call, revocation, and local reset working together. Use [OAuth scopes](/oauth/scopes) to plan the client's scopes and [troubleshooting](/oauth/troubleshooting) when a flow fails.