Authenticate your backend services with Cubewire using OAuth 2.0 client credentials.
For server-to-server integration, Cubewire uses the OAuth 2.0 Client Credentials flow. Your backend exchanges a client ID and secret for a short-lived access token, then uses that token to authenticate API requests.
The Client Secret is only displayed once when generated. Copy it immediately and store it securely. If you lose it, you'll need to generate a new credential.
Exchange your credentials for an access token by calling the token endpoint.
Endpoint: POST /api/v1/oauth2/token
Content-Type: application/x-www-form-urlencoded
| Parameter | Type | Required | Description |
|---|---|---|---|
grant_type | string | Yes | Must be client_credentials |
client_id | string | Yes | Your OAuth2 client ID |
client_secret | string | Yes | Your OAuth2 client secret |
curl -X POST https://api.cubewire.com/api/v1/oauth2/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_CLIENT_SECRET"
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 3600
}
| Field | Description |
|---|---|
access_token | JWT token for API authentication |
token_type | Always Bearer |
expires_in | Token lifetime in seconds (3600 = 1 hour) |
Include the access token in the Authorization header for all API requests:
Authorization: Bearer {access_token}
curl -X GET https://api.cubewire.com/api/v1/vaults \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json"
Access tokens expire after 1 hour (3600 seconds). When a token expires, API requests return 401 Unauthorized.
Implement proactive token refresh in your application. Refresh tokens before they expire (recommended: 5 minutes before expiry):
class CubewireClient {
constructor(clientId, clientSecret) {
this.clientId = clientId;
this.clientSecret = clientSecret;
this.accessToken = null;
this.tokenExpiry = null;
}
async getToken() {
// Refresh if token is missing or expires within 5 minutes
const bufferMs = 5 * 60 * 1000;
if (!this.accessToken || Date.now() >= this.tokenExpiry - bufferMs) {
await this.refreshToken();
}
return this.accessToken;
}
async refreshToken() {
const params = new URLSearchParams({
grant_type: 'client_credentials',
client_id: this.clientId,
client_secret: this.clientSecret
});
const response = await fetch('https://api.cubewire.com/api/v1/oauth2/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: params
});
const data = await response.json();
this.accessToken = data.access_token;
this.tokenExpiry = Date.now() + (data.expires_in * 1000);
}
async request(method, path, body = null) {
const token = await this.getToken();
const options = {
method,
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
};
if (body) {
options.body = JSON.stringify(body);
}
const response = await fetch(`https://api.cubewire.com${path}`, options);
if (response.status === 401) {
// Token might have been revoked, try refreshing once
await this.refreshToken();
return this.request(method, path, body);
}
return response.json();
}
}
// Usage
const client = new CubewireClient(
process.env.CUBEWIRE_CLIENT_ID,
process.env.CUBEWIRE_CLIENT_SECRET
);
const vaults = await client.request('GET', '/api/v1/vaults');
API credentials inherit permissions from their assigned roles. The credential can only access what its roles allow.
When creating a credential:
| Use Case | Recommended Roles |
|---|---|
| Read-only monitoring | Viewer |
| Transaction submission | Member |
| Full automation | Admin |
| Custom workflows | Custom role with specific permissions |
Create separate credentials for different purposes. A credential that only needs to read vault balances shouldn't have permission to send transactions.
Restrict API access to specific IP addresses for enhanced security.
| Format | Example | Description |
|---|---|---|
| IPv4 | 203.0.113.50 | Single IP address |
| CIDR | 203.0.113.0/24 | IP range (256 addresses) |
403 ForbiddenYou can update IP whitelists via the console or API. See API Credentials for management options.
# .env file (never commit to git)
CUBEWIRE_CLIENT_ID=your_client_id
CUBEWIRE_CLIENT_SECRET=your_client_secret
// Access in code
const clientId = process.env.CUBEWIRE_CLIENT_ID;
const clientSecret = process.env.CUBEWIRE_CLIENT_SECRET;
| Practice | Description |
|---|---|
| Never commit secrets | Add .env to .gitignore; use secrets managers in production |
| Use HTTPS only | All Cubewire endpoints use HTTPS; never disable certificate verification |
| Rotate credentials | Generate new credentials periodically; revoke old ones |
| Monitor access logs | Review API usage for unusual patterns |
| Separate environments | Use different credentials for development, staging, and production |
| Status Code | Error | Solution |
|---|---|---|
400 | invalid_grant | Check that grant_type is client_credentials |
401 | invalid_client | Verify client_id and client_secret are correct |
401 | Unauthorized | Access token is invalid or expired; request a new token |
403 | Forbidden | Credential lacks permission, or IP not whitelisted |
{
"error": "invalid_client",
"error_description": "Client authentication failed"
}
For 401 Unauthorized on API requests:
API requests are rate-limited to ensure platform stability.
| Limit Type | Value |
|---|---|
| Token requests | 10 per minute per credential |
| API requests | 100 per second per organization |
When rate-limited, you'll receive a 429 Too Many Requests response. Implement exponential backoff:
async function requestWithRetry(fn, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
if (error.status === 429 && i < maxRetries - 1) {
const delay = Math.pow(2, i) * 1000; // 1s, 2s, 4s
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
throw error;
}
}
}
Authenticate your backend services with Cubewire using OAuth 2.0 client credentials.
For server-to-server integration, Cubewire uses the OAuth 2.0 Client Credentials flow. Your backend exchanges a client ID and secret for a short-lived access token, then uses that token to authenticate API requests.
The Client Secret is only displayed once when generated. Copy it immediately and store it securely. If you lose it, you'll need to generate a new credential.
Exchange your credentials for an access token by calling the token endpoint.
Endpoint: POST /api/v1/oauth2/token
Content-Type: application/x-www-form-urlencoded
| Parameter | Type | Required | Description |
|---|---|---|---|
grant_type | string | Yes | Must be client_credentials |
client_id | string | Yes | Your OAuth2 client ID |
client_secret | string | Yes | Your OAuth2 client secret |
curl -X POST https://api.cubewire.com/api/v1/oauth2/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_CLIENT_SECRET"
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 3600
}
| Field | Description |
|---|---|
access_token | JWT token for API authentication |
token_type | Always Bearer |
expires_in | Token lifetime in seconds (3600 = 1 hour) |
Include the access token in the Authorization header for all API requests:
Authorization: Bearer {access_token}
curl -X GET https://api.cubewire.com/api/v1/vaults \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json"
Access tokens expire after 1 hour (3600 seconds). When a token expires, API requests return 401 Unauthorized.
Implement proactive token refresh in your application. Refresh tokens before they expire (recommended: 5 minutes before expiry):
class CubewireClient {
constructor(clientId, clientSecret) {
this.clientId = clientId;
this.clientSecret = clientSecret;
this.accessToken = null;
this.tokenExpiry = null;
}
async getToken() {
// Refresh if token is missing or expires within 5 minutes
const bufferMs = 5 * 60 * 1000;
if (!this.accessToken || Date.now() >= this.tokenExpiry - bufferMs) {
await this.refreshToken();
}
return this.accessToken;
}
async refreshToken() {
const params = new URLSearchParams({
grant_type: 'client_credentials',
client_id: this.clientId,
client_secret: this.clientSecret
});
const response = await fetch('https://api.cubewire.com/api/v1/oauth2/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: params
});
const data = await response.json();
this.accessToken = data.access_token;
this.tokenExpiry = Date.now() + (data.expires_in * 1000);
}
async request(method, path, body = null) {
const token = await this.getToken();
const options = {
method,
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
};
if (body) {
options.body = JSON.stringify(body);
}
const response = await fetch(`https://api.cubewire.com${path}`, options);
if (response.status === 401) {
// Token might have been revoked, try refreshing once
await this.refreshToken();
return this.request(method, path, body);
}
return response.json();
}
}
// Usage
const client = new CubewireClient(
process.env.CUBEWIRE_CLIENT_ID,
process.env.CUBEWIRE_CLIENT_SECRET
);
const vaults = await client.request('GET', '/api/v1/vaults');
API credentials inherit permissions from their assigned roles. The credential can only access what its roles allow.
When creating a credential:
| Use Case | Recommended Roles |
|---|---|
| Read-only monitoring | Viewer |
| Transaction submission | Member |
| Full automation | Admin |
| Custom workflows | Custom role with specific permissions |
Create separate credentials for different purposes. A credential that only needs to read vault balances shouldn't have permission to send transactions.
Restrict API access to specific IP addresses for enhanced security.
| Format | Example | Description |
|---|---|---|
| IPv4 | 203.0.113.50 | Single IP address |
| CIDR | 203.0.113.0/24 | IP range (256 addresses) |
403 ForbiddenYou can update IP whitelists via the console or API. See API Credentials for management options.
# .env file (never commit to git)
CUBEWIRE_CLIENT_ID=your_client_id
CUBEWIRE_CLIENT_SECRET=your_client_secret
// Access in code
const clientId = process.env.CUBEWIRE_CLIENT_ID;
const clientSecret = process.env.CUBEWIRE_CLIENT_SECRET;
| Practice | Description |
|---|---|
| Never commit secrets | Add .env to .gitignore; use secrets managers in production |
| Use HTTPS only | All Cubewire endpoints use HTTPS; never disable certificate verification |
| Rotate credentials | Generate new credentials periodically; revoke old ones |
| Monitor access logs | Review API usage for unusual patterns |
| Separate environments | Use different credentials for development, staging, and production |
| Status Code | Error | Solution |
|---|---|---|
400 | invalid_grant | Check that grant_type is client_credentials |
401 | invalid_client | Verify client_id and client_secret are correct |
401 | Unauthorized | Access token is invalid or expired; request a new token |
403 | Forbidden | Credential lacks permission, or IP not whitelisted |
{
"error": "invalid_client",
"error_description": "Client authentication failed"
}
For 401 Unauthorized on API requests:
API requests are rate-limited to ensure platform stability.
| Limit Type | Value |
|---|---|
| Token requests | 10 per minute per credential |
| API requests | 100 per second per organization |
When rate-limited, you'll receive a 429 Too Many Requests response. Implement exponential backoff:
async function requestWithRetry(fn, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
if (error.status === 429 && i < maxRetries - 1) {
const delay = Math.pow(2, i) * 1000; // 1s, 2s, 4s
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
throw error;
}
}
}