code. Your backend exchanges the code for a connection ID.
When to use this: whenever a user connects an account in your product, for example from a “Connect GitHub” button in your settings page.
How the flow works
- Your app sends the user to BundleUp’s
/authorizeURL with your client ID, the integration to connect, and where to send them afterwards. - BundleUp checks those values and sends the user to the provider’s consent screen, using your OAuth app.
- The user approves. The provider sends them back to BundleUp, which exchanges the provider’s code for tokens, encrypts them and creates a connection.
- BundleUp redirects the user to your
redirect_uriwith a one-timecode(or anerror). - Your backend exchanges the
codefor the connection ID, using your API key.
Before you start
- An integration for the provider. Note its identifier (for example
github). - Your redirect URI registered in the dashboard under Auth → Redirect URIs. It must match exactly, including scheme, host, port, path and trailing slash.
- Your client ID, from the same Auth page.
- An API key on your backend.
Step 1: Send the user to BundleUp
Build this URL and redirect the user’s browser to it:
The SDKs build the URL for you:
client_id identifies your workspace but isn’t a secret, and BundleUp only redirects to URIs you registered. Each SDK also exposes the builder as a standalone helper, so you can build the URL in the browser or anywhere the API key must not live: buildAuthorizationUrl from @bundleup/sdk, build_authorization_url from bundleup in Python, and BundleUp.build_authorization_url in Ruby. Exchanging the code afterwards still needs the API key, so keep that step on your server.
If the client_id, integration_id or redirect_uri is missing or wrong, BundleUp shows an error page instead of redirecting. Nothing is sent to your redirect URI in that case, because BundleUp can’t trust it.
On the Free plan, users see a BundleUp screen for about a second before they’re sent to the provider. On Pro, they go straight to the provider.
Step 2: The user approves at the provider
The user sees the provider’s consent screen, showing your OAuth app’s name and the scopes configured on the integration. After they approve, the provider sends them back to BundleUp, which completes the token exchange and stores the tokens encrypted. The user has 10 minutes from starting the flow to finish it. After that, they need to start again.Step 3: Handle the redirect
BundleUp redirects the user to yourredirect_uri.
On success:
On failure:
error_description carries a human-readable explanation. Show the user a friendly message and a way to try again.
Step 4: Exchange the code for a connection ID
From your backend, send the code and the sameredirect_uri to BundleUp with your API key:
200 OK
integration_id here is the integration’s identifier. external_id is null if you didn’t send one.
The exchange fails with 400 if the code has expired, was already used, was issued for a different redirect_uri, or belongs to a different workspace than your API key. A missing or invalid API key returns 401. Full reference: Exchange a code.
Store the connection_id against the user. You’re done: use it for every Proxy, Unified API and MCP request for that account.
Protect the flow with state
Withoutstate, an attacker could trick a signed-in user into completing a flow the attacker started, linking the attacker’s account to the victim’s profile in your app. To prevent that:
- Before redirecting, generate a random value and store it in the user’s session.
- Pass it as
state. - In your callback, compare the
statequery parameter with the stored value. Reject the request if they differ.
Reconnecting an account
Each completed flow creates a new connection, even for the same user and provider. When a user reconnects (for example, to grant new scopes or after revoking access), store the new connection ID and delete the old connection withDELETE /v1/connections/{id}, so you don’t keep unused credentials around.
Why the flow works this way
- Your API key never touches the browser. It’s only used in the server-to-server code exchange.
- The connection ID never appears in a URL. A leaked callback URL (browser history, server logs, a
Refererheader) exposes only a code that is short-lived, single-use, bound to your redirect URI, and useless without your API key. - Redirects only go to URIs you registered, so the flow can’t be used to send users somewhere else.
Next steps
- Authenticating API requests: use the connection ID.
- Connection lifecycle: what happens to a connection after it’s created.

