Programmatic Access
Authenticate your backend services with Cubewire using OAuth 2.0 client credentials.
Overview
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.
Create API Credentials
- Sign in to wallet.cubewire.com
- Go to Settings → API Access
- Click Generate Credential
- Enter a descriptive name (e.g., "Production Backend", "Staging Server")
- Select the roles to assign (determines what the credential can access)
- Optionally configure:
- Expiration date — Credential automatically stops working after this date
- IP whitelist — Restrict access to specific IP addresses (max 10 entries)
- Click Create
- Copy the Client ID and Client Secret
Save Your Secret
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.
Get an Access Token
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.io/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"
Response
{
"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) |
Make Authenticated Requests
Include the access token in the Authorization header for all API requests:
Authorization: Bearer {access_token}
curl -X GET https://api.cubewire.io/api/v1/vaults \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json"
Token Management
Token Expiration
Access tokens expire after 1 hour (3600 seconds). When a token expires, API requests return 401 Unauthorized.
Handling Expiration
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.io/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.io${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');
Credential Permissions
API credentials inherit permissions from their assigned roles. The credential can only access what its roles allow.
Assigning Roles
When creating a credential:
- Select one or more roles
- The credential inherits all permissions from those roles
- Use the principle of least privilege—assign only what's needed
Common Configurations
| Use Case | Recommended Roles |
|---|---|
| Read-only monitoring | Viewer |
| Transaction submission | Member |
| Full automation | Admin |
| Custom workflows | Custom role with specific permissions |
Least Privilege
Create separate credentials for different purposes. A credential that only needs to read vault balances shouldn't have permission to send transactions.
IP Whitelisting
Restrict API access to specific IP addresses for enhanced security.
Supported Formats
| Format | Example | Description |
|---|---|---|
| IPv4 | 203.0.113.50 | Single IP address |
| CIDR | 203.0.113.0/24 | IP range (256 addresses) |
Configuration
- Maximum 10 IP entries per credential
- Empty whitelist = no IP restriction (any IP allowed)
- Requests from non-whitelisted IPs are rejected with
403 Forbidden
You can update IP whitelists via the console or API. See API Credentials for management options.
Security Best Practices
Store Secrets Securely
# .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;
Recommendations
| 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 |
Error Handling
Authentication Errors
| 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 |
Example Error Response
{
"error": "invalid_client",
"error_description": "Client authentication failed"
}
Retry Strategy
For 401 Unauthorized on API requests:
- Request a new access token
- Retry the original request with the new token
- If still failing, check if the credential was revoked in the console
Rate Limits
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;
}
}
}
Next
- Console Access — Dashboard authentication for users
Programmatic Access
Authenticate your backend services with Cubewire using OAuth 2.0 client credentials.
Overview
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.
Create API Credentials
- Sign in to wallet.cubewire.com
- Go to Settings → API Access
- Click Generate Credential
- Enter a descriptive name (e.g., "Production Backend", "Staging Server")
- Select the roles to assign (determines what the credential can access)
- Optionally configure:
- Expiration date — Credential automatically stops working after this date
- IP whitelist — Restrict access to specific IP addresses (max 10 entries)
- Click Create
- Copy the Client ID and Client Secret
Save Your Secret
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.
Get an Access Token
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.io/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"
Response
{
"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) |
Make Authenticated Requests
Include the access token in the Authorization header for all API requests:
Authorization: Bearer {access_token}
curl -X GET https://api.cubewire.io/api/v1/vaults \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json"
Token Management
Token Expiration
Access tokens expire after 1 hour (3600 seconds). When a token expires, API requests return 401 Unauthorized.
Handling Expiration
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.io/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.io${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');
Credential Permissions
API credentials inherit permissions from their assigned roles. The credential can only access what its roles allow.
Assigning Roles
When creating a credential:
- Select one or more roles
- The credential inherits all permissions from those roles
- Use the principle of least privilege—assign only what's needed
Common Configurations
| Use Case | Recommended Roles |
|---|---|
| Read-only monitoring | Viewer |
| Transaction submission | Member |
| Full automation | Admin |
| Custom workflows | Custom role with specific permissions |
Least Privilege
Create separate credentials for different purposes. A credential that only needs to read vault balances shouldn't have permission to send transactions.
IP Whitelisting
Restrict API access to specific IP addresses for enhanced security.
Supported Formats
| Format | Example | Description |
|---|---|---|
| IPv4 | 203.0.113.50 | Single IP address |
| CIDR | 203.0.113.0/24 | IP range (256 addresses) |
Configuration
- Maximum 10 IP entries per credential
- Empty whitelist = no IP restriction (any IP allowed)
- Requests from non-whitelisted IPs are rejected with
403 Forbidden
You can update IP whitelists via the console or API. See API Credentials for management options.
Security Best Practices
Store Secrets Securely
# .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;
Recommendations
| 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 |
Error Handling
Authentication Errors
| 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 |
Example Error Response
{
"error": "invalid_client",
"error_description": "Client authentication failed"
}
Retry Strategy
For 401 Unauthorized on API requests:
- Request a new access token
- Retry the original request with the new token
- If still failing, check if the credential was revoked in the console
Rate Limits
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;
}
}
}
Next
- Console Access — Dashboard authentication for users