API Credentials
API Credentials allow users to create OAuth2 client credentials for programmatic access to the CargoXplorer API. Each credential provides a client_id and client_secret for use with the OAuth2 client credentials flow.
Overview
- Authentication Method: OAuth2 Client Credentials Grant
- Security: Secrets hashed using PBKDF2-HMAC-SHA256 (100,000 iterations)
- Limit: Maximum 5 credentials per user
- Features: Optional IP restrictions, expiration dates, and soft delete
- Permissions: Credentials inherit the user's roles and permissions
Authorization
- Regular Users: Can create API credentials for themselves only
- Administrators: Can create API credentials for any user in their organization (must be member of "Administrators" group)
GraphQL Schema
Types
ApiCredential
type apiCredential {
apiCredentialId: Int!
organizationId: Int!
userId: String!
name: String!
clientId: String!
expiresAt: DateTime
allowedIpAddresses: [String]
isDeleted: Boolean!
lastUsedAt: DateTime
createdBy: String
created: DateTime
lastModifiedBy: String
lastModified: DateTime
}
ApiCredentialWithSecret
Returned only during creation or secret regeneration. This is the only time the plaintext secret is shown.
type apiCredentialWithSecret {
apiCredentialId: Int!
organizationId: Int!
userId: String!
name: String!
clientId: String!
clientSecret: String! # Only shown once!
expiresAt: DateTime
allowedIpAddresses: [String]
isDeleted: Boolean!
lastUsedAt: DateTime
createdBy: String
created: DateTime
lastModifiedBy: String
lastModified: DateTime
}
Queries
Get Single API Credential
Retrieve a specific API credential by ID. Users can only access their own credentials.
query {
apiCredential(
organizationId: 1
apiCredentialId: 123
) {
apiCredentialId
name
clientId
expiresAt
allowedIpAddresses
lastUsedAt
created
}
}
List API Credentials
Retrieve all API credentials for the current user within an organization.
query {
apiCredentials(
organizationId: 1
search: "production"
orderBy: "created desc"
skip: 0
take: 10
) {
items {
apiCredentialId
name
clientId
expiresAt
lastUsedAt
}
pageInfo {
hasNextPage
hasPreviousPage
}
totalCount
}
}
Parameters:
organizationId(required): Organization IDsearch(optional): Search by name or clientIdfilter(optional): Filter expressionorderBy(optional): Sort order (e.g., "created desc", "name asc")skip(optional): Number of items to skip for paginationtake(optional): Number of items to return (max 100)
Mutations
Create API Credential
Create a new API credential. Returns the credential with the plaintext clientSecret - this is the only time it will be shown.
For yourself:
mutation {
createApiCredential(
organizationId: 1
values: {
name: "Production API Key"
expiresAt: "2026-12-31T23:59:59Z"
allowedIpAddresses: ["192.168.1.0/24", "10.0.0.5"]
}
) {
apiCredentialId
name
clientId
clientSecret # Save this - it won't be shown again!
expiresAt
allowedIpAddresses
}
}
For another user (Administrators only):
mutation {
createApiCredential(
organizationId: 1
values: {
name: "John's API Key"
userId: "user-abc-123" # Only Administrators can specify this
expiresAt: "2026-12-31T23:59:59Z"
}
) {
apiCredentialId
userId
clientId
clientSecret
}
}
Response:
{
"data": {
"createApiCredential": {
"apiCredentialId": 456,
"name": "Production API Key",
"clientId": "api-a1b2c3d4e5f6",
"clientSecret": "dGVzdHNlY3JldDEyMzQ1Njc4OTBhYmNkZWZnaGlqa2xtbm8",
"expiresAt": "2026-12-31T23:59:59Z",
"allowedIpAddresses": ["192.168.1.0/24", "10.0.0.5"]
}
}
}
Important: Store the clientSecret securely. It cannot be retrieved after creation.
Update API Credential
Update an existing API credential's properties. Users can only update their own credentials.
mutation {
updateApiCredential(
organizationId: 1
apiCredentialId: 456
values: {
name: "Production API Key (Updated)"
expiresAt: "2027-12-31T23:59:59Z"
allowedIpAddresses: ["192.168.1.0/24", "10.0.0.0/8"]
}
) {
apiCredentialId
name
expiresAt
allowedIpAddresses
lastModified
}
}
Regenerate Secret
Generate a new secret for an existing credential. Returns the new plaintext secret - this is the only time it will be shown.
mutation {
regenerateApiCredentialSecret(
organizationId: 1
apiCredentialId: 456
) {
apiCredentialId
clientId
clientSecret # New secret - save this!
lastModified
}
}
Delete API Credential
Soft-delete an API credential. Deleted credentials can no longer be used for authentication.
mutation {
deleteApiCredential(
organizationId: 1
apiCredentialId: 456
) {
deletedCount
deletedId
}
}
IP Address Restrictions
API credentials support IP-based access control using the allowedIpAddresses field. If specified, the credential can only be used from requests originating from the allowed IP addresses.
Supported Formats
- IPv4:
192.168.1.5 - IPv6:
2001:0db8:85a3::8a2e:0370:7334 - CIDR Notation:
192.168.1.0/24,10.0.0.0/8,2001:db8::/32
Examples
Single IP:
allowedIpAddresses: ["203.0.113.10"]
Multiple IPs:
allowedIpAddresses: ["192.168.1.5", "10.0.0.100", "2001:db8::1"]
CIDR Ranges:
allowedIpAddresses: ["192.168.1.0/24", "10.0.0.0/8"]
No Restrictions:
allowedIpAddresses: null # or omit the field
Using API Credentials
OAuth2 Token Request
Use the credential to obtain an access token via OAuth2 Client Credentials flow:
POST /connect/token HTTP/1.1
Host: api.cargoxplorer.com
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
&client_id=api-a1b2c3d4e5f6
&client_secret=dGVzdHNlY3JldDEyMzQ1Njc4OTBhYmNkZWZnaGlqa2xtbm8
Response:
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 3600
}
Using the Access Token
Include the access token in the Authorization header:
POST /graphql HTTP/1.1
Host: api.cargoxplorer.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json
{
"query": "{ orders(organizationId: 1) { items { orderId } } }"
}
Code Example (JavaScript)
// Step 1: Get access token
async function getAccessToken(clientId, clientSecret) {
const response = await fetch('https://api.cargoxplorer.com/connect/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
grant_type: 'client_credentials',
client_id: clientId,
client_secret: clientSecret,
}),
});
const data = await response.json();
return data.access_token;
}
// Step 2: Use access token with GraphQL
async function queryOrders(accessToken, organizationId) {
const response = await fetch('https://api.cargoxplorer.com/graphql', {
method: 'POST',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
query: `
query {
orders(organizationId: ${organizationId}) {
items {
orderId
trackingNumber
status
}
}
}
`,
}),
});
return await response.json();
}
// Usage
const clientId = 'api-a1b2c3d4e5f6';
const clientSecret = 'dGVzdHNlY3JldDEyMzQ1Njc4OTBhYmNkZWZnaGlqa2xtbm8';
const token = await getAccessToken(clientId, clientSecret);
const orders = await queryOrders(token, 1);
console.log(orders);
Code Example (Python)
import requests
# Step 1: Get access token
def get_access_token(client_id, client_secret):
response = requests.post(
'https://api.cargoxplorer.com/connect/token',
data={
'grant_type': 'client_credentials',
'client_id': client_id,
'client_secret': client_secret,
}
)
return response.json()['access_token']
# Step 2: Use access token with GraphQL
def query_orders(access_token, organization_id):
response = requests.post(
'https://api.cargoxplorer.com/graphql',
headers={
'Authorization': f'Bearer {access_token}',
'Content-Type': 'application/json',
},
json={
'query': f'''
query {{
orders(organizationId: {organization_id}) {{
items {{
orderId
trackingNumber
status
}}
}}
}}
'''
}
)
return response.json()
# Usage
client_id = 'api-a1b2c3d4e5f6'
client_secret = 'dGVzdHNlY3JldDEyMzQ1Njc4OTBhYmNkZWZnaGlqa2xtbm8'
token = get_access_token(client_id, client_secret)
orders = query_orders(token, 1)
print(orders)
Security Best Practices
Credential Management
- Store Secrets Securely: Never commit secrets to version control
- Use Environment Variables: Store credentials in environment variables or secure vaults
- Rotate Regularly: Regenerate secrets periodically using the
regenerateApiCredentialSecretmutation - Delete Unused Credentials: Remove credentials that are no longer needed
- Monitor Usage: Check the
lastUsedAtfield to identify unused credentials
IP Restrictions
- Use CIDR Notation: Restrict access to known IP ranges
- Minimize Exposure: Use the smallest IP range necessary
- Update When Needed: Update
allowedIpAddresseswhen infrastructure changes
Expiration
- Set Expiration Dates: Always set an
expiresAtvalue for production credentials - Renew Before Expiry: Create new credentials before old ones expire
- Short-Lived for Testing: Use short expiration times for development/testing
Token Handling
- Cache Tokens: Access tokens are valid for 1 hour - cache and reuse them
- Handle Expiration: Implement token refresh logic when tokens expire
- Secure Storage: Store tokens securely in memory, never in local storage or cookies
Limits and Quotas
- Maximum Credentials per User: 5
- Client ID Format:
api-{32-character-hex} - Secret Length: 43 characters (URL-safe Base64)
- Token Lifetime: 3600 seconds (1 hour)
- Name Length: Maximum 100 characters
- IP Addresses: Maximum 50 addresses per credential
Error Handling
Common Errors
Maximum credentials exceeded:
{
"errors": [{
"message": "Maximum of 5 API credentials per user is allowed",
"extensions": {
"code": "INVALID_OPERATION"
}
}]
}
Unauthorized (non-admin creating for another user):
{
"errors": [{
"message": "Only members of the Administrators group can create API credentials for other users",
"extensions": {
"code": "UNAUTHORIZED"
}
}]
}
Invalid IP address:
{
"errors": [{
"message": "All IP addresses must be valid IPv4, IPv6, or CIDR notation",
"extensions": {
"code": "VALIDATION_ERROR"
}
}]
}
Credential not found:
{
"errors": [{
"message": "ApiCredential with id 999 was not found",
"extensions": {
"code": "NOT_FOUND"
}
}]
}
IP blocked:
HTTP/1.1 403 Forbidden
{
"error": "invalid_client",
"error_description": "IP address not allowed"
}
FAQ
How do I get my first API credential?
Use the CargoXplorer web interface:
- Navigate to Settings → API Credentials
- Click "Create New Credential"
- Fill in the details and save
- Copy the secret immediately - it won't be shown again
What happens if I lose my client secret?
You cannot retrieve a lost secret. Use the regenerateApiCredentialSecret mutation to generate a new one. The old secret will be invalidated immediately.
Can I use the same credential from multiple servers?
Yes, if those servers are within your allowed IP ranges. If no IP restrictions are set, the credential can be used from anywhere.
How do I test my API credential?
Use tools like Postman, cURL, or the code examples above to:
- Request an access token from
/connect/token - Use the token to query
/graphql
Why is my credential not working?
Check the following:
- Credential hasn't expired (
expiresAt) - Credential isn't deleted (
isDeleted: false) - IP address is allowed (if
allowedIpAddressesis set) - Client ID and secret are correct
- Token hasn't expired (tokens last 1 hour)
Can Administrators see other users' secrets?
No. Secrets are hashed and cannot be retrieved by anyone, including administrators. Administrators can create credentials for others but cannot view their secrets after creation.