Generating an Authentication Token
Learn how to request a v2 OAuth-style access token for the Bitwave API and use it to authenticate subsequent API calls.
Overview
Bitwave's APIs require an OAuth-style access token for every request. New integrations should request tokens from the current v2 endpoint: POST /v2/oauth/token.
This guide walks through the v2 token request process, explains the request and response fields, and provides example requests. The legacy POST /oauth/token endpoint is deprecated and should only be used by existing integrations that have not yet migrated.
Endpoint Details
Use the following endpoint to obtain a new authentication token:
| Field | Value |
|---|---|
| Method | POST |
| URL | https://api.bitwave.io/v2/oauth/token (production). Substitute the appropriate base URL for sandbox or staging environments. |
| Content-Type | application/x-www-form-urlencoded |
| Query string | grant_type=client_credentials |
| Request Body | Form-encoded client_id and client_secret values. |
| Returns | A JSON object containing access_token, token_type, and expires_in. |
Legacy endpoint: POST /oauth/token is deprecated. It accepts grant_type in the form body, but new integrations should use POST /v2/oauth/token with grant_type in the query string.
Request Steps
Follow these steps to obtain a token with the v2 endpoint.
- Prepare your credentials.
- Add
grant_type=client_credentialsto the query string. - Construct the form-encoded request body.
- Send a POST request.
- Parse the response.
- Include the token in subsequent requests.
Request Parameters
| Parameter | Location | Required | Description |
|---|---|---|---|
| grant_type | Query string | Yes | OAuth grant type. For API-key authentication, use client_credentials. |
| client_id | Form body | Yes | Your Bitwave API key. |
| client_secret | Form body | Yes | Your Bitwave API secret. |
The v2 client-credentials flow does not honor a scope parameter. Do not include scope in new integrations.
Request Flow
- Prepare your credentials: Have your Bitwave API key and API secret, also called client ID and client secret, available. Store them securely, such as in environment variables.
- Add the grant type: Set
grant_type=client_credentialsin the URL query string. - Construct the request body: Create a form-encoded body containing
client_idandclient_secret. - Send a POST request: Submit the request to
/v2/oauth/token. Include theContent-Type: application/x-www-form-urlencodedheader. - Parse the response: If successful, the API returns an object with an
access_token. Extract this value for use in subsequent calls. Theexpires_inproperty is fixed at3600seconds. - Include the token: Set the HTTP header
Authorization: Bearer ACCESS_TOKENon every API request you make. When the token expires, call/v2/oauth/tokenagain with yourclient_idandclient_secretto obtain a fresh token.
Response Fields
The token endpoint returns a JSON object with these fields:
| Field | Description |
|---|---|
| access_token | The bearer token you must include in the Authorization header for subsequent requests. |
| token_type | Bearer, indicating that the token is used in a Bearer authorization header. |
| expires_in | 3600, the number of seconds until the token expires. Request a new token after this time. |
The token response does not include a refresh_token. To renew access, call /v2/oauth/token again with your client_id and client_secret.
Example Requests
Use the following examples to request an access token.
Example: cURL
curl -X POST "https://api.bitwave.io/v2/oauth/token?grant_type=client_credentials" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "client_id=YOUR_API_KEY&client_secret=YOUR_API_SECRET"Replace YOUR_API_KEY and YOUR_API_SECRET with your actual credentials.
Example: Node.js
The following Node.js example defines a small OAuth client to fetch a token and an Auth wrapper for convenience:
import axios from 'axios';
interface OauthResponse {
access_token: string;
token_type: 'Bearer';
expires_in: number;
}
class Oauth {
route = '/v2/oauth';
clientId: string;
clientSecret: string;
baseUrl: string;
constructor(baseUrl: string, clientId: string, clientSecret: string) {
this.baseUrl = baseUrl;
this.clientId = clientId;
this.clientSecret = clientSecret;
}
public async getToken(): Promise<OauthResponse> {
const url = `${this.baseUrl}${this.route}/token?grant_type=client_credentials`;
const body = new URLSearchParams({
client_id: this.clientId,
client_secret: this.clientSecret,
});
const resp = await axios.post<OauthResponse>(url, body);
return resp.data;
}
}
class Auth {
static async getToken(
baseUrl: string,
clientId: string,
clientSecret: string
): Promise<OauthResponse> {
const oauth = new Oauth(baseUrl, clientId, clientSecret);
return await oauth.getToken();
}
}
async function main() {
const apiKey = process.env.BITWAVE_API_KEY!;
const apiSecret = process.env.BITWAVE_API_SECRET!;
const authResp = await Auth.getToken('https://api.bitwave.io', apiKey, apiSecret);
console.log('Access token:', authResp.access_token);
}
main().catch((err) => console.error(err));After retrieving the token, include it in the Authorization header as Bearer ACCESS_TOKEN when calling Bitwave APIs.
Optional: Token Exchange Grant
The /v2/oauth/token endpoint also supports an OAuth 2.0 token-exchange grant for converting an existing Bitwave-issued JWT into another token form. This flow is primarily intended for browser and single-page-app integrations that already hold a valid Bitwave JWT and need to install it as a session cookie. For server-to-server access with an API key and secret, use the client-credentials flow above instead.
This endpoint requires authentication. Unlike client credentials, the token-exchange grant requires the caller to supply a valid existing Bitwave JWT on the request, in either of these forms:
Authorization: Bearer <jwt>header (preferred), or__sessioncookie carrying the JWT.
Requests with an invalid or expired token are rejected with 403.
Request
Pass the grant type and target token type as query-string parameters, and supply the source JWT in the Authorization header:
| Field | Location | Required | Description |
|---|---|---|---|
| grant_type | Query string | Yes | Must be urn:ietf:params:oauth:grant-type:token-exchange. |
| requested_token_type | Query string | Yes | Target token form. See the table below for accepted values. |
| Authorization header | Header | Yes | Bearer <jwt>. The existing Bitwave JWT to exchange. |
| requested_token_type | Effect |
|---|---|
urn:ietf:params:oauth:token-type:jwt | Validates the supplied JWT and returns it in the standard access_token response field. The token itself is unchanged; this form is useful for confirming a token is still valid and reading its remaining lifetime. |
urn:bitwave:params:oauth:token-type:cookie | Validates the supplied JWT and installs it as a __session cookie on the response. The access_token field in the response body is empty; the actual token is delivered via the Set-Cookie header. Used for browser/SPA flows. |
Response
For requested_token_type=urn:ietf:params:oauth:token-type:jwt:
{
"access_token": "<jwt>",
"token_type": "Bearer",
"expires_in": 3600
}For requested_token_type=urn:bitwave:params:oauth:token-type:cookie, the response body contains:
{
"access_token": "",
"token_type": "Cookie",
"expires_in": 3600
}and the response also includes a Set-Cookie: __session=... header. The cookie is HttpOnly, scoped to the API's parent domain (for example, .bitwave.io), uses SameSite=Lax, and is marked Secure when served over HTTPS. Subsequent same-site browser requests carry the cookie automatically.
Example: exchange a JWT for a session cookie
curl -X POST \
"https://api.bitwave.io/v2/oauth/token?grant_type=urn:ietf:params:oauth:grant-type:token-exchange&requested_token_type=urn:bitwave:params:oauth:token-type:cookie" \
-H "Authorization: Bearer EXISTING_JWT" \
-iThe -i flag prints response headers so you can see the Set-Cookie value the server returns. In a browser, the cookie is installed automatically and no further action is needed.
When to use which flow
grant_type=client_credentials— server-to-server access using a Bitwave API key and secret. This is the right choice for almost every backend integration.grant_type=urn:ietf:params:oauth:grant-type:token-exchange— browser/SPA integrations that already hold a Bitwave JWT (for example, after federated sign-in) and need to convert it to a session cookie or revalidate it.
Error Handling
When a request fails, the API returns a non-200 status code with an error object. Common failure scenarios include:
| Scenario | Response |
|---|---|
Missing or unsupported grant_type | 400 with JSON body { "code": "invalid_request", "message": "Invalid token request" }. Add grant_type=client_credentials to the query string. |
Missing client_id or client_secret | 400 with a plain-text body of Missing client_id or Missing client_secret. Include both values in the form-encoded request body. |
| Invalid credentials | 403 response with an empty body. Covers unknown client_id, disabled credentials, or incorrect client_secret. Verify your API key and secret. |
| Expired token on later API calls | Request a new token from /v2/oauth/token and retry the API call. |
Error responses may be a plain-text body (for missing parameters), an empty body (for 403 security errors), or a JSON object with code and message fields (for invalid requests). Inspect the HTTP status code together with the response body to determine the cause.
Best Practices
- Secure your secrets: Never hard-code your client credentials. Use environment variables or a secrets manager.
- Handle expiration: Tokens expire after
3600seconds. Request a fresh token before the current token expires. - Renew by re-authenticating: The endpoint does not return a refresh token. To renew access, call
/v2/oauth/tokenagain with yourclient_idandclient_secret. - Error logging: Log error responses for debugging and to help identify misconfigurations or network issues.
- Use v2 for new integrations: Treat
/oauth/tokenas a legacy endpoint and build new integrations against/v2/oauth/token. - Watch for rate-limit headers: Responses may include
X-RateLimit-Audit: would-blockandX-RateLimit-RetryAfterheaders when usage exceeds soft thresholds. These are informational today but are intended to become enforced limits — back off when you see them rather than waiting for hard429responses.
Next Steps
With a valid access token, you can authenticate calls to other Bitwave APIs, such as retrieving transactions or importing data. See the respective endpoint guides for details on constructing those requests.
Updated about 10 hours ago
