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:

FieldValue
MethodPOST
URLhttps://api.bitwave.io/v2/oauth/token (production). Substitute the appropriate base URL for sandbox or staging environments.
Content-Typeapplication/x-www-form-urlencoded
Query stringgrant_type=client_credentials
Request BodyForm-encoded client_id and client_secret values.
ReturnsA 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.

  1. Prepare your credentials.
  2. Add grant_type=client_credentials to the query string.
  3. Construct the form-encoded request body.
  4. Send a POST request.
  5. Parse the response.
  6. Include the token in subsequent requests.

Request Parameters

ParameterLocationRequiredDescription
grant_typeQuery stringYesOAuth grant type. For API-key authentication, use client_credentials.
client_idForm bodyYesYour Bitwave API key.
client_secretForm bodyYesYour 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_credentials in the URL query string.
  • Construct the request body: Create a form-encoded body containing client_id and client_secret.
  • Send a POST request: Submit the request to /v2/oauth/token. Include the Content-Type: application/x-www-form-urlencoded header.
  • Parse the response: If successful, the API returns an object with an access_token. Extract this value for use in subsequent calls. The expires_in property is fixed at 3600 seconds.
  • Include the token: Set the HTTP header Authorization: Bearer ACCESS_TOKEN on every API request you make. When the token expires, call /v2/oauth/token again with your client_id and client_secret to obtain a fresh token.

Response Fields

The token endpoint returns a JSON object with these fields:

FieldDescription
access_tokenThe bearer token you must include in the Authorization header for subsequent requests.
token_typeBearer, indicating that the token is used in a Bearer authorization header.
expires_in3600, 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
  • __session cookie 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:

FieldLocationRequiredDescription
grant_typeQuery stringYesMust be urn:ietf:params:oauth:grant-type:token-exchange.
requested_token_typeQuery stringYesTarget token form. See the table below for accepted values.
Authorization headerHeaderYesBearer <jwt>. The existing Bitwave JWT to exchange.
requested_token_typeEffect
urn:ietf:params:oauth:token-type:jwtValidates 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:cookieValidates 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" \
  -i

The -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:

ScenarioResponse
Missing or unsupported grant_type400 with JSON body { "code": "invalid_request", "message": "Invalid token request" }. Add grant_type=client_credentials to the query string.
Missing client_id or client_secret400 with a plain-text body of Missing client_id or Missing client_secret. Include both values in the form-encoded request body.
Invalid credentials403 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 callsRequest 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 3600 seconds. 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/token again with your client_id and client_secret.
  • Error logging: Log error responses for debugging and to help identify misconfigurations or network issues.
  • Use v2 for new integrations: Treat /oauth/token as a legacy endpoint and build new integrations against /v2/oauth/token.
  • Watch for rate-limit headers: Responses may include X-RateLimit-Audit: would-block and X-RateLimit-RetryAfter headers 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 hard 429 responses.

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.