Code Examples
Code Examples
Section titled “Code Examples”This page provides complete, production-ready code examples for integrating with the CRM M2M API in multiple programming languages and platforms.
Note: the language examples below call
GET /products(the list endpoint) as a minimal smoke test. That endpoint is deprecated for catalog sync — for a real integration, useGET /products/searchwithsyncToken, as shown in the WooCommerce section and the product sync guide.
Quick Links
Section titled “Quick Links”- Node.js Examples
- Python Examples
- PHP Examples
- cURL Examples
- WooCommerce Integration
- Postman Collection
Node.js Examples
Section titled “Node.js Examples”Installation
Section titled “Installation”npm install axiosGet Access Token
Section titled “Get Access Token”/** * Get OAuth Access Token - Node.js Example */import axios from 'axios';
// Configuration - Load from environment variablesconst COGNITO_DOMAIN = process.env.OAUTH_COGNITO_DOMAIN;const CLIENT_ID = process.env.OAUTH_CLIENT_ID;const CLIENT_SECRET = process.env.OAUTH_CLIENT_SECRET;const SCOPES = process.env.OAUTH_SCOPES || 'crm-api/products.read';
if (!COGNITO_DOMAIN || !CLIENT_ID || !CLIENT_SECRET) { console.error('Missing required environment variables: OAUTH_COGNITO_DOMAIN, OAUTH_CLIENT_ID, OAUTH_CLIENT_SECRET'); process.exit(1);}
async function getAccessToken() { try { const tokenUrl = `https://${COGNITO_DOMAIN}/oauth2/token`;
const params = new URLSearchParams({ grant_type: 'client_credentials', client_id: CLIENT_ID, client_secret: CLIENT_SECRET, scope: SCOPES, });
const response = await axios.post(tokenUrl, params, { headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, });
const { access_token, token_type, expires_in } = response.data;
console.log('Access token obtained successfully'); console.log(`Token type: ${token_type}`); console.log(`Expires in: ${expires_in} seconds (${expires_in / 60} minutes)`);
return access_token; } catch (error) { if (error.response) { console.error('Token request failed:', error.response.data); throw new Error(`Token error: ${error.response.data.error}`); } throw error; }}
export { getAccessToken };Make API Calls
Section titled “Make API Calls”/** * Call CRM API - Node.js Example */import axios from 'axios';import { getAccessToken } from './get-token.js';
const API_BASE_URL = process.env.API_BASE_URL;
if (!API_BASE_URL) { console.error('Missing required environment variable: API_BASE_URL'); process.exit(1);}
async function getProducts(accessToken, limit = 10) { try { const response = await axios.get(`${API_BASE_URL}/products`, { params: { limit }, headers: { 'Authorization': `Bearer ${accessToken}` }, });
console.log(`Retrieved ${response.data.products.length} products`); return response.data; } catch (error) { if (error.response) { console.error('API request failed:', error.response.status, error.response.data); throw new Error(`API error: ${error.response.data.message}`); } throw error; }}
async function main() { try { console.log('Obtaining access token...\n'); const accessToken = await getAccessToken();
console.log('\nFetching products...\n'); const productsData = await getProducts(accessToken, 5);
console.log('\nProducts:'); productsData.products.forEach(product => { console.log(` - ${product.productCode}: ${product.description} (R${product.price})`); });
console.log('\nAll API calls completed successfully!'); } catch (error) { console.error('\nError:', error.message); process.exit(1); }}
main();# Run the examplenode call-api.jsPython Examples
Section titled “Python Examples”Installation
Section titled “Installation”pip install requestsGet Access Token
Section titled “Get Access Token”"""Get OAuth Access Token - Python Example"""import osimport requestsfrom typing import Dict
# Configuration - Load from environment variablesCOGNITO_DOMAIN = os.environ['OAUTH_COGNITO_DOMAIN']CLIENT_ID = os.environ['OAUTH_CLIENT_ID']CLIENT_SECRET = os.environ['OAUTH_CLIENT_SECRET']SCOPES = os.environ.get('OAUTH_SCOPES', 'crm-api/products.read')
def get_access_token() -> str: """Get an access token using client credentials.""" token_url = f"https://{COGNITO_DOMAIN}/oauth2/token"
data = { "grant_type": "client_credentials", "client_id": CLIENT_ID, "client_secret": CLIENT_SECRET, "scope": SCOPES, }
response = requests.post( token_url, data=data, headers={"Content-Type": "application/x-www-form-urlencoded"}, )
if response.status_code != 200: error_data = response.json() raise requests.HTTPError( f"Token request failed: {error_data.get('error')} - " f"{error_data.get('error_description')}" )
token_data = response.json() access_token = token_data["access_token"] expires_in = token_data["expires_in"]
print("✅ Access token obtained successfully") print(f"Expires in: {expires_in} seconds ({expires_in / 60:.0f} minutes)")
return access_token
if __name__ == "__main__": try: token = get_access_token() print(f"\n📋 Use this token in API requests:") print(f"Authorization: Bearer {token[:50]}...") except Exception as e: print(f"❌ Error: {e}") exit(1)Make API Calls
Section titled “Make API Calls”"""Call CRM API - Python Example"""import osimport requestsfrom typing import List, Dict, Optionalfrom get_token import get_access_token
API_BASE_URL = os.environ['API_BASE_URL']
def get_products(access_token: str, limit: int = 10) -> Dict: """Get products from the CRM API.""" response = requests.get( f"{API_BASE_URL}/products", params={"limit": limit}, headers={"Authorization": f"Bearer {access_token}"}, )
if response.status_code != 200: error_data = response.json() raise requests.HTTPError( f"API request failed: {response.status_code} - " f"{error_data.get('message', response.text)}" )
data = response.json() print(f"✅ Retrieved {len(data['products'])} products") return data
def main(): """Main example function""" try: # Step 1: Get access token print("🔐 Obtaining access token...\n") access_token = get_access_token()
# Step 2: Get products print("\n📦 Fetching products...\n") products_data = get_products(access_token, limit=5)
print("\nProducts:") for product in products_data["products"]: print( f" - {product['productCode']}: {product['description']} " f"(R{product['price']})" )
print("\n✅ All API calls completed successfully!")
except Exception as e: print(f"\n❌ Error: {e}") exit(1)
if __name__ == "__main__": main()# Run the examplepython call_api.pyPHP Examples
Section titled “PHP Examples”Get Access Token
Section titled “Get Access Token”<?php/** * Get OAuth Access Token - PHP Example */
// Configuration - Load from environment variables$cognitoDomain = getenv('OAUTH_COGNITO_DOMAIN');$clientId = getenv('OAUTH_CLIENT_ID');$clientSecret = getenv('OAUTH_CLIENT_SECRET');$scopes = getenv('OAUTH_SCOPES') ?: 'crm-api/products.read';
if (!$cognitoDomain || !$clientId || !$clientSecret) { fwrite(STDERR, "Missing required environment variables: OAUTH_COGNITO_DOMAIN, OAUTH_CLIENT_ID, OAUTH_CLIENT_SECRET\n"); exit(1);}
function getAccessToken(): array{ $cognitoDomain = getenv('OAUTH_COGNITO_DOMAIN'); $clientId = getenv('OAUTH_CLIENT_ID'); $clientSecret = getenv('OAUTH_CLIENT_SECRET'); $scopes = getenv('OAUTH_SCOPES') ?: 'crm-api/products.read';
$tokenUrl = 'https://' . $cognitoDomain . '/oauth2/token';
$data = [ 'grant_type' => 'client_credentials', 'client_id' => $clientId, 'client_secret' => $clientSecret, 'scope' => $scopes, ];
$ch = curl_init($tokenUrl); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_POSTFIELDS => http_build_query($data), CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ['Content-Type: application/x-www-form-urlencoded'], ]);
$response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch);
if ($httpCode !== 200) { $errorData = json_decode($response, true); throw new Exception( "Token request failed: {$errorData['error']} - {$errorData['error_description']}" ); }
$tokenData = json_decode($response, true); echo "✅ Access token obtained successfully\n";
return $tokenData;}
if (php_sapi_name() === 'cli') { try { $tokenData = getAccessToken(); echo "\n📋 Use this token in API requests:\n"; echo "Authorization: Bearer " . substr($tokenData['access_token'], 0, 50) . "...\n"; } catch (Exception $e) { echo "❌ Error: {$e->getMessage()}\n"; exit(1); }}Make API Calls
Section titled “Make API Calls”<?php/** * Call CRM API - PHP Example */
require_once __DIR__ . '/get-token.php';
$apiBaseUrl = getenv('API_BASE_URL');if (!$apiBaseUrl) { fwrite(STDERR, "Missing required environment variable: API_BASE_URL\n"); exit(1);}
function getProducts(string $accessToken, int $limit = 10): array{ $apiBaseUrl = getenv('API_BASE_URL'); $url = $apiBaseUrl . '/products?' . http_build_query(['limit' => $limit]);
$ch = curl_init($url); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ["Authorization: Bearer {$accessToken}"], ]);
$response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch);
if ($httpCode !== 200) { $errorData = json_decode($response, true); throw new Exception( "API request failed: {$httpCode} - " . ($errorData['message'] ?? $response) ); }
$data = json_decode($response, true); echo "✅ Retrieved " . count($data['products']) . " products\n"; return $data;}
if (php_sapi_name() === 'cli') { try { echo "🔐 Obtaining access token...\n\n"; $tokenData = getAccessToken();
echo "\n📦 Fetching products...\n\n"; $productsData = getProducts($tokenData['access_token'], 5);
echo "\nProducts:\n"; foreach ($productsData['products'] as $product) { echo " - {$product['productCode']}: {$product['description']} (R{$product['price']})\n"; }
echo "\n✅ All API calls completed successfully!\n"; } catch (Exception $e) { echo "\n❌ Error: {$e->getMessage()}\n"; exit(1); }}# Run the examplephp call-api.phpcURL Examples
Section titled “cURL Examples”Get Access Token
Section titled “Get Access Token”#!/bin/bash
# Configuration - Load from environment variables: "${OAUTH_TOKEN_ENDPOINT:?Missing required env var OAUTH_TOKEN_ENDPOINT}": "${OAUTH_CLIENT_ID:?Missing required env var OAUTH_CLIENT_ID}": "${OAUTH_CLIENT_SECRET:?Missing required env var OAUTH_CLIENT_SECRET}"SCOPES="${OAUTH_SCOPES:-crm-api/products.read}"
TOKEN_RESPONSE=$(curl -s -X POST "${OAUTH_TOKEN_ENDPOINT}" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=client_credentials" \ -d "client_id=${OAUTH_CLIENT_ID}" \ -d "client_secret=${OAUTH_CLIENT_SECRET}" \ -d "scope=${SCOPES}")
ACCESS_TOKEN=$(echo "$TOKEN_RESPONSE" | grep -o '"access_token":"[^"]*' | sed 's/"access_token":"//')
echo "Access token obtained: ${ACCESS_TOKEN:0:50}..."Get Products
Section titled “Get Products”: "${API_BASE_URL:?Missing required env var API_BASE_URL}"
PRODUCTS_RESPONSE=$(curl -s -X GET "${API_BASE_URL}/products?limit=5" \ -H "Authorization: Bearer ${ACCESS_TOKEN}")
echo "$PRODUCTS_RESPONSE" | jq '.'Search Products
Section titled “Search Products”SEARCH_RESPONSE=$(curl -s -X GET "${API_BASE_URL}/products/search?q=widget" \ -H "Authorization: Bearer ${ACCESS_TOKEN}")
echo "$SEARCH_RESPONSE" | jq '.'Get Single Product
Section titled “Get Single Product”PRODUCT_ID="prod-123"
PRODUCT_RESPONSE=$(curl -s -X GET "${API_BASE_URL}/products/${PRODUCT_ID}" \ -H "Authorization: Bearer ${ACCESS_TOKEN}")
echo "$PRODUCT_RESPONSE" | jq '.'List Brands
Section titled “List Brands”BRANDS_RESPONSE=$(curl -s -X GET "${API_BASE_URL}/brands?limit=20" \ -H "Authorization: Bearer ${ACCESS_TOKEN}")
echo "$BRANDS_RESPONSE" | jq '.'The M2M API is read-only. There is no
POST/PUT/DELETEfor products or brands via M2M — those operations require an admin web-app session and return403for an M2M token. Use the read endpoints above to sync the CRM catalog into your own system.
WooCommerce Integration
Section titled “WooCommerce Integration”WordPress example for syncing the CRM catalog into WooCommerce. The M2M API is read-only, so the supported direction is CRM → WooCommerce: read products from the CRM and upsert them as WooCommerce products. (To push WooCommerce products into the CRM you would need write access, which is not available to M2M clients.)
For the recommended incremental-sync pattern (updatedAfter + syncToken), see the M2M Product Sync Guide.
Features
Section titled “Features”- ✅ Pull the CRM catalog into WooCommerce (one-way, CRM → WooCommerce)
- ✅ Upsert by
productCode→ WooCommerce SKU (create or update) - ✅ Paginated full sync via
GET /products/search+syncToken - ✅ Token caching (cache until shortly before
expires_in) - ✅ Scheduled background sync via WP-Cron
- ✅ Error handling and admin notices
Installation
Section titled “Installation”Add this code to your theme’s functions.php or create a custom plugin:
<?php/** * WooCommerce CRM Catalog Sync (CRM → WooCommerce, read-only) */
// Configuration (add to wp-config.php)define('CRM_CLIENT_ID', 'your-client-id-here');define('CRM_CLIENT_SECRET', 'your-client-secret-here');define('CRM_API_BASE_URL', 'https://{api-base-url}');define('CRM_TOKEN_URL', '{token-endpoint}');define('CRM_SCOPES', 'crm-api/products.read');
/** * Get CRM access token (with caching) */function crm_get_access_token(){ $cached_token = get_transient('crm_access_token'); if ($cached_token !== false) { return $cached_token; }
$response = wp_remote_post(CRM_TOKEN_URL, [ 'headers' => ['Content-Type' => 'application/x-www-form-urlencoded'], 'body' => [ 'grant_type' => 'client_credentials', 'client_id' => CRM_CLIENT_ID, 'client_secret' => CRM_CLIENT_SECRET, 'scope' => CRM_SCOPES, ], 'timeout' => 15, ]);
if (is_wp_error($response)) { return $response; }
$body = json_decode(wp_remote_retrieve_body($response), true); $access_token = $body['access_token'];
// Cache for 50 minutes (new clients get 1-hour tokens) set_transient('crm_access_token', $access_token, 50 * MINUTE_IN_SECONDS);
return $access_token;}
/** * Upsert a single CRM product into WooCommerce, keyed on productCode → SKU */function crm_upsert_woo_product(array $crm_product){ $sku = $crm_product['productCode'] ?? null; if (!$sku) { return; // skip products without a stable code }
$product_id = wc_get_product_id_by_sku($sku); $product = $product_id ? wc_get_product($product_id) : new WC_Product_Simple();
$product->set_sku($sku); $product->set_name($crm_product['description'] ?? $sku); if (isset($crm_product['price'])) { $product->set_regular_price((string) $crm_product['price']); } $product->save();}
/** * Full catalog sync: page through GET /products/search using syncToken */function crm_sync_catalog_to_woo(){ $access_token = crm_get_access_token(); if (is_wp_error($access_token)) { return $access_token; }
$sync_token = null; do { $query = ['size' => 100]; if ($sync_token) { $query['syncToken'] = $sync_token; }
$response = wp_remote_get( CRM_API_BASE_URL . '/products/search?' . http_build_query($query), [ 'headers' => ['Authorization' => 'Bearer ' . $access_token], 'timeout' => 30, ] );
if (is_wp_error($response)) { return $response; }
$body = json_decode(wp_remote_retrieve_body($response), true); foreach (($body['data'] ?? []) as $crm_product) { crm_upsert_woo_product($crm_product); }
// syncToken is null on the last page $sync_token = $body['syncToken'] ?? null; } while ($sync_token);
return true;}
// Run the catalog sync on a daily WP-Cron scheduleadd_action('crm_daily_catalog_sync', 'crm_sync_catalog_to_woo');if (!wp_next_scheduled('crm_daily_catalog_sync')) { wp_schedule_event(time(), 'daily', 'crm_daily_catalog_sync');}Configuration
Section titled “Configuration”Add to wp-config.php:
define('CRM_CLIENT_ID', 'abc123xyz456def789ghi012');define('CRM_CLIENT_SECRET', 'your-secret-here');Postman Collection
Section titled “Postman Collection”Import the complete API collection into Postman for easy testing.
Download: M2M_API.postman_collection.json
- Import the collection into Postman
- Set collection variables:
apiUrl: Your API base URLcognitoDomain: Your Cognito domainclientId: Your M2M client IDclientSecret: Your M2M client secret
- Run “Get Access Token” (automatically saves token)
- Use other requests (automatically use saved token)
Included Requests
Section titled “Included Requests”-
Authentication
- Get Access Token
-
Client Management (Admin only)
- List M2M Clients
- Create M2M Client
- Revoke M2M Client
-
API Examples
- Get Products
- Search Products
- Get Product by ID
Next Steps
Section titled “Next Steps”- Getting Started - Quick start guide
- Authentication - OAuth flow details
- API Reference - Complete endpoint documentation
- Troubleshooting - Common issues and solutions