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