Skip to content

API Client Setup

Configure API access for programmatic integration with Automatum.

Overview

The Automatum API provides programmatic access to all platform features. Use API clients to automate workflows, integrate with existing systems, and build custom solutions.

Authentication

The Automatum API uses OAuth 2.0 Client Credentials flow for secure, server-to-server authentication.

OAuth 2.0 Setup

Create OAuth Application:

  1. Go to Settings > Integrations > API Access
  2. Click Create OAuth Application
  3. Provide a descriptive name and description
  4. Select the scopes (permissions) your application needs
  5. Click Create
  6. Copy the Client ID and Secret immediately (secret shown only once)

OAuth Application Credentials:

json
{
  "clientId": "app_1a2b3c4d5e6f",
  "clientSecret": "secret_7g8h9i0j1k2l3m4n5o6p"
}

Keep Your Secret Safe

The clientSecret is shown only once. Store it securely:

  • Use environment variables
  • Never commit to version control
  • Rotate periodically
  • Store in a secrets manager (AWS Secrets Manager, HashiCorp Vault, etc.)

Get Access Token

Exchange your client credentials for an access token:

bash
curl -X POST https://api.automatum.io/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=app_1a2b3c4d5e6f" \
  -d "client_secret=secret_7g8h9i0j1k2l3m4n5o6p"

Response:

json
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "read:customers read:entitlements write:metering"
}

Using Access Token:

bash
curl -X GET https://api.automatum.io/api/v1/customers \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Token Management

Access tokens expire after 1 hour. Implement automatic token refresh in your application. See the Authentication Guide for detailed token management examples.

Making API Calls

The Automatum API is a RESTful API that you can call directly using any HTTP client. Here are examples in common languages:

cURL

bash
# Get access token
TOKEN_RESPONSE=$(curl -X POST https://api.automatum.io/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=$CLIENT_ID" \
  -d "client_secret=$CLIENT_SECRET")

ACCESS_TOKEN=$(echo $TOKEN_RESPONSE | jq -r '.access_token')

# List all private offers
curl -X GET https://api.automatum.io/api/v1/private-offer \
  -H "Authorization: Bearer $ACCESS_TOKEN"

# Create a new offer
curl -X POST https://api.automatum.io/api/v1/private-offer \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "vendor": "aws",
    "listingId": "listing_123",
    "customerId": "cust_456",
    "details": {
      "pricing": {
        "discountPercentage": 20,
        "duration": 12
      }
    }
  }'

JavaScript/Node.js

javascript
// Helper function to get access token
async function getAccessToken() {
  const response = await fetch('https://api.automatum.io/oauth/token', {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: new URLSearchParams({
      grant_type: 'client_credentials',
      client_id: process.env.AUTOMATUM_CLIENT_ID,
      client_secret: process.env.AUTOMATUM_CLIENT_SECRET
    })
  });
  
  const data = await response.json();
  return data.access_token;
}

// List all private offers
async function listPrivateOffers() {
  const token = await getAccessToken();
  
  const response = await fetch('https://api.automatum.io/api/v1/private-offer', {
    headers: {
      'Authorization': `Bearer ${token}`
    }
  });
  
  return response.json();
}

// Create a new offer
async function createPrivateOffer(offerData) {
  const token = await getAccessToken();
  
  const response = await fetch('https://api.automatum.io/api/v1/private-offer', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(offerData)
  });
  
  return response.json();
}

// Usage
const offers = await listPrivateOffers();

const newOffer = await createPrivateOffer({
  vendor: 'aws',
  listingId: 'listing_123',
  customerId: 'cust_456',
  details: {
    pricing: {
      discountPercentage: 20,
      duration: 12
    }
  }
});

Python

python
import os
import requests
from typing import Dict, Any

def get_access_token() -> str:
    """Get OAuth access token"""
    response = requests.post(
        'https://api.automatum.io/oauth/token',
        data={
            'grant_type': 'client_credentials',
            'client_id': os.environ['AUTOMATUM_CLIENT_ID'],
            'client_secret': os.environ['AUTOMATUM_CLIENT_SECRET']
        }
    )
    response.raise_for_status()
    return response.json()['access_token']

def list_private_offers() -> Dict[str, Any]:
    """List all private offers"""
    token = get_access_token()
    response = requests.get(
        'https://api.automatum.io/api/v1/private-offer',
        headers={'Authorization': f'Bearer {token}'}
    )
    response.raise_for_status()
    return response.json()

def create_private_offer(offer_data: Dict[str, Any]) -> Dict[str, Any]:
    """Create a new private offer"""
    token = get_access_token()
    response = requests.post(
        'https://api.automatum.io/api/v1/private-offer',
        headers={
            'Authorization': f'Bearer {token}',
            'Content-Type': 'application/json'
        },
        json=offer_data
    )
    response.raise_for_status()
    return response.json()

# Usage
offers = list_private_offers()

new_offer = create_private_offer({
    'vendor': 'aws',
    'listingId': 'listing_123',
    'customerId': 'cust_456',
    'details': {
        'pricing': {
            'discountPercentage': 20,
            'duration': 12
        }
    }
})

TypeScript

typescript
interface TokenResponse {
  access_token: string;
  token_type: string;
  expires_in: number;
  scope: string;
}

async function getAccessToken(): Promise<string> {
  const response = await fetch('https://api.automatum.io/oauth/token', {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: new URLSearchParams({
      grant_type: 'client_credentials',
      client_id: process.env.AUTOMATUM_CLIENT_ID!,
      client_secret: process.env.AUTOMATUM_CLIENT_SECRET!
    })
  });
  
  const data: TokenResponse = await response.json();
  return data.access_token;
}

async function listPrivateOffers() {
  const token = await getAccessToken();
  
  const response = await fetch('https://api.automatum.io/api/v1/private-offer', {
    headers: {
      'Authorization': `Bearer ${token}`
    }
  });
  
  return response.json();
}

async function createPrivateOffer(offerData: any) {
  const token = await getAccessToken();
  
  const response = await fetch('https://api.automatum.io/api/v1/private-offer', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(offerData)
  });
  
  return response.json();
}

Token Management

Access tokens expire after 1 hour. Implement token caching and automatic refresh in your application:

JavaScript Token Cache Example

javascript
let cachedToken = null;
let tokenExpiresAt = 0;

async function getValidToken() {
  // Check if token is still valid (with 5 minute buffer)
  if (cachedToken && Date.now() < tokenExpiresAt - 300000) {
    return cachedToken;
  }
  
  // Get new token
  const response = await fetch('https://api.automatum.io/oauth/token', {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: new URLSearchParams({
      grant_type: 'client_credentials',
      client_id: process.env.AUTOMATUM_CLIENT_ID,
      client_secret: process.env.AUTOMATUM_CLIENT_SECRET
    })
  });
  
  const data = await response.json();
  cachedToken = data.access_token;
  tokenExpiresAt = Date.now() + (data.expires_in * 1000);
  
  return cachedToken;
}

Python Token Cache Example

python
import time
from typing import Optional

class TokenManager:
    def __init__(self):
        self.token: Optional[str] = None
        self.expires_at: float = 0
    
    def get_valid_token(self) -> str:
        # Check if token is still valid (with 5 minute buffer)
        if self.token and time.time() < self.expires_at - 300:
            return self.token
        
        # Get new token
        response = requests.post(
            'https://api.automatum.io/oauth/token',
            data={
                'grant_type': 'client_credentials',
                'client_id': os.environ['AUTOMATUM_CLIENT_ID'],
                'client_secret': os.environ['AUTOMATUM_CLIENT_SECRET']
            }
        )
        response.raise_for_status()
        data = response.json()
        
        self.token = data['access_token']
        self.expires_at = time.time() + data['expires_in']
        
        return self.token

# Usage
token_manager = TokenManager()
token = token_manager.get_valid_token()

Environment Variables

Recommended environment configuration:

bash
# .env file
AUTOMATUM_CLIENT_ID=app_1a2b3c4d5e6f
AUTOMATUM_CLIENT_SECRET=secret_7g8h9i0j1k2l3m4n5o6p
AUTOMATUM_BASE_URL=https://api.automatum.io/api/v1
AUTOMATUM_TOKEN_URL=https://api.automatum.io/oauth/token

API Resources

Available Resources

ResourceDescription
privateOffersManage private offers
listingsProduct listings
customersCustomer management
entitlementsCustomer entitlements
meteringEventsUsage metering
organizationsOrganization settings
activitiesActivity feed

Resource Methods

Each resource supports standard CRUD operations. See the API Reference for complete endpoint documentation:

javascript
// List with filters and pagination
const token = await getValidToken();
const response = await fetch(
  'https://api.automatum.io/api/v1/listing?vendor=aws&status=active&page=1&limit=20',
  {
    headers: { 'Authorization': `Bearer ${token}` }
  }
);
const items = await response.json();

// Get by ID
const itemResponse = await fetch(
  'https://api.automatum.io/api/v1/listing/listing_123',
  {
    headers: { 'Authorization': `Bearer ${token}` }
  }
);
const item = await itemResponse.json();

// Create new
const created = await fetch('https://api.automatum.io/api/v1/listing', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${token}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({ ... })
});

// Update existing
const updated = await fetch('https://api.automatum.io/api/v1/listing/listing_123', {
  method: 'PUT',
  headers: {
    'Authorization': `Bearer ${token}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({ ... })
});

// Delete
await fetch('https://api.automatum.io/api/v1/listing/listing_123', {
  method: 'DELETE',
  headers: { 'Authorization': `Bearer ${token}` }
});

Error Handling

Error Handling

javascript
async function makeApiRequest(url, options = {}) {
  const token = await getValidToken();
  
  const response = await fetch(url, {
    ...options,
    headers: {
      ...options.headers,
      'Authorization': `Bearer ${token}`
    }
  });
  
  if (!response.ok) {
    const error = await response.json();
    
    switch (response.status) {
      case 401:
        // Authentication error - token expired or invalid
        console.error('Authentication failed:', error.message);
        // Clear cached token and retry
        cachedToken = null;
        break;
      case 403:
        // Insufficient scope
        console.error('Insufficient permissions:', error.message);
        break;
      case 404:
        // Resource not found
        console.error('Resource not found:', error.message);
        break;
      case 422:
        // Validation error
        console.error('Validation failed:', error.errors);
        break;
      case 429:
        // Rate limit exceeded
        const retryAfter = response.headers.get('Retry-After');
        console.error('Rate limit exceeded, retry after:', retryAfter, 'seconds');
        break;
      default:
        console.error('API error:', error.message);
    }
    
    throw new Error(error.message || 'API request failed');
  }
  
  return response.json();
}

Error Response Structure

json
{
  "error": {
    "type": "validation_error",
    "message": "Invalid request parameters",
    "code": "INVALID_PARAMS",
    "errors": [
      {
        "field": "customerId",
        "message": "Customer ID is required"
      }
    ]
  }
}

Rate Limiting

Default Limits

  • 1000 requests per hour per API key
  • 50 requests per minute burst limit

Handling Rate Limits

javascript
const client = new Automatum({
  apiKey: 'sk_live_...',
  maxRetries: 3,
  retryOnRateLimit: true, // Auto-retry on 429
});

Manual handling:

javascript
async function makeRequestWithRetry(url, options = {}, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    const response = await fetch(url, options);
    
    if (response.status === 429) {
      const retryAfter = parseInt(response.headers.get('Retry-After') || '60');
      console.log(`Rate limited. Retrying after ${retryAfter} seconds...`);
      await sleep(retryAfter * 1000);
      continue;
    }
    
    return response;
  }
  
  throw new Error('Max retries exceeded');
}

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

Rate Limit Headers

X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 850
X-RateLimit-Reset: 1642334400

Pagination

Cursor-Based Pagination

javascript
// First page
const token = await getValidToken();
const page1 = await fetch(
  'https://api.automatum.io/api/v1/customer?limit=50',
  { headers: { 'Authorization': `Bearer ${token}` } }
).then(r => r.json());

// Next page using cursor
const page2 = await fetch(
  `https://api.automatum.io/api/v1/customer?limit=50&cursor=${page1.nextCursor}`,
  { headers: { 'Authorization': `Bearer ${token}` } }
).then(r => r.json());

// Iterate all pages
async function* getAllCustomers() {
  let cursor = null;
  const token = await getValidToken();
  
  do {
    const url = cursor 
      ? `https://api.automatum.io/api/v1/customer?limit=100&cursor=${cursor}`
      : 'https://api.automatum.io/api/v1/customer?limit=100';
    
    const response = await fetch(url, {
      headers: { 'Authorization': `Bearer ${token}` }
    });
    const data = await response.json();
    
    for (const customer of data.data) {
      yield customer;
    }
    
    cursor = data.nextCursor;
  } while (cursor);
}

// Usage
for await (const customer of getAllCustomers()) {
  console.log(customer.name);
}

Page-Based Pagination

javascript
const token = await getValidToken();
const response = await fetch(
  'https://api.automatum.io/api/v1/listing?page=2&limit=20',
  { headers: { 'Authorization': `Bearer ${token}` } }
).then(r => r.json());

console.log({
  currentPage: response.pagination.page,
  totalPages: response.pagination.pages,
  totalItems: response.pagination.total
});

Filtering and Sorting

Query Filters

javascript
const token = await getValidToken();

// Build query parameters
const params = new URLSearchParams({
  vendor: 'aws',
  status: 'accepted',
  'createdAt[gte]': '2026-01-01',
  'createdAt[lte]': '2026-12-31',
  'value[gt]': '10000'
});

const offers = await fetch(
  `https://api.automatum.io/api/v1/private-offer?${params}`,
  { headers: { 'Authorization': `Bearer ${token}` } }
).then(r => r.json());

Sorting

javascript
const token = await getValidToken();

// Single field sort
const customers = await fetch(
  'https://api.automatum.io/api/v1/customer?sort=-createdAt', // Descending
  { headers: { 'Authorization': `Bearer ${token}` } }
).then(r => r.json());

// Or ascending
const customersAsc = await fetch(
  'https://api.automatum.io/api/v1/customer?sort=name',
  { headers: { 'Authorization': `Bearer ${token}` } }
).then(r => r.json());

// Multiple sort fields
const customersMulti = await fetch(
  'https://api.automatum.io/api/v1/customer?sort=-value,name',
  { headers: { 'Authorization': `Bearer ${token}` } }
).then(r => r.json());

Idempotency

Prevent duplicate operations by including an Idempotency-Key header:

javascript
const token = await getValidToken();
const offer = await fetch('https://api.automatum.io/api/v1/private-offer', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${token}`,
    'Content-Type': 'application/json',
    'Idempotency-Key': 'unique-request-id-123'
  },
  body: JSON.stringify({
    vendor: 'aws',
    listingId: 'listing_123',
    customerId: 'cust_456'
  })
}).then(r => r.json());

Webhooks

Register and manage webhooks:

javascript
// Create webhook
const token = await getValidToken();
const webhook = await fetch('https://api.automatum.io/api/v1/webhook', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${token}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    url: 'https://your-app.com/webhooks',
    events: ['entitlement.created', 'entitlement.updated'],
    name: 'My Webhook'
  })
}).then(r => r.json());

// Verify webhook signature (see Webhooks documentation for implementation)

Testing

Test/Staging Environment

Use test OAuth applications pointing to staging environment:

javascript
// Use staging base URL for testing
const STAGING_BASE_URL = 'https://api.staging.automatum.io/api/v1';
const STAGING_TOKEN_URL = 'https://api.staging.automatum.io/oauth/token';

async function getStagingToken() {
  const response = await fetch(STAGING_TOKEN_URL, {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: new URLSearchParams({
      grant_type: 'client_credentials',
      client_id: process.env.AUTOMATUM_TEST_CLIENT_ID,
      client_secret: process.env.AUTOMATUM_TEST_CLIENT_SECRET
    })
  });
  
  const data = await response.json();
  return data.access_token;
}

// All operations use sandbox environment
const token = await getStagingToken();
const offer = await fetch(`${STAGING_BASE_URL}/private-offer`, {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${token}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({...})
}).then(r => r.json());

Mocking for Unit Tests

For unit tests, mock the fetch API:

javascript
// Mock fetch for testing
global.fetch = jest.fn(() =>
  Promise.resolve({
    ok: true,
    json: () => Promise.resolve({ data: [] })
  })
);

// In your tests
const offers = await listPrivateOffers();
expect(fetch).toHaveBeenCalledWith(
  expect.stringContaining('/private-offer'),
  expect.any(Object)
);

Logging

Request/Response Logging

Add logging to your API calls:

javascript
async function makeApiRequest(url, options = {}) {
  const token = await getValidToken();
  
  console.log('Request:', options.method || 'GET', url);
  
  const response = await fetch(url, {
    ...options,
    headers: {
      ...options.headers,
      'Authorization': `Bearer ${token}`
    }
  });
  
  console.log('Response:', response.status, response.statusText);
  
  const data = await response.json();
  console.log('Response data:', data);
  
  return data;
}

Custom Logger

javascript
const logger = {
  debug: (msg) => console.debug(`[DEBUG] ${msg}`),
  info: (msg) => console.info(`[INFO] ${msg}`),
  warn: (msg) => console.warn(`[WARN] ${msg}`),
  error: (msg) => console.error(`[ERROR] ${msg}`)
};

async function makeApiRequest(url, options = {}) {
  logger.info(`Making ${options.method || 'GET'} request to ${url}`);
  
  try {
    const token = await getValidToken();
    const response = await fetch(url, {
      ...options,
      headers: {
        ...options.headers,
        'Authorization': `Bearer ${token}`
      }
    });
    
    if (!response.ok) {
      logger.error(`Request failed: ${response.status} ${response.statusText}`);
    }
    
    return response.json();
  } catch (error) {
    logger.error(`Request error: ${error.message}`);
    throw error;
  }
}

Best Practices

  1. Store credentials securely - Use environment variables, never commit to code
  2. Implement token caching - Cache access tokens and refresh before expiration (tokens expire after 1 hour)
  3. Handle errors gracefully - Implement proper error handling for all HTTP status codes
  4. Use retries - Implement automatic retries for transient failures (429, 5xx errors)
  5. Implement idempotency - Use Idempotency-Key header to prevent duplicate operations
  6. Monitor usage - Track API usage, rate limits, and errors
  7. Respect rate limits - Implement exponential backoff when rate limited
  8. Rotate secrets periodically - Regenerate client secrets for security
  9. Use HTTPS - Always use HTTPS for all API requests
  10. Validate responses - Always validate API responses before using data

Troubleshooting

Common Issues

Authentication Error:

  • Verify client ID and secret are correct
  • Check if OAuth application was deleted
  • Ensure credentials haven't been regenerated
  • Verify access token hasn't expired (tokens expire after 1 hour)
  • Check that application has required scopes

Rate Limit Exceeded:

  • Implement exponential backoff
  • Cache responses where possible
  • Request rate limit increase if needed

Timeout Errors:

  • Increase timeout configuration
  • Check network connectivity
  • Verify endpoint is responsive

Next Steps

Need Help?

Contact api@automatum.io for API client support.

Automatum GTM Platform