> ## Documentation Index
> Fetch the complete documentation index at: https://learn.biq.li/llms.txt
> Use this file to discover all available pages before exploring further.

# Build your own integration

> Let Biqli users connect one workspace to your product with OAuth 2.0.

Build an integration when your product needs to create links, manage workspace
resources, record conversions, or configure webhooks for Biqli users. OAuth
lets each user approve access to one workspace without sharing an API key.

Use a workspace API key for an internal service that manages only your own
workspace. Use OAuth when other Biqli users connect their workspaces to your
product.

## How the connection works

1. You create an OAuth application in Biqli.
2. Your product sends the user to Biqli with the scopes it needs.
3. The user signs in, reviews the permissions, and selects a workspace.
4. Biqli returns a short-lived authorization code to your callback URL.
5. Your product exchanges the code for an access token and refresh token.
6. API requests run only inside the approved workspace and scopes.

## 1. Create an OAuth application

Open **Workspace settings → OAuth Apps** and select **Create OAuth app**.

Add a recognizable name, developer details, an install URL, and every callback
URL your product uses. Callback URLs are exact-match values. Production URLs
must use HTTPS; HTTP is accepted only for localhost and loopback development.

Choose **Allow PKCE** when the client cannot protect a secret, including browser,
mobile, and desktop applications.

Creating the application does not reveal its initial client secret. Open the
saved application's menu and select **Regenerate secret** when you are ready to
store a new one-time value in a server-side secret manager.

<Warning>
  Never place a client secret in browser code, a mobile or desktop bundle, a
  public repository, a URL, or a log.
</Warning>

For dashboard field behavior and application management, read
[Manage OAuth applications](/help/workspace/developer/oauth-apps).

## 2. Choose a client type

| Client | Authentication | Use when |
| :- | :- | :- |
| Confidential | Client ID and client secret | A trusted server performs the token exchange and refresh. |
| Public | Client ID and S256 PKCE | Browser, mobile, or desktop code cannot keep a secret private. |

PKCE uses a fresh high-entropy verifier for each authorization. Send its
base64url-encoded SHA-256 challenge during authorization and the original
verifier during the code exchange.

Read the [authorization request reference](/docs/api-reference/oauth/authorize)
for the exact parameter contract.

## 3. Redirect the user to Biqli

Create an unpredictable `state` value, store it in the user's session, and send
the user to the authorization endpoint:

```text theme={null}
https://biq.li/oauth/authorize?client_id=YOUR_CLIENT_ID&redirect_uri=https%3A%2F%2Fapp.example.com%2Foauth%2Fcallback&response_type=code&state=RANDOM_STATE&scope=workspace.read%20links.view
```

Request only the permissions the integration needs. Biqli displays those
permissions before the user chooses a workspace and approves the connection.

After approval, Biqli returns `code` and the unchanged `state` to the registered
callback URL. Verify `state` before exchanging the code. Authorization codes
expire after five minutes and can be used only once.

If the user declines, the callback receives `error=access_denied`, an
`error_description`, and the original `state`.

## 4. Exchange the authorization code

For a confidential client, exchange the code from your server:

```bash theme={null}
curl --request POST \
  --url https://biq.li/api/v1/oauth/token \
  --header 'Content-Type: application/x-www-form-urlencoded' \
  --data-urlencode 'grant_type=authorization_code' \
  --data-urlencode 'client_id=YOUR_CLIENT_ID' \
  --data-urlencode 'client_secret=YOUR_CLIENT_SECRET' \
  --data-urlencode 'redirect_uri=https://app.example.com/oauth/callback' \
  --data-urlencode 'code=AUTHORIZATION_CODE'
```

Public clients omit `client_secret` and include the original `code_verifier`.
Biqli accepts only the `S256` PKCE method.

A successful response returns a bearer access token, its lifetime, a refresh
token, the refresh-family lifetime, and the granted scope string. Store tokens
server-side when the client architecture allows it.

See [Exchange or refresh a token](/docs/api-reference/oauth/token) for complete
request, response, and error behavior.

## 5. Confirm the connection

Call `GET /api/v1/oauth/me` before saving the installation:

```bash theme={null}
curl https://biq.li/api/v1/oauth/me \
  --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
  --header 'Accept: application/json'
```

The response identifies the user and selected workspace. Store the public
connection ID and workspace ID with your installation record. Do not ask the
user to type an internal workspace identifier.

Read [Retrieve the connected workspace](/docs/api-reference/oauth/connection)
for the response contract.

## 6. Make an API request

Use the access token as a bearer credential. For example, list links in the
connected workspace:

```bash theme={null}
curl --request GET \
  --url 'https://biq.li/api/v1/link?page_size=10' \
  --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
  --header 'Accept: application/json'
```

The token selects the connected workspace and its scopes control the available
operations. Workspace roles, resource policies, plan entitlements, and quotas
still apply after authorization.

Use the endpoint pages under API Reference for request and response contracts.
The [scope reference](/docs/api-reference/oauth/scopes) maps OAuth permissions
to those operations.

## 7. Refresh tokens safely

Access tokens are short-lived. Exchange the current refresh token before the
access token expires and atomically replace both stored tokens with the new
values.

Every successful refresh rotates the refresh token. Reusing an older refresh
token revokes the entire token family, including the newest access token. Do
not retry with a token that may already have succeeded in another worker.

## 8. Revoke disconnected installations

When a user disconnects your integration, send the current access or refresh
token to `POST /api/v1/oauth/revoke`, then remove the local credentials.

Revocation invalidates the connection. Webhook endpoints created by that OAuth
connection are removed automatically. Removing the OAuth application revokes
all of its connections.

See [Revoke a connection](/docs/api-reference/oauth/revoke) for the exact
request contract.

## Add webhooks when you need events

Use webhooks instead of polling when your integration reacts to link, click,
lead, or sale activity. Request only the webhook permissions you need, store
the endpoint signing secret securely, verify the exact raw request body, and
deduplicate events by their envelope ID.

Start with the [webhook introduction](/webhooks/introduction).
Use the [webhook endpoint API](/docs/api-reference/webhooks/manage) to create,
test, inspect, and remove endpoints from your integration.

## Production checklist

* Register every production callback URL exactly.
* Generate and validate a fresh `state` for each authorization.
* Use S256 PKCE for every public client.
* Keep client secrets, access tokens, and refresh tokens out of logs.
* Request the smallest granular scope set.
* Verify `/oauth/me` before binding the installation locally.
* Replace refresh tokens atomically after every successful refresh.
* Reauthorize after `invalid_grant`; never replay a code or rotated token.
* Revoke credentials when the user disconnects.
* Test authorization, denial, refresh, revocation, and insufficient-scope paths.

For protocol failures, read [OAuth errors](/docs/api-reference/oauth/errors).
