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.



Did this page help you?