Skip to content

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 hour

Getting Started

Step 1: Create OAuth Application

  1. Log in to Automatum
  2. Navigate to Settings > Integrations > API Access
  3. Click Create OAuth Application
  4. Fill in the details:
    • Name: e.g., "Salesforce Integration"
    • Description: e.g., "Syncs customers and contracts"
    • Scopes: Select required permissions
  5. Save your client_id and client_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:

  1. Navigate to Settings > Integrations > Webhooks
  2. Click Create Webhook
  3. Enter your endpoint URL (must be HTTPS)
  4. Select events you want to receive
  5. 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?

Next Steps

Automatum GTM Platform