Cargoholic APIs use OAuth 2.0 Client Credentials Grant for machine-to-machine (M2M) authentication. Every protected request requires a valid Bearer token and your assigned Tenant ID.
Prerequisites
Before making API calls, obtain your credentials from your Cargoholic representative:
| Parameter | Description |
|---|---|
client_id | Unique public identifier for your API client |
client_secret | Confidential secret key used to authenticate your client |
X-Tenant-ID | Unique identifier for your organization's tenant workspace |
Keep Your Secrets SafeNever expose your
client_secretin frontend applications, client-side code, or public repositories. Store it in a secure environment variable or secrets manager.
Step 1: Obtain an Access Token
Send a POST request to the token endpoint using application/x-www-form-urlencoded in the request body.
Endpoint
POST https://authentication.cargoholic.com/oauth2/token
Content-Type: application/x-www-form-urlencoded
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
grant_type | string | Yes | Must be set to client_credentials |
client_id | string | Yes | Your assigned client ID |
client_secret | string | Yes | Your assigned client secret |
Example Request
curl --request POST \
--url https://authentication.cargoholic.com/oauth2/token \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data grant_type=client_credentials \
--data client_id=YOUR_CLIENT_ID \
--data client_secret=YOUR_CLIENT_SECRET
const params = new URLSearchParams({
grant_type: 'client_credentials',
client_id: process.env.CARGOHOLIC_CLIENT_ID,
client_secret: process.env.CARGOHOLIC_CLIENT_SECRET
});
const response = await fetch('https://authentication.cargoholic.com/oauth2/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: params.toString()
});
const data = await response.json();
console.log(data.access_token);
import os
import requests
url = "https://authentication.cargoholic.com/oauth2/token"
payload = {
"grant_type": "client_credentials",
"client_id": os.getenv("CARGOHOLIC_CLIENT_ID"),
"client_secret": os.getenv("CARGOHOLIC_CLIENT_SECRET")
}
headers = {"Content-Type": "application/x-www-form-urlencoded"}
response = requests.post(url, data=payload, headers=headers)
token_data = response.json()
print(token_data["access_token"])
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"os"
"strings"
)
type TokenResponse struct {
AccessToken string `json:"access_token"`
ExpiresIn int `json:"expires_in"`
TokenType string `json:"token_type"`
}
func main() {
formData := url.Values{}
formData.Set("grant_type", "client_credentials")
formData.Set("client_id", os.Getenv("CARGOHOLIC_CLIENT_ID"))
formData.Set("client_secret", os.Getenv("CARGOHOLIC_CLIENT_SECRET"))
req, _ := http.NewRequest("POST", "https://authentication.cargoholic.com/oauth2/token", strings.NewReader(formData.Encode()))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
var tokenResp TokenResponse
_ = json.Unmarshal(body, &tokenResp)
fmt.Printf("Token: %s (Expires in: %d seconds)\n", tokenResp.AccessToken, tokenResp.ExpiresIn)
}
Response
{
"access_token": "eyJraWQiOiJ...",
"expires_in": 3600,
"token_type": "Bearer"
}
| Field | Type | Description |
|---|---|---|
access_token | string | The JWT bearer token used to authenticate subsequent API calls |
expires_in | integer | Token lifespan in seconds (typically 3600 / 1 hour) |
token_type | string | Type of token issued (Bearer) |
Step 2: Use the Token in Protected API Requests
Include the following two HTTP headers on every request to Cargoholic API endpoints:
Authorization: Bearer <access_token>X-Tenant-ID: <tenant_id>
Example Protected Request
curl --request GET \
--url https://api-sandbox.cargoholic.com/v1/orders \
--header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
--header 'X-Tenant-ID: YOUR_TENANT_ID' \
--header 'Accept: application/json'
const response = await fetch('https://api-sandbox.cargoholic.com/v1/orders', {
method: 'GET',
headers: {
'Authorization': `Bearer ${accessToken}`,
'X-Tenant-ID': process.env.CARGOHOLIC_TENANT_ID,
'Accept': 'application/json'
}
});
const orders = await response.json();
import requests
url = "https://api-sandbox.cargoholic.com/v1/orders"
headers = {
"Authorization": f"Bearer {access_token}",
"X-Tenant-ID": os.getenv("CARGOHOLIC_TENANT_ID"),
"Accept": "application/json"
}
response = requests.get(url, headers=headers)
print(response.json())
Token Lifecycle & Best Practices
- Token Caching: Access tokens are valid for 3,600 seconds (1 hour). Store the token in memory or a cache (e.g., Redis) and reuse it across multiple API requests until it expires. Requesting a new token on every API call degrades performance and may trigger rate limits.
- Proactive Refresh: Request a fresh token shortly before the
expires_inwindow elapses (e.g., at the 55-minute mark). - 401 Unauthorized Handling: If an API endpoint responds with
401 Unauthorized, invalidate your cached token, request a new token from the/oauth2/tokenendpoint, and retry the failed request once. - Tenant Isolation: The
X-Tenant-IDheader routes your request to your tenant's data partition. Omitting this header or passing an invalid ID will result in a400 Bad Request.

