# Standardized HubSpot API Helpers

**Last Updated:** 2025-11-17  
**Status:** ✅ Production Ready

## Overview

This document describes the standardized HubSpot API integration system, including helper functions, configuration, logging, metrics, validation, and troubleshooting guides.

## Table of Contents

1. [Configuration](#configuration)
2. [API Helper Functions](#api-helper-functions)
3. [Logging System](#logging-system)
4. [Metrics Collection](#metrics-collection)
5. [Request Validation](#request-validation)
6. [Response Validation](#response-validation)
7. [Error Handling](#error-handling)
8. [Troubleshooting](#troubleshooting)

---

## Configuration

### Centralized Configuration (`v2/config/hubspot-config.php`)

All HubSpot API settings are centralized in a single configuration file.

#### API Token Management

```php
// Priority: Environment variable > Fallback (development only)
define('HUBSPOT_API_TOKEN', getenv('HUBSPOT_API_TOKEN') ?: 'fallback-token');

// Token validation
validateHubSpotTokenPermissions();
```

**Security Best Practices:**

- ✅ Always use `HUBSPOT_API_TOKEN` environment variable in production
- ✅ Never commit tokens to version control
- ✅ Token format validation: `pat-{region}-{hash}`
- ✅ Warnings logged when using fallback tokens

#### Portal ID and Form GUIDs

```php
define('HUBSPOT_PORTAL_ID', '145133546');

// Form GUIDs by endpoint
define('HUBSPOT_FORM_GUID_LEAD_CAPTURE', '9f9d4e35-d8d9-4283-93b6-1a789e0a1281');
define('HUBSPOT_FORM_GUID_TOOLS', 'a91b263c-7ca2-418b-b35c-9664c30e968b');
// ... see hubspot-config.php for complete list
```

#### Retry and Timeout Settings

```php
define('HUBSPOT_DEFAULT_MAX_RETRIES', 3);
define('HUBSPOT_DEFAULT_RETRY_DELAY', 1); // seconds
define('HUBSPOT_DEFAULT_TIMEOUT', 30); // seconds
define('HUBSPOT_DEFAULT_CONNECT_TIMEOUT', 10); // seconds
```

#### Helper Functions

```php
// Build Forms API URLs
buildHubSpotFormsAPISecureUrl($formGuid);
buildHubSpotFormsAPIPublicUrl($formGuid);

// Get form GUID by endpoint name
getHubSpotFormGuid('lead-capture'); // Returns form GUID

// Validate configuration
$validation = validateHubSpotConfig();
```

---

## API Helper Functions

### `makeHubSpotAPICall()`

Centralized function for all HubSpot API calls with automatic retry logic, rate limit handling, and error classification.

#### Signature

```php
makeHubSpotAPICall(
    string $url,
    string $method,
    array $headers,
    ?string $body,
    array $options = []
): array
```

#### Options

- `maxRetries` (int): Maximum retry attempts (default: 3)
- `initialRetryDelay` (int): Initial retry delay in seconds (default: 1)
- `timeout` (int): Request timeout in seconds (default: 30)
- `connectTimeout` (int): Connection timeout in seconds (default: 10)
- `userAgent` (string): Custom user agent
- `logPrefix` (string): Prefix for log messages
- `endpoint` (string): Endpoint name for metrics
- `apiType` (string): API type for metrics (e.g., 'HubSpot Forms', 'HubSpot CRM')

#### Return Value

```php
[
    'success' => bool,
    'data' => array|null,      // Decoded JSON response
    'error' => string|null,    // User-friendly error message
    'httpCode' => int,          // HTTP status code
    'attempts' => int,          // Number of attempts made
    'errorCategory' => string,  // 'network', 'transient', 'server', 'client'
    'correlationId' => string, // Request correlation ID
    'troubleshooting' => [     // Troubleshooting hints
        'hint' => string
    ]
]
```

#### Example Usage

```php
$result = makeHubSpotAPICall(
    'https://api.hubapi.com/crm/v3/objects/contacts',
    'POST',
    [
        'Authorization: Bearer ' . HUBSPOT_API_TOKEN,
        'Content-Type: application/json'
    ],
    json_encode($contactData),
    [
        'maxRetries' => 3,
        'endpoint' => 'CRM API v3',
        'apiType' => 'HubSpot CRM',
        'logPrefix' => 'CreateContact'
    ]
);

if ($result['success']) {
    $contactId = $result['data']['id'];
} else {
    // Handle error with correlation ID
    $correlationId = $result['correlationId'];
    $hint = $result['troubleshooting']['hint'];
}
```

### `createHubSpotSuccessResponse()`

Standardized success response format.

```php
$response = createHubSpotSuccessResponse(
    $data,
    'Contact created successfully',
    ['contact_id' => $contactId]
);
```

### `createHubSpotErrorResponse()`

Standardized error response format with correlation IDs and troubleshooting hints.

```php
$response = createHubSpotErrorResponse(
    'Failed to create contact',
    400,
    ['field' => 'email', 'reason' => 'Invalid format']
);
```

---

## Logging System

### Structured Logging (`v2/helpers/logger.php`)

All logging uses structured JSON format with log levels, correlation IDs, and context.

#### Log Levels

- `LOG_LEVEL_DEBUG`: Detailed debugging information
- `LOG_LEVEL_INFO`: General informational messages
- `LOG_LEVEL_WARN`: Warning messages (validation failures, rate limits)
- `LOG_LEVEL_ERROR`: Error messages (API failures, validation errors)
- `LOG_LEVEL_CRITICAL`: Critical errors (configuration issues, fatal errors)

#### Usage

```php
require_once __DIR__ . '/../helpers/logger.php';

// Basic logging
ordio_log(LOG_LEVEL_INFO, 'Contact created', [
    'contact_id' => $contactId,
    'email' => $email
]);

// With correlation ID (automatically added)
$correlationId = getCorrelationId();
ordio_log(LOG_LEVEL_ERROR, 'API call failed', [
    'endpoint' => 'Forms API v3',
    'http_code' => 429,
    'correlation_id' => $correlationId
]);
```

#### Log Format

```json
{
  "timestamp": "2025-11-17T10:30:45+00:00",
  "level": "ERROR",
  "message": "API call failed",
  "context": {
    "endpoint": "Forms API v3",
    "http_code": 429
  },
  "correlation_id": "req_67890abcdef",
  "endpoint": "/v2/api/lead-capture.php"
}
```

---

## Metrics Collection

### Metrics System (`v2/helpers/metrics.php`)

Automatic metrics collection for all API calls.

#### Metrics Tracked

- **API Call Counts**: Total, successful, failed
- **Latency**: Total, average, min, max per endpoint
- **Retry Attempts**: Total retries across all calls
- **Rate Limit Hits**: Number of 429 responses
- **Error Types**: Categorized by type (client_error, server_error, network_error, etc.)

#### Accessing Metrics

```php
// Get summary metrics
$metrics = getMetrics();

// Get detailed metrics with individual call details
$detailedMetrics = getMetrics(true);

// Access via HTTP endpoint
// GET /v2/api/metrics.php?details=true
```

#### Metrics Endpoint

**URL:** `/v2/api/metrics.php`

**Query Parameters:**

- `details` (boolean): Include individual API call details

**Response:**

```json
{
  "success": true,
  "data": {
    "total_calls": 150,
    "successful_calls": 145,
    "failed_calls": 5,
    "avg_latency": 0.234,
    "total_retries": 12,
    "rate_limit_hits": 2,
    "error_types": {
      "client_error": 3,
      "server_error": 2
    },
    "endpoints": {
      "Forms API v3": {
        "total_calls": 100,
        "successful_calls": 98,
        "avg_latency": 0.189,
        "max_latency": 0.456,
        "min_latency": 0.123
      }
    }
  }
}
```

---

## Request Validation

### Validation System (`v2/helpers/request-validator.php`)

All API endpoints validate incoming requests before processing.

#### Validation Rules

```php
$validationRules = [
    'email' => [
        'required' => true,
        'type' => 'email',
    ],
    'name' => [
        'required' => true,
        'type' => 'string',
        'min_length' => 1,
        'max_length' => 200,
    ],
    'phone' => [
        'required' => false,
        'type' => 'phone',
        'max_length' => 50,
    ],
    'format' => [
        'required' => true,
        'type' => 'string',
        'enum' => ['xlsx', 'csv', 'pdf'],
    ],
];
```

#### Validation Types

- `string`: String validation with length constraints
- `email`: Email format validation
- `url`: URL format validation
- `phone`: Phone number validation
- `integer`: Integer validation with min/max values

#### Usage

```php
$validationResult = validateRequest($input, $validationRules);

if (!$validationResult->valid) {
    http_response_code(400);
    echo json_encode([
        'success' => false,
        'error' => 'Validation failed',
        'errors' => array_map(function($e) { return $e->toArray(); }, $validationResult->errors)
    ]);
    exit;
}
```

#### Error Response Format

```json
{
  "success": false,
  "error": "Validation failed",
  "errors": [
    {
      "field": "email",
      "message": "Field 'email' is required",
      "code": "REQUIRED"
    },
    {
      "field": "name",
      "message": "Field 'name' must be at least 1 characters",
      "code": "TOO_SHORT"
    }
  ]
}
```

---

## Response Validation

### Response Validator (`v2/helpers/response-validator.php`)

Validates HubSpot API responses to ensure expected structure and data types.

#### Usage

```php
$validationResult = validateHubSpotResponse(
    $response,
    'Forms API v3',
    ['submittedAt', 'redirectUri']
);

if (!$validationResult->valid) {
    // Handle validation failure
}
```

---

## Error Handling

### Error Classification

Errors are automatically classified into categories:

- **network**: Network/connectivity errors (retryable)
- **transient**: Temporary errors like rate limits (retryable)
- **server**: Server errors (500-599, retryable)
- **client**: Client errors (400-499, generally not retryable)

### Retry Logic

- **Network errors**: Always retried
- **Rate limits (429)**: Retried with `Retry-After` header support
- **Server errors**: Retried with exponential backoff
- **Client errors**: Generally not retried (except 408, 409)

### Error Messages

User-friendly error messages are automatically generated:

```php
// Instead of: "HTTP 429 after 3 attempts: ..."
// Returns: "Rate limit exceeded. Please try again later."
```

### Correlation IDs

All error responses include correlation IDs for request tracking:

```json
{
  "success": false,
  "error": "Rate limit exceeded. Please try again later.",
  "correlationId": "req_67890abcdef",
  "troubleshooting": {
    "hint": "Wait before retrying. Check Retry-After header."
  }
}
```

---

## Troubleshooting

### Common Issues

#### 1. Rate Limit Errors (429)

**Symptoms:**

- HTTP 429 responses
- "Rate limit exceeded" error messages

**Solutions:**

- System automatically respects `Retry-After` header
- Exponential backoff implemented
- Check metrics endpoint for rate limit frequency
- Consider implementing request queuing for high-volume scenarios

#### 2. Authentication Errors (401)

**Symptoms:**

- HTTP 401 responses
- "Authentication failed" error messages

**Solutions:**

- Verify `HUBSPOT_API_TOKEN` environment variable is set
- Check token format: `pat-{region}-{hash}`
- Verify token has required scopes:
  - `crm.objects.contacts.write`
  - `crm.objects.contacts.read`
  - `forms-uploaded-forms`
  - `timeline.events.manage`

#### 3. Validation Failures

**Symptoms:**

- HTTP 400 responses with validation errors
- Field-specific error messages

**Solutions:**

- Check validation error details in response
- Verify required fields are present
- Check field types and length constraints
- Review validation rules in endpoint code

#### 4. Network Errors

**Symptoms:**

- cURL errors
- Timeout errors

**Solutions:**

- Check network connectivity
- Verify timeout settings are appropriate
- Review connection timeout vs request timeout
- Check firewall/proxy settings

### Debugging Tools

#### Health Check Endpoint

**URL:** `/v2/api/health.php`

Checks:

- HubSpot API token presence
- Portal ID configuration
- Log file permissions
- API connectivity

#### Metrics Endpoint

**URL:** `/v2/api/metrics.php`

Monitor:

- API call success rates
- Average latency
- Rate limit frequency
- Error distribution

#### Correlation IDs

All requests generate correlation IDs for tracking:

- Check logs for correlation ID
- Use correlation ID to trace request through system
- Correlation ID included in all error responses

### Log Analysis

#### Finding Errors

```bash
# Search for errors in logs
grep '"level":"ERROR"' v2/logs/application.log

# Find specific correlation ID
grep "req_67890abcdef" v2/logs/application.log

# Find rate limit issues
grep "429" v2/logs/application.log
```

#### Log Levels

- **DEBUG**: Detailed debugging (development only)
- **INFO**: Normal operations
- **WARN**: Validation failures, rate limits
- **ERROR**: API failures, validation errors
- **CRITICAL**: Configuration issues, fatal errors

---

## Best Practices

### 1. Always Use Standardized Functions

✅ **DO:**

```php
$result = makeHubSpotAPICall($url, $method, $headers, $body, $options);
$response = createHubSpotSuccessResponse($data);
```

❌ **DON'T:**

```php
// Manual cURL calls
$ch = curl_init();
// ... manual implementation
```

### 2. Validate Requests Early

✅ **DO:**

```php
$validationResult = validateRequest($input, $validationRules);
if (!$validationResult->valid) {
    // Return validation errors immediately
}
```

❌ **DON'T:**

```php
// Validate after API calls
// Process invalid data
```

### 3. Use Structured Logging

✅ **DO:**

```php
ordio_log(LOG_LEVEL_ERROR, 'API call failed', [
    'endpoint' => 'Forms API v3',
    'http_code' => 429
]);
```

❌ **DON'T:**

```php
error_log("API call failed: HTTP 429");
```

### 4. Handle Errors Gracefully

✅ **DO:**

```php
if (!$result['success']) {
    $correlationId = $result['correlationId'];
    $hint = $result['troubleshooting']['hint'];
    // Log with correlation ID
    // Return user-friendly error
}
```

❌ **DON'T:**

```php
// Expose internal error details
// Return raw HTTP error messages
```

### 5. Monitor Metrics

✅ **DO:**

- Regularly check `/v2/api/metrics.php`
- Monitor success rates and latency
- Track rate limit frequency
- Set up alerts for anomalies

---

## Migration Guide

### Migrating Existing Endpoints

1. **Include helper files:**

   ```php
   require_once __DIR__ . '/../config/hubspot-config.php';
   require_once __DIR__ . '/../config/hubspot-api-helpers.php';
   require_once __DIR__ . '/../helpers/request-validator.php';
   require_once __DIR__ . '/../helpers/logger.php';
   ```

2. **Replace manual cURL calls:**

   ```php
   // Before
   $ch = curl_init();
   curl_setopt($ch, CURLOPT_URL, $url);
   // ... manual setup

   // After
   $result = makeHubSpotAPICall($url, 'POST', $headers, $body, $options);
   ```

3. **Add request validation:**

   ```php
   $validationResult = validateRequest($input, $validationRules);
   if (!$validationResult->valid) {
       // Return validation errors
   }
   ```

4. **Use standardized responses:**

   ```php
   // Before
   echo json_encode(['success' => true, 'data' => $data]);

   // After
   echo json_encode(createHubSpotSuccessResponse($data));
   ```

5. **Update error handling:**

   ```php
   // Before
   echo json_encode(['error' => 'Something went wrong']);

   // After
   echo json_encode(createHubSpotErrorResponse('Something went wrong', 500));
   ```

---

## API Endpoints Using Standardized Helpers

All 10 HubSpot API endpoints now use standardized helpers:

1. `collect-lead.php` - Tools lead collection
2. `submit-template.php` - Template downloads
3. `lead-capture.php` - Two-step lead capture
4. `shiftops-hubspot.php` - ShiftOps reports
5. `addon-request.php` - Add-on pricing inquiries
6. `export-workdays.php` - Workdays export
7. `shiftops-nps.php` - NPS feedback
8. `webinar-registration.php` - Webinar registrations
9. `payroll-webinar-registration.php` - Payroll webinar registrations
10. `generate_excel.php` - Excel file generation

---

## Support

For issues or questions:

1. Check logs: `v2/logs/application.log`
2. Review metrics: `/v2/api/metrics.php`
3. Check health: `/v2/api/health.php`
4. Review correlation IDs in error responses
