API Reference Guide

Complete REST API documentation for the CRM platform

Last Updated: October 27, 2025


Table of Contents

  1. Overview
  2. Authentication
  3. Base URL
  4. Common Headers
  5. Error Codes
  6. Rate Limiting
  7. Public Endpoints
  8. Contact Endpoints
  9. Campaign Endpoints
  10. Distribution List Endpoints
  11. Email Template Endpoints
  12. Tag Endpoints
  13. Analytics Endpoints

Overview

The CRM platform provides a RESTful API with 30+ endpoints for managing contacts, campaigns, distribution lists, and email templates. The API uses JSON for request and response bodies.

API Characteristics

Versioning

Current version: v1 (no version prefix in URLs)


Authentication

Overview

All authenticated endpoints require a valid JWT token obtained from Amazon Cognito. Public endpoints (subscription, unsubscribe) do not require authentication.

Obtaining Tokens

Step 1: Authenticate with Cognito

// Using Amazon Cognito Identity SDK
const authenticationData = {
  Username: 'user@example.com',
  Password: 'password123'
};

const authenticationDetails = new AmazonCognitoIdentity.AuthenticationDetails(authenticationData);

const poolData = {
  UserPoolId: 'ca-central-1_KY0FKXuer',
  ClientId: '65ejgncojr16d12d3nktdmg93'
};

const userPool = new AmazonCognitoIdentity.CognitoUserPool(poolData);

const userData = {
  Username: 'user@example.com',
  Pool: userPool
};

const cognitoUser = new AmazonCognitoIdentity.CognitoUser(userData);

cognitoUser.authenticateUser(authenticationDetails, {
  onSuccess: function(result) {
    const idToken = result.getIdToken().getJwtToken();
    console.log('Token:', idToken);
  },
  onFailure: function(err) {
    console.error('Authentication failed:', err);
  }
});

Step 2: Include Token in Requests

fetch('https://zqkprqp60h.execute-api.ca-central-1.amazonaws.com/Prod/contacts', {
  method: 'GET',
  headers: {
    'Authorization': `Bearer ${idToken}`,
    'Content-Type': 'application/json'
  }
});

Token Format

JWT tokens include: - sub: User ID (UUID) - email: User email address - custom:tenantId: Tenant ID for multi-tenancy - custom:organizationName: Organization name - exp: Expiration timestamp (1 hour)

Token Refresh

Tokens expire after 1 hour. Use the refresh token to obtain new tokens without re-authentication.


Base URL

Production: https://zqkprqp60h.execute-api.ca-central-1.amazonaws.com/Prod

Replace with your actual API Gateway URL from AWS deployment.

Example:

https://abc123xyz.execute-api.ca-central-1.amazonaws.com/Prod

Common Headers

Request Headers

Header Required Description
Authorization Yes (except public) Bearer token from Cognito
Content-Type Yes application/json
Accept No application/json

Response Headers

Header Description
Content-Type application/json
Access-Control-Allow-Origin * (CORS enabled)
Access-Control-Allow-Methods GET, POST, PUT, DELETE, OPTIONS

Error Codes

HTTP Status Codes

Code Meaning Description
200 OK Request successful
201 Created Resource created successfully
400 Bad Request Invalid request parameters
401 Unauthorized Missing or invalid authentication
403 Forbidden Insufficient permissions
404 Not Found Resource not found
500 Internal Server Error Server error occurred

Error Response Format

{
  "error": "Error message describing what went wrong"
}

Common Errors

401 Unauthorized:

{
  "error": "Unauthorized - Missing or invalid token"
}

400 Bad Request:

{
  "error": "Missing required field: email"
}

404 Not Found:

{
  "error": "Contact not found"
}

500 Internal Server Error:

{
  "error": "Failed to process request"
}

Rate Limiting

Current Limits

Best Practices


Public Endpoints

Subscribe to List

Endpoint: POST /public/subscribe

Authentication: None required

Description: Add a new contact to a distribution list without authentication.

Request Body:

{
  "email": "user@example.com",
  "listId": "550e8400-e29b-41d4-a716-446655440000",
  "firstName": "John",
  "lastName": "Doe"
}

Parameters:

Field Type Required Description
email string Yes Valid email address
listId string Yes Distribution list UUID
firstName string No Contact's first name
lastName string No Contact's last name

Success Response (201):

{
  "message": "Successfully subscribed to the list",
  "contactId": "abc123-contact-id",
  "listId": "550e8400-e29b-41d4-a716-446655440000"
}

Error Response (400):

{
  "error": "Missing required field: email"
}

Example:

const response = await fetch('https://zqkprqp60h.execute-api.ca-central-1.amazonaws.com/Prod/public/subscribe', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    email: 'user@example.com',
    firstName: 'John',
    lastName: 'Doe',
    listId: '550e8400-e29b-41d4-a716-446655440000'
  })
});

const data = await response.json();
console.log(data);

Unsubscribe from List

Endpoint: GET /public/unsubscribe

Authentication: None required

Description: Remove a contact from a distribution list and mark as unsubscribed.

Query Parameters:

Parameter Type Required Description
email string Yes Contact email address
listId string Yes Distribution list UUID

URL Format:

GET /public/unsubscribe?email=user@example.com&listId=550e8400-e29b-41d4-a716-446655440000

Success Response (200):

{
  "message": "Successfully unsubscribed"
}

Error Response (400):

{
  "error": "Missing required parameter: email"
}

Example:

const email = 'user@example.com';
const listId = '550e8400-e29b-41d4-a716-446655440000';

const response = await fetch(
  `https://zqkprqp60h.execute-api.ca-central-1.amazonaws.com/Prod/public/unsubscribe?email=${email}&listId=${listId}`
);

const data = await response.json();
console.log(data);

Contact Endpoints

List Contacts

Endpoint: GET /contacts

Authentication: Required

Description: Retrieve all contacts for the authenticated tenant.

Success Response (200):

{
  "contacts": [
    {
      "contactId": "abc123",
      "email": "john@example.com",
      "firstName": "John",
      "lastName": "Doe",
      "organizationName": "Acme Corp",
      "status": "active",
      "tenantId": "tenant-123",
      "createdAt": "2025-01-15T10:30:00Z",
      "updatedAt": "2025-01-15T10:30:00Z"
    }
  ]
}

Example:

const response = await fetch('https://zqkprqp60h.execute-api.ca-central-1.amazonaws.com/Prod/contacts', {
  headers: {
    'Authorization': `Bearer ${token}`,
    'Content-Type': 'application/json'
  }
});

const data = await response.json();
console.log(data.contacts);

Get Contact

Endpoint: GET /contacts/{contactId}

Authentication: Required

Description: Retrieve a specific contact by ID.

Path Parameters:

Parameter Type Required Description
contactId string Yes Contact UUID

Success Response (200):

{
  "contactId": "abc123",
  "email": "john@example.com",
  "firstName": "John",
  "lastName": "Doe",
  "organizationName": "Acme Corp",
  "status": "active",
  "tenantId": "tenant-123",
  "createdAt": "2025-01-15T10:30:00Z",
  "updatedAt": "2025-01-15T10:30:00Z"
}

Error Response (404):

{
  "error": "Contact not found"
}

Create Contact

Endpoint: POST /contacts

Authentication: Required

Description: Create a new contact.

Request Body:

{
  "email": "john@example.com",
  "firstName": "John",
  "lastName": "Doe",
  "organizationName": "Acme Corp",
  "status": "active"
}

Parameters:

Field Type Required Description
email string Yes Valid email address
firstName string No First name
lastName string No Last name
organizationName string No Organization name
status string No Status (active, unsubscribed, bounced, pending)

Success Response (201):

{
  "message": "Contact created successfully",
  "contact": {
    "contactId": "abc123",
    "email": "john@example.com",
    "firstName": "John",
    "lastName": "Doe",
    "organizationName": "Acme Corp",
    "status": "active",
    "tenantId": "tenant-123",
    "createdAt": "2025-01-15T10:30:00Z"
  }
}

Example:

const response = await fetch('https://zqkprqp60h.execute-api.ca-central-1.amazonaws.com/Prod/contacts', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${token}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    email: 'john@example.com',
    firstName: 'John',
    lastName: 'Doe',
    organizationName: 'Acme Corp'
  })
});

const data = await response.json();
console.log(data.contact);

Update Contact

Endpoint: PUT /contacts/{contactId}

Authentication: Required

Description: Update an existing contact.

Path Parameters:

Parameter Type Required Description
contactId string Yes Contact UUID

Request Body:

{
  "firstName": "John",
  "lastName": "Doe",
  "organizationName": "New Company Inc",
  "status": "active"
}

Success Response (200):

{
  "message": "Contact updated successfully",
  "contact": {
    "contactId": "abc123",
    "email": "john@example.com",
    "firstName": "John",
    "lastName": "Doe",
    "organizationName": "New Company Inc",
    "status": "active",
    "updatedAt": "2025-01-15T11:45:00Z"
  }
}

Delete Contact

Endpoint: DELETE /contacts/{contactId}

Authentication: Required

Description: Delete a contact permanently.

Path Parameters:

Parameter Type Required Description
contactId string Yes Contact UUID

Success Response (200):

{
  "message": "Contact deleted successfully"
}

Example:

const response = await fetch(`https://zqkprqp60h.execute-api.ca-central-1.amazonaws.com/Prod/contacts/${contactId}`, {
  method: 'DELETE',
  headers: {
    'Authorization': `Bearer ${token}`,
    'Content-Type': 'application/json'
  }
});

const data = await response.json();
console.log(data.message);

Campaign Endpoints

List Campaigns

Endpoint: GET /campaigns

Authentication: Required

Description: Retrieve all campaigns for the authenticated tenant.

Success Response (200):

{
  "campaigns": [
    {
      "campaignId": "campaign-123",
      "name": "Monthly Newsletter - January 2025",
      "subject": "What's New This Month",
      "status": "sent",
      "sentCount": 250,
      "deliveredCount": 245,
      "openedCount": 120,
      "clickedCount": 45,
      "bouncedCount": 5,
      "createdAt": "2025-01-10T09:00:00Z",
      "sentAt": "2025-01-15T14:00:00Z"
    }
  ]
}

Get Campaign

Endpoint: GET /campaigns/{campaignId}

Authentication: Required

Description: Retrieve a specific campaign by ID.

Success Response (200):

{
  "campaignId": "campaign-123",
  "name": "Monthly Newsletter - January 2025",
  "subject": "What's New This Month",
  "htmlBody": "<html>...</html>",
  "status": "sent",
  "sentCount": 250,
  "deliveredCount": 245,
  "openedCount": 120,
  "clickedCount": 45,
  "bouncedCount": 5,
  "tenantId": "tenant-123",
  "createdAt": "2025-01-10T09:00:00Z",
  "sentAt": "2025-01-15T14:00:00Z"
}

Create Campaign

Endpoint: POST /campaigns

Authentication: Required

Description: Create a new email campaign.

Request Body:

{
  "name": "Monthly Newsletter - February 2025",
  "subject": "February Updates",
  "htmlBody": "<html><body><h1>Hello!</h1></body></html>"
}

Parameters:

Field Type Required Description
name string Yes Campaign name
subject string Yes Email subject line
htmlBody string Yes HTML email content

Success Response (201):

{
  "message": "Campaign created successfully",
  "campaign": {
    "campaignId": "campaign-456",
    "name": "Monthly Newsletter - February 2025",
    "subject": "February Updates",
    "status": "draft",
    "createdAt": "2025-02-01T10:00:00Z"
  }
}

Send Campaign

Endpoint: POST /campaigns/{campaignId}/send

Authentication: Required

Description: Send a campaign to a distribution list.

Path Parameters:

Parameter Type Required Description
campaignId string Yes Campaign UUID

Request Body:

{
  "listId": "list-123"
}

Success Response (200):

{
  "message": "Campaign sent successfully",
  "sentCount": 250
}

Example:

const response = await fetch(
  `https://zqkprqp60h.execute-api.ca-central-1.amazonaws.com/Prod/campaigns/${campaignId}/send`,
  {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${token}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      listId: 'list-123'
    })
  }
);

const data = await response.json();
console.log(data.message);

Clone Campaign

Endpoint: POST /campaigns/{campaignId}/clone

Authentication: Required

Description: Create a copy of an existing campaign.

Path Parameters:

Parameter Type Required Description
campaignId string Yes Campaign UUID to clone

Success Response (201):

{
  "message": "Campaign cloned successfully",
  "campaign": {
    "campaignId": "new-campaign-789",
    "name": "Monthly Newsletter - February 2025 (Copy)",
    "subject": "February Updates",
    "status": "draft",
    "createdAt": "2025-02-05T11:00:00Z"
  }
}

Delete Campaign

Endpoint: DELETE /campaigns/{campaignId}

Authentication: Required

Description: Delete a campaign permanently.

Success Response (200):

{
  "message": "Campaign deleted successfully"
}

Distribution List Endpoints

List Distribution Lists

Endpoint: GET /lists

Authentication: Required

Description: Retrieve all distribution lists for the authenticated tenant.

Success Response (200):

{
  "lists": [
    {
      "listId": "list-123",
      "name": "Monthly Newsletter Subscribers",
      "description": "Main newsletter distribution list",
      "memberCount": 250,
      "tenantId": "tenant-123",
      "createdAt": "2024-12-01T10:00:00Z"
    }
  ]
}

Get Distribution List

Endpoint: GET /lists/{listId}

Authentication: Required

Description: Retrieve a specific distribution list by ID.

Success Response (200):

{
  "listId": "list-123",
  "name": "Monthly Newsletter Subscribers",
  "description": "Main newsletter distribution list",
  "memberCount": 250,
  "tenantId": "tenant-123",
  "createdAt": "2024-12-01T10:00:00Z"
}

Create Distribution List

Endpoint: POST /lists

Authentication: Required

Description: Create a new distribution list.

Request Body:

{
  "name": "Product Launch List",
  "description": "Contacts interested in product launches"
}

Parameters:

Field Type Required Description
name string Yes List name
description string No List description

Success Response (201):

{
  "message": "List created successfully",
  "list": {
    "listId": "list-456",
    "name": "Product Launch List",
    "description": "Contacts interested in product launches",
    "memberCount": 0,
    "createdAt": "2025-02-01T10:00:00Z"
  }
}

Update Distribution List

Endpoint: PUT /lists/{listId}

Authentication: Required

Description: Update an existing distribution list.

Request Body:

{
  "name": "Updated List Name",
  "description": "Updated description"
}

Success Response (200):

{
  "message": "List updated successfully"
}

Delete Distribution List

Endpoint: DELETE /lists/{listId}

Authentication: Required

Description: Delete a distribution list permanently.

Success Response (200):

{
  "message": "List deleted successfully"
}

Get List Members

Endpoint: GET /lists/{listId}/members

Authentication: Required

Description: Retrieve all members of a distribution list.

Success Response (200):

{
  "members": [
    {
      "contactId": "contact-123",
      "email": "john@example.com",
      "firstName": "John",
      "lastName": "Doe",
      "status": "active",
      "addedAt": "2025-01-01T10:00:00Z"
    }
  ]
}

Add Contact to List

Endpoint: POST /lists/{listId}/members

Authentication: Required

Description: Add a contact to a distribution list.

Request Body:

{
  "contactId": "contact-123"
}

Success Response (201):

{
  "message": "Contact added to list successfully"
}

Remove Contact from List

Endpoint: DELETE /lists/{listId}/members/{contactId}

Authentication: Required

Description: Remove a contact from a distribution list.

Success Response (200):

{
  "message": "Contact removed from list successfully"
}

Duplicate Distribution List

Endpoint: POST /lists/{listId}/duplicate

Authentication: Required

Description: Create a copy of a distribution list including all members.

Success Response (201):

{
  "message": "List duplicated successfully",
  "list": {
    "listId": "new-list-789",
    "name": "Monthly Newsletter Subscribers (Copy)",
    "memberCount": 250,
    "createdAt": "2025-02-05T11:00:00Z"
  }
}

Email Template Endpoints

List Templates

Endpoint: GET /templates

Authentication: Required

Description: Retrieve all email templates for the authenticated tenant.

Success Response (200):

{
  "templates": [
    {
      "templateId": "template-123",
      "name": "Monthly Newsletter Template",
      "subject": "{{month}} Newsletter",
      "tenantId": "tenant-123",
      "createdAt": "2024-11-15T10:00:00Z"
    }
  ]
}

Get Template

Endpoint: GET /templates/{templateId}

Authentication: Required

Description: Retrieve a specific template by ID.

Success Response (200):

{
  "templateId": "template-123",
  "name": "Monthly Newsletter Template",
  "subject": "{{month}} Newsletter",
  "htmlBody": "<html>...</html>",
  "tenantId": "tenant-123",
  "createdAt": "2024-11-15T10:00:00Z"
}

Create Template

Endpoint: POST /templates

Authentication: Required

Description: Create a new email template.

Request Body:

{
  "name": "Product Announcement Template",
  "subject": "Introducing {{productName}}",
  "htmlBody": "<html><body><h1>Check out {{productName}}</h1></body></html>"
}

Success Response (201):

{
  "message": "Template created successfully",
  "template": {
    "templateId": "template-456",
    "name": "Product Announcement Template",
    "createdAt": "2025-02-01T10:00:00Z"
  }
}

Update Template

Endpoint: PUT /templates/{templateId}

Authentication: Required

Description: Update an existing template.

Request Body:

{
  "name": "Updated Template Name",
  "subject": "Updated Subject",
  "htmlBody": "<html>...</html>"
}

Success Response (200):

{
  "message": "Template updated successfully"
}

Delete Template

Endpoint: DELETE /templates/{templateId}

Authentication: Required

Description: Delete a template permanently.

Success Response (200):

{
  "message": "Template deleted successfully"
}

Tag Endpoints

List Contact Tags

Endpoint: GET /contacts/{contactId}/tags

Authentication: Required

Description: Retrieve all tags for a specific contact.

Success Response (200):

{
  "tags": [
    {
      "tagId": "tag-123",
      "tagName": "VIP Customer",
      "color": "purple",
      "createdAt": "2025-01-10T10:00:00Z"
    },
    {
      "tagId": "tag-456",
      "tagName": "Newsletter Subscriber",
      "color": "blue",
      "createdAt": "2025-01-15T11:00:00Z"
    }
  ]
}

Add Tag to Contact

Endpoint: POST /contacts/{contactId}/tags

Authentication: Required

Description: Add a tag to a contact.

Request Body:

{
  "tagName": "VIP Customer",
  "color": "purple"
}

Parameters:

Field Type Required Description
tagName string Yes Tag name
color string Yes Color (red, orange, yellow, green, blue, purple, pink, gray)

Success Response (201):

{
  "message": "Tag added successfully",
  "tag": {
    "tagId": "tag-789",
    "tagName": "VIP Customer",
    "color": "purple",
    "contactId": "contact-123",
    "createdAt": "2025-02-01T10:00:00Z"
  }
}

Remove Tag from Contact

Endpoint: DELETE /contacts/{contactId}/tags/{tagId}

Authentication: Required

Description: Remove a tag from a contact.

Success Response (200):

{
  "message": "Tag removed successfully"
}

Analytics Endpoints

Get Dashboard Statistics

Endpoint: GET /analytics/dashboard

Authentication: Required

Description: Retrieve overall platform statistics for the dashboard.

Success Response (200):

{
  "totalCampaigns": 25,
  "totalEmailsSent": 5000,
  "avgDeliveryRate": 98.5,
  "totalContacts": 850,
  "campaignsByStatus": {
    "draft": 5,
    "sent": 18,
    "scheduled": 2
  },
  "deliveryStats": {
    "delivered": 4925,
    "bounced": 75
  },
  "contactGrowth": [
    {"month": "2024-11", "count": 500},
    {"month": "2024-12", "count": 650},
    {"month": "2025-01", "count": 850}
  ],
  "topCampaigns": [
    {
      "name": "January Newsletter",
      "openRate": 45.2,
      "clickRate": 12.5
    }
  ]
}

Complete Endpoint List

Public Endpoints (No Auth)

Contact Management

Campaign Management

Distribution Lists

Email Templates

Contact Tags

Analytics


Code Examples

Complete JavaScript Client

class CRMClient {
  constructor(apiUrl, token) {
    this.apiUrl = apiUrl;
    this.token = token;
  }

  async request(endpoint, options = {}) {
    const url = `${this.apiUrl}${endpoint}`;
    const headers = {
      'Content-Type': 'application/json',
      ...options.headers
    };

    if (this.token) {
      headers['Authorization'] = `Bearer ${this.token}`;
    }

    const response = await fetch(url, {
      ...options,
      headers
    });

    if (!response.ok) {
      const error = await response.json();
      throw new Error(error.error || 'Request failed');
    }

    return response.json();
  }

  // Contacts
  async listContacts() {
    return this.request('/contacts');
  }

  async getContact(contactId) {
    return this.request(`/contacts/${contactId}`);
  }

  async createContact(contact) {
    return this.request('/contacts', {
      method: 'POST',
      body: JSON.stringify(contact)
    });
  }

  async updateContact(contactId, updates) {
    return this.request(`/contacts/${contactId}`, {
      method: 'PUT',
      body: JSON.stringify(updates)
    });
  }

  async deleteContact(contactId) {
    return this.request(`/contacts/${contactId}`, {
      method: 'DELETE'
    });
  }

  // Campaigns
  async listCampaigns() {
    return this.request('/campaigns');
  }

  async createCampaign(campaign) {
    return this.request('/campaigns', {
      method: 'POST',
      body: JSON.stringify(campaign)
    });
  }

  async sendCampaign(campaignId, listId) {
    return this.request(`/campaigns/${campaignId}/send`, {
      method: 'POST',
      body: JSON.stringify({ listId })
    });
  }

  // Lists
  async listDistributionLists() {
    return this.request('/lists');
  }

  async createList(list) {
    return this.request('/lists', {
      method: 'POST',
      body: JSON.stringify(list)
    });
  }

  async addToList(listId, contactId) {
    return this.request(`/lists/${listId}/members`, {
      method: 'POST',
      body: JSON.stringify({ contactId })
    });
  }

  // Tags
  async addTag(contactId, tagName, color) {
    return this.request(`/contacts/${contactId}/tags`, {
      method: 'POST',
      body: JSON.stringify({ tagName, color })
    });
  }

  async removeTag(contactId, tagId) {
    return this.request(`/contacts/${contactId}/tags/${tagId}`, {
      method: 'DELETE'
    });
  }

  // Analytics
  async getDashboardStats() {
    return this.request('/analytics/dashboard');
  }
}

// Usage
const client = new CRMClient('https://zqkprqp60h.execute-api.ca-central-1.amazonaws.com/Prod', 'your-jwt-token');

// Create a contact
const contact = await client.createContact({
  email: 'john@example.com',
  firstName: 'John',
  lastName: 'Doe'
});

// Create and send a campaign
const campaign = await client.createCampaign({
  name: 'Welcome Email',
  subject: 'Welcome to our platform!',
  htmlBody: '<h1>Welcome!</h1>'
});

await client.sendCampaign(campaign.campaign.campaignId, 'list-id');

Postman Collection

Import this JSON into Postman for easy API testing:

{
  "info": {
    "name": "CRM Platform API",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "auth": {
    "type": "bearer",
    "bearer": [
      {
        "key": "token",
        "value": "{{jwt_token}}",
        "type": "string"
      }
    ]
  },
  "variable": [
    {
      "key": "base_url",
      "value": "https://zqkprqp60h.execute-api.ca-central-1.amazonaws.com/Prod"
    },
    {
      "key": "jwt_token",
      "value": "your-token-here"
    }
  ],
  "item": [
    {
      "name": "Contacts",
      "item": [
        {
          "name": "List Contacts",
          "request": {
            "method": "GET",
            "url": "{{base_url}}/contacts"
          }
        },
        {
          "name": "Create Contact",
          "request": {
            "method": "POST",
            "url": "{{base_url}}/contacts",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"test@example.com\",\n  \"firstName\": \"Test\",\n  \"lastName\": \"User\"\n}"
            }
          }
        }
      ]
    }
  ]
}

Best Practices

1. Error Handling

Always handle errors gracefully:

try {
  const response = await fetch('https://zqkprqp60h.execute-api.ca-central-1.amazonaws.com/Prod/contacts', {
    headers: { 'Authorization': `Bearer ${token}` }
  });

  if (!response.ok) {
    const error = await response.json();
    console.error('API Error:', error.error);
    // Show user-friendly message
    return;
  }

  const data = await response.json();
  // Process data
} catch (error) {
  console.error('Network Error:', error);
  // Show connection error message
}

2. Token Management

Store and refresh tokens properly:

// Store token securely
localStorage.setItem('crm_token', token);

// Check expiration before requests
function isTokenExpired(token) {
  const payload = JSON.parse(atob(token.split('.')[1]));
  return Date.now() >= payload.exp * 1000;
}

// Refresh if needed
if (isTokenExpired(token)) {
  token = await refreshToken();
}

3. Request Optimization

Batch requests when possible:

// Instead of multiple sequential requests
const contacts = await Promise.all([
  client.getContact('id1'),
  client.getContact('id2'),
  client.getContact('id3')
]);

4. CORS Handling

Ensure CORS is properly configured:

// API Gateway CORS configuration
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization

Support

For API issues: - Check CloudWatch Logs: /aws/lambda/crm-*-dev - Verify authentication tokens - Review request/response in browser DevTools - See troubleshooting in COMPLETE_FEATURE_GUIDE.md


Document Version: 1.0 Last Updated: October 27, 2025 API Version: v1