API Integration Guide
Learn how to integrate the Automatum API into your workflows to automate marketplace operations, sync customer data, and build custom solutions.
What Can You Do with the API?
The Automatum API enables you to:
- Automate Customer Onboarding - Sync customer data with your CRM
- Submit Usage Metering - Report usage automatically from your product
- Receive Real-time Events - React to marketplace events via webhooks
Common Use Cases
1. CRM Integration
Automatically sync Automatum customers with your Salesforce, HubSpot, or custom CRM:
typescript
// Sync new customers to CRM via webhook
webhook.on('customer.created', async (event) => {
const customer = event.data;
await crm.createContact({
email: customer.details.company.name,
company: customer.details.company.name,
customFields: {
automatumCustomerId: customer.id,
cloudIdentifier: customer.cloudIdentifier
}
});
});2. Automated Usage Metering
Submit usage data automatically from your application:
typescript
// Report API usage every hour
setInterval(async () => {
const usage = await getHourlyApiCalls();
const token = await getAccessToken();
await fetch('https://api.automatum.io/api/v1/metering', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
vendor: 'aws',
listingId: process.env.LISTING_ID,
details: {
customer: {
id: customer.automatumId
},
reportingDate: new Date().toISOString().split('T')[0],
charged: usage.totalCalls * 0.01,
usages: [{
apiKey: 'api_calls',
units: usage.totalCalls,
title: 'API Calls',
description: 'Number of API requests',
per: 'request',
price: '0.01'
}]
}
})
});
}, 3600000); // Every hourGetting Started
Step 1: Create OAuth Application
- Log in to Automatum
- Navigate to Settings > Integrations > API Access
- Click Create OAuth Application
- Fill in the details:
- Name: e.g., "Salesforce Integration"
- Description: e.g., "Syncs customers and contracts"
- Scopes: Select required permissions
- Save your
client_idandclient_secret
Secret Shown Once
Your client_secret is displayed only once. Store it securely in a password manager or secrets vault.
Step 2: Test Authentication
Test your credentials with a simple request:
bash
# Get access token
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=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_CLIENT_SECRET"
# Use token to fetch customers
curl -X GET https://api.automatum.io/api/v1/customers \
-H "Authorization: Bearer ACCESS_TOKEN"Step 3: Build Your Integration
Choose your preferred language and framework:
Node.js / TypeScript
typescript
// Get access token
const tokenResponse = 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,
scope: 'read:customers'
})
});
const { access_token } = await tokenResponse.json();
// Use token to fetch customers
const customersResponse = await fetch('https://api.automatum.io/api/v1/customers', {
headers: { 'Authorization': `Bearer ${access_token}` }
});
const { data: customers } = await customersResponse.json();Python
python
import requests
import os
# Get access token
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'],
'scope': 'read:customers'
}
)
access_token = token_response.json()['access_token']
# Use token to fetch customers
customers_response = requests.get('https://api.automatum.io/api/v1/customers',
headers={'Authorization': f'Bearer {access_token}'}
)
customers = customers_response.json()['data']Step 4: Set Up Webhooks (Optional)
Receive real-time notifications for events:
- Navigate to Settings > Integrations > Webhooks
- Click Create Webhook
- Enter your endpoint URL (must be HTTPS)
- Select events you want to receive
- Save and test
Best Practices
1. Environment-Based Configuration
Use different OAuth applications for each environment:
typescript
const config = {
development: {
clientId: process.env.DEV_CLIENT_ID,
clientSecret: process.env.DEV_CLIENT_SECRET,
baseUrl: 'https://api.automatum.io'
},
production: {
clientId: process.env.PROD_CLIENT_ID,
clientSecret: process.env.PROD_CLIENT_SECRET,
baseUrl: 'https://api.automatum.io'
}
};
const currentConfig = config[process.env.NODE_ENV || 'development'];2. Token Caching
Cache access tokens to minimize token requests:
typescript
class TokenManager {
private token?: string;
private expiresAt?: number;
async getToken(): Promise<string> {
// Return cached token if still valid
if (this.token && this.expiresAt && Date.now() < this.expiresAt - 60000) {
return this.token;
}
// Request new token
const response = await this.requestToken();
this.token = response.access_token;
this.expiresAt = Date.now() + (response.expires_in * 1000);
return this.token;
}
}3. Error Handling
Implement robust error handling:
typescript
async function makeAPIRequest(endpoint: string, token: string) {
try {
const response = await fetch(`https://api.automatum.io${endpoint}`, {
headers: { 'Authorization': `Bearer ${token}` }
});
if (!response.ok) {
if (response.status === 401) {
// Token expired, get new token and retry
const newToken = await getAccessToken();
return makeAPIRequest(endpoint, newToken);
} else if (response.status === 429) {
// Rate limited, wait and retry
const retryAfter = parseInt(response.headers.get('Retry-After') || '60');
await sleep(retryAfter * 1000);
return makeAPIRequest(endpoint, token);
} else if (response.status >= 500) {
// Server error, retry with exponential backoff
return retryWithBackoff(() => makeAPIRequest(endpoint, token));
}
throw new Error(`API request failed: ${response.status}`);
}
return await response.json();
} catch (error) {
throw error;
}
}4. Pagination
Handle paginated responses properly:
typescript
async function getAllCustomers(token: string) {
const allCustomers = [];
let cursor: string | undefined;
do {
const url = cursor
? `https://api.automatum.io/api/v1/customers?limit=100&cursor=${cursor}`
: 'https://api.automatum.io/api/v1/customers?limit=100';
const response = await fetch(url, {
headers: { 'Authorization': `Bearer ${token}` }
});
const result = await response.json();
if (Array.isArray(result.data)) {
allCustomers.push(...result.data);
cursor = undefined; // No pagination info
} else {
allCustomers.push(...result.data.items);
cursor = result.data.nextCursor;
}
} while (cursor);
return allCustomers;
}5. Idempotency
Use idempotency keys for critical operations:
typescript
// Prevent duplicate metering submissions
const idempotencyKey = `metering-${customerId}-${date}-${apiKey}`;
await fetch('https://api.automatum.io/api/v1/metering', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
'Idempotency-Key': idempotencyKey
},
body: JSON.stringify({
vendor: 'aws',
listingId: listingId,
details: {
customer: { id: customerId },
reportingDate: date,
charged: quantity * price,
usages: [{
apiKey: apiKey,
units: quantity,
title: 'Usage',
per: 'unit',
price: price.toString()
}]
}
})
});Security Considerations
Credential Storage
✅ Do:
- Use environment variables for development
- Use secrets managers (AWS Secrets Manager, HashiCorp Vault) for production
- Rotate credentials every 30-90 days
- Revoke credentials immediately if compromised
❌ Don't:
- Hardcode credentials in source code
- Commit credentials to version control
- Share credentials between environments
- Use the same credentials for multiple applications
Network Security
✅ Do:
- Use HTTPS for all requests
- Validate SSL certificates
- Use secure webhook endpoints (HTTPS)
- Implement webhook signature verification
❌ Don't:
- Disable SSL verification
- Use HTTP for API requests
- Expose webhook endpoints publicly without authentication
Access Control
✅ Do:
- Request minimum required scopes
- Create separate OAuth applications for different purposes
- Monitor API usage and access logs
- Implement IP allowlisting when possible
❌ Don't:
- Request all scopes "just in case"
- Share OAuth applications across teams
- Ignore unusual API access patterns
Monitoring and Observability
Log API Calls
typescript
const logger = winston.createLogger({
format: winston.format.json(),
transports: [new winston.transports.Console()]
});
// Wrap fetch to log requests/responses
async function loggedFetch(url: string, options: RequestInit) {
const startTime = Date.now();
logger.info('API Request', {
method: options.method || 'GET',
url,
timestamp: new Date().toISOString()
});
const response = await fetch(url, options);
logger.info('API Response', {
status: response.status,
duration: Date.now() - startTime,
timestamp: new Date().toISOString()
});
return response;
}Track Metrics
Monitor key integration metrics:
- API Request Count - Total requests per hour/day
- Error Rate - Percentage of failed requests
- Response Time - Average API response time
- Rate Limit Usage - Percentage of rate limit used
- Token Refresh Rate - How often tokens are refreshed
Set Up Alerts
Create alerts for critical issues:
typescript
// Alert on high error rate
if (errorRate > 5%) {
sendAlert({
severity: 'high',
message: `Automatum API error rate is ${errorRate}%`,
channel: '#engineering'
});
}
// Alert on approaching rate limits
if (rateLimitRemaining < 100) {
sendAlert({
severity: 'medium',
message: 'Approaching Automatum API rate limit',
channel: '#engineering'
});
}Testing
Unit Tests
Mock the Automatum API in tests:
typescript
import { jest } from '@jest/globals';
// Mock fetch
global.fetch = jest.fn();
describe('CRM Sync', () => {
it('syncs new customers', async () => {
(global.fetch as jest.Mock).mockResolvedValueOnce({
ok: true,
json: async () => ({
code: 200,
data: [{ id: 'cust_123', cloudIdentifier: 'test-123' }]
})
});
const result = await syncCustomersToCRM('test-token');
expect(result.synced).toBe(1);
});
});Example Integrations
Salesforce
typescript
import { SalesforceClient } from 'salesforce-sdk';
async function syncCustomers(token: string) {
const salesforce = new SalesforceClient({...});
// Fetch customers from Automatum
const response = await fetch('https://api.automatum.io/api/v1/customers', {
headers: { 'Authorization': `Bearer ${token}` }
});
const { data: customers } = await response.json();
for (const customer of customers) {
const email = customer.details?.contacts?.[0]?.email;
if (!email) continue;
const existingContact = await salesforce.findContactByEmail(email);
if (existingContact) {
await salesforce.updateContact(existingContact.id, {
MarketplaceCustomerId__c: customer.id,
CloudIdentifier__c: customer.cloudIdentifier,
});
} else {
await salesforce.createContact({
Email: email,
Company: customer.details?.company?.name || 'Unknown',
MarketplaceCustomerId__c: customer.id,
CloudIdentifier__c: customer.cloudIdentifier,
});
}
}
}Slack Notifications
typescript
import { WebClient } from '@slack/web-api';
webhook.on('private_offer.accepted', async (event) => {
const offer = event.data;
const slack = new WebClient(process.env.SLACK_TOKEN);
await slack.chat.postMessage({
channel: '#sales',
text: `:tada: Private offer accepted!`,
blocks: [
{
type: 'section',
text: {
type: 'mrkdwn',
text: `*${offer.details?.summary?.customerName || 'Customer'}* accepted the private offer!`
}
},
{
type: 'section',
fields: [
{ type: 'mrkdwn', text: `*Offer ID:*\n${offer.id}` },
{ type: 'mrkdwn', text: `*Listing:*\n${offer.listingId}` }
]
}
]
});
});Support
Need help with your integration?
- Documentation: API Reference
- Support Email: support@automatum.io
Next Steps
- Authentication Guide - OAuth 2.0 setup
- API Reference - Complete endpoint documentation
- Webhooks Guide - Real-time event notifications