Maverick Integration
Maverick Integration
The Maverick integration enables seamless bi-directional synchronization between Pulse and Maverick, a merchant services platform. This integration automatically syncs tickets, categories, and maintains data consistency across both systems.
Overview
What is Maverick?
Maverick is a merchant services platform that manages:
- Merchant boarding applications
- Support tickets
- Document management
- Merchant account data
Integration Capabilities
The Pulse-Maverick integration provides:
- Bi-directional Ticket Sync - Pull tickets from Maverick, push changes back
- Status Synchronization - Keep ticket statuses in sync across platforms
- Comment Synchronization - Sync responses and internal notes
- Category Mapping - Automatically map Maverick categories to Pulse types
- Conflict Prevention - Intelligent handling of concurrent modifications
- Document Transfer - Sync documents between systems
Configuration
Setting Up the Integration
const response = await fetch("/api/integrations/maverick/config", { method: "PUT", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, body: JSON.stringify({ enabled: true, apiUrl: "https://api.maverick.com", apiKey: "your-maverick-api-key", autoSync: true, syncIntervalMinutes: 15, syncDirection: "bidirectional", // 'inbound', 'outbound', or 'bidirectional' defaultAssigneeId: 123, // User to assign imported tickets to defaultTeamId: 5, // Team to assign imported tickets to }),});Configuration Options
Connection Settings
enabled(boolean) - Whether integration is activeapiUrl(string) - Maverick API base URLapiKey(string) - Maverick API authentication token
Sync Settings
autoSync(boolean) - Enable automatic background synchronizationsyncIntervalMinutes(number) - How often to sync (default: 15 minutes)syncDirection(string) - Sync direction:inbound- Only pull from Maverick to Pulseoutbound- Only push from Pulse to Maverickbidirectional- Sync both directions (default)
Assignment Defaults
defaultAssigneeId(number) - User to assign imported tickets todefaultTeamId(number) - Team to assign imported tickets to
Testing the Connection
Before enabling the integration, test the connection:
const response = await fetch('/api/integrations/maverick/config/test', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify({ apiUrl: 'https://api.maverick.com', apiKey: 'your-maverick-api-key' })});
// Returns:{ "success": true, "message": "Connection successful", "version": "1.2.3"}Ticket Synchronization
Inbound Sync (Maverick → Pulse)
Pull tickets from Maverick into Pulse:
const response = await fetch('/api/tickets/maverick', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify({ syncAll: false, // If true, sync all tickets; if false, sync only recent since: '2025-11-01T00:00:00Z', // Optional: only sync tickets modified after this time limit: 100 // Optional: limit number of tickets to sync })});
// Returns:{ "success": true, "created": 15, "updated": 23, "skipped": 7, // Locally modified tickets "errors": 2, "conflicts": 3, "totalProcessed": 47}How Inbound Sync Works
- Fetch Tickets - Retrieves tickets from Maverick API
- Map Data - Converts Maverick format to Pulse format
- Check Conflicts - Identifies locally modified tickets
- Upsert - Creates new tickets or updates existing ones
- Skip Local Changes - Preserves local modifications to prevent overwriting
- Update Sync Timestamp - Records last sync time
Outbound Sync (Pulse → Maverick)
Push changes from Pulse to Maverick automatically when:
- Ticket status changes
- Comments are added
- Priority is modified
Status Synchronization
// Automatically called when ticket is closed in Pulseawait syncTicketStatusToMaverick(ticketId, "closed");
// Automatically called when ticket is reopened in Pulseawait syncTicketStatusToMaverick(ticketId, "open");Comment Synchronization
// Automatically called when comment is added in Pulseawait syncCommentToMaverick(ticketId, commentId, { isInternal: false, // False = response, True = internal note body: "Your ticket has been resolved...",});Data Mapping
Status Mapping
| Maverick Status | Pulse Status |
|---|---|
new | open |
open | in_progress |
pending | waiting_customer |
waiting | waiting_customer |
resolved | resolved |
closed | closed |
Priority Mapping
| Maverick Priority | Pulse Priority |
|---|---|
critical | urgent |
high | high |
normal | medium |
low | low |
Category Mapping
Maverick categories are automatically mapped to Pulse ticket types:
// Maverick categories are fetched and mappedconst response = await fetch('/api/tickets/maverick/categories', { headers: { 'Authorization': `Bearer ${token}` }});
// Returns:{ "categories": [ { "id": 1, "name": "Technical Support", "maverickCategoryId": "tech-support", "pulseTicketTypeId": 5 }, { "id": 2, "name": "Billing Question", "maverickCategoryId": "billing", "pulseTicketTypeId": 3 } ]}Categories are auto-created in Pulse if they don’t exist.
Synced Ticket Properties
Tickets imported from Maverick include additional metadata:
External Source Tracking
{ "externalSource": "maverick", "externalTicketId": "MV-12345", // Original Maverick ticket ID "externalUrl": "https://maverick.com/tickets/12345", // Link back to Maverick "lastSyncedFromSource": "2025-11-01T14:30:00Z", // Last inbound sync "lastSyncedToSource": "2025-11-01T14:35:00Z", // Last outbound sync "syncStatus": "synced", // or "conflict", "error" "locallyModified": false // True if modified in Pulse after sync}Custom Fields
Maverick-specific data is stored in custom fields:
{ "customFields": { "maverickTicketId": "12345", // Maverick internal ID "maverickDbaId": "DBA-789", // Merchant DBA identifier "maverickCategoryId": "tech-support", // Original category "notifyMerchant": true, // Whether to notify merchant "attentionMerchant": false // Requires merchant attention flag }}Conflict Resolution
What Causes Conflicts?
Conflicts occur when a ticket is modified in both systems between syncs:
- Ticket updated in Maverick
- Same ticket updated in Pulse
- Sync attempts to pull Maverick changes
- Conflict detected
Conflict Prevention
The integration uses several strategies to prevent conflicts:
Local Modification Flag
When a ticket is modified in Pulse, locallyModified is set to true. During inbound sync, these tickets are skipped to preserve local changes.
// Ticket modified in Pulse{ "locallyModified": true, "lastSyncedFromSource": "2025-11-01T14:00:00Z"}
// During sync, this ticket is skipped{ "skipped": 1, "reason": "Locally modified, preventing overwrite"}Sync Timestamps
Separate timestamps track inbound vs. outbound syncs:
{ "lastSyncedFromSource": "2025-11-01T14:00:00Z", // Last pulled from Maverick "lastSyncedToSource": "2025-11-01T14:30:00Z" // Last pushed to Maverick}Sync Status
{ "syncStatus": "synced" // or "conflict", "error", "pending"}Manual Conflict Resolution
If a conflict occurs, you can manually resolve it:
// Force sync from Maverick (overwrite local changes)const response = await fetch("/api/tickets/1234/sync/force-inbound", { method: "POST", headers: { Authorization: `Bearer ${token}`, },});
// Force sync to Maverick (overwrite Maverick changes)const response = await fetch("/api/tickets/1234/sync/force-outbound", { method: "POST", headers: { Authorization: `Bearer ${token}`, },});
// Mark conflict as resolved without syncingconst response = await fetch("/api/tickets/1234/sync/mark-resolved", { method: "POST", headers: { Authorization: `Bearer ${token}`, },});Viewing Maverick Tickets
List Maverick Tickets in Pulse
const response = await fetch("/api/tickets/maverick", { headers: { Authorization: `Bearer ${token}`, },});
// Returns Pulse tickets that originated from MaverickGet Maverick Ticket Details
// Get full details from Maverick APIconst response = await fetch("/api/tickets/maverick/MV-12345", { headers: { Authorization: `Bearer ${token}`, },});
// Returns detailed Maverick ticket dataView Ticket Responses from Maverick
// Get all responses/comments from Maverickconst response = await fetch('/api/tickets/maverick/MV-12345/responses', { headers: { 'Authorization': `Bearer ${token}` }});
// Returns:{ "responses": [ { "id": "resp-123", "body": "We're investigating your issue...", "isInternal": false, "createdBy": "agent@company.com", "createdAt": "2025-11-01T14:15:00Z" } ]}Automatic Synchronization
Background Sync Job
When autoSync: true, the integration runs a background job at the configured interval:
Every 15 minutes:1. Fetch recent Maverick tickets (modified since last sync)2. Map and validate data3. Create/update tickets in Pulse4. Log sync results5. Update lastSyncedFromSource timestampSync Job Logs
View sync history:
const response = await fetch('/api/integrations/maverick/sync-history', { headers: { 'Authorization': `Bearer ${token}` }});
// Returns:{ "history": [ { "startedAt": "2025-11-01T14:00:00Z", "completedAt": "2025-11-01T14:02:35Z", "created": 5, "updated": 12, "skipped": 3, "errors": 0, "conflicts": 1 } ]}Manual Sync
Trigger a manual sync at any time:
const response = await fetch("/api/tickets/maverick/sync", { method: "POST", headers: { Authorization: `Bearer ${token}`, },});Document Transfer
Syncing Documents from Maverick
const response = await fetch('/api/tickets/maverick/1234/documents/sync', { method: 'POST', headers: { 'Authorization': `Bearer ${token}` }});
// Returns:{ "success": true, "documentsSynced": 3, "documents": [ { "id": "doc-123", "filename": "invoice.pdf", "url": "/api/files/tickets/1234/invoice.pdf" } ]}Uploading Documents to Maverick
const response = await fetch("/api/tickets/1234/documents/push-to-maverick", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, body: JSON.stringify({ documentId: 456, }),});Merchant Boarding Integration
The integration also supports syncing merchant boarding applications:
Sync Boarding Application
const response = await fetch("/api/applications/sync-from-maverick", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, body: JSON.stringify({ maverickApplicationId: "app-12345", }),});Webhook Integration
Configure Maverick webhooks to receive real-time updates:
Webhook Endpoint
POST /api/webhooks/maverick/ticketsWebhook Payload
{ "event": "ticket.updated", "ticketId": "MV-12345", "timestamp": "2025-11-01T14:30:00Z", "changes": { "status": "resolved", "assignedTo": "agent@maverick.com" }}Setting Up Webhooks in Maverick
- Log into Maverick admin panel
- Navigate to Settings → Webhooks
- Add new webhook:
- URL:
https://your-pulse-domain.com/api/webhooks/maverick/tickets - Events:
ticket.created,ticket.updated,ticket.comment_added - Secret: Generate a webhook secret
- URL:
- Save the webhook configuration
Configure Webhook Secret in Pulse
const response = await fetch("/api/integrations/maverick/config", { method: "PUT", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, body: JSON.stringify({ webhookSecret: "your-webhook-secret", }),});Monitoring and Troubleshooting
Sync Statistics
View overall sync health:
const response = await fetch('/api/integrations/maverick/stats', { headers: { 'Authorization': `Bearer ${token}` }});
// Returns:{ "totalTickets": 1250, "lastSyncAt": "2025-11-01T14:00:00Z", "syncStatus": "healthy", "conflictCount": 3, "errorCount": 0, "averageSyncDuration": 125 // seconds}Viewing Conflicts
const response = await fetch("/api/tickets?syncStatus=conflict", { headers: { Authorization: `Bearer ${token}`, },});Viewing Sync Errors
const response = await fetch('/api/integrations/maverick/errors', { headers: { 'Authorization': `Bearer ${token}` }});
// Returns:{ "errors": [ { "ticketId": "MV-12345", "error": "Invalid status value", "timestamp": "2025-11-01T14:30:00Z", "retryCount": 2 } ]}Retry Failed Syncs
const response = await fetch("/api/integrations/maverick/retry-errors", { method: "POST", headers: { Authorization: `Bearer ${token}`, },});Best Practices
1. Start with Inbound Only
Initially configure syncDirection: 'inbound' to safely test importing Maverick tickets before enabling bi-directional sync.
2. Set Appropriate Sync Interval
- High-volume systems: 30-60 minutes
- Medium-volume: 15-30 minutes
- Low-volume: 5-15 minutes
Shorter intervals increase API load but provide faster synchronization.
3. Monitor Conflicts Regularly
Check for conflicts daily and resolve them promptly to maintain data consistency.
4. Use Webhooks for Real-Time Updates
Configure Maverick webhooks for immediate notification of changes, reducing need for frequent polling.
5. Document Category Mappings
Maintain clear documentation of how Maverick categories map to Pulse ticket types.
6. Test Before Production
Use a staging environment to test the integration thoroughly before deploying to production.
7. Audit Sync Logs
Regularly review sync logs to identify patterns in errors or conflicts.
8. Handle Rate Limits
Maverick API has rate limits (10ms delay between requests). The client automatically handles this, but be aware when syncing large volumes.
Security Considerations
API Key Management
- Store Maverick API keys securely (encrypted at rest)
- Rotate API keys regularly (every 90 days recommended)
- Use separate API keys for staging and production
Data Access
- Integration requires
Permission.readSupportandPermission.writeSupport - Limit integration user permissions to only what’s necessary
- Audit integration access regularly
Webhook Security
- Always configure webhook secret
- Verify webhook signatures on incoming requests
- Use HTTPS endpoints only
Permissions
Maverick integration requires:
Permission.readSupport- View ticketsPermission.writeSupport- Create/update ticketsPermission.manageIntegrations- Configure integration settings
API Endpoints
Configuration
GET /api/integrations/maverick/config- Get configurationPUT /api/integrations/maverick/config- Update configurationPOST /api/integrations/maverick/config/test- Test connection
Tickets
GET /api/tickets/maverick- List Maverick tickets in PulsePOST /api/tickets/maverick- Sync tickets from MaverickGET /api/tickets/maverick/:ticketId- Get Maverick ticket detailsGET /api/tickets/maverick/:ticketId/responses- Get ticket responses
Synchronization
POST /api/tickets/maverick/sync- Trigger manual syncPOST /api/tickets/:id/sync/force-inbound- Force inbound syncPOST /api/tickets/:id/sync/force-outbound- Force outbound syncPOST /api/tickets/:id/sync/mark-resolved- Resolve conflict
Categories
GET /api/tickets/maverick/categories- Get category mappings
Monitoring
GET /api/integrations/maverick/stats- Get sync statisticsGET /api/integrations/maverick/sync-history- View sync historyGET /api/integrations/maverick/errors- View sync errorsPOST /api/integrations/maverick/retry-errors- Retry failed syncs
Webhooks
POST /api/webhooks/maverick/tickets- Webhook endpoint for Maverick
Database Schema
Integration Configuration
Stored in integration_settings table:
{ "provider": "maverick", "config": { "enabled": true, "apiUrl": "https://api.maverick.com", "apiKey": "encrypted-key", "autoSync": true, "syncIntervalMinutes": 15, "syncDirection": "bidirectional", "webhookSecret": "encrypted-secret" }}Ticket Fields
Maverick-related fields on tickets table:
externalSource- “maverick”externalTicketId- Maverick ticket IDexternalUrl- Link to Maverick ticketlastSyncedFromSource- Last inbound sync timestamplastSyncedToSource- Last outbound sync timestampsyncStatus- Current sync statuslocallyModified- Local modification flagcustomFields- JSON with Maverick metadata
Limitations
Rate Limiting
- Maverick API: 10ms delay between requests enforced
- Bulk operations may take time with large datasets
Data Mapping
- Some Maverick fields may not have direct Pulse equivalents
- Custom fields handle unmapped data
- Complex status workflows may require manual mapping
Real-Time Sync
- Background sync has configured interval (not instant)
- Webhooks provide near-real-time updates
- Some delay expected between systems
Troubleshooting
Tickets not syncing
- Check integration enabled - Verify
enabled: true - Test connection - Use test endpoint to verify API key
- Review sync logs - Check for errors in sync history
- Verify permissions - Ensure Maverick API key has proper permissions
Duplicate tickets
- Check external ID tracking - Ensure
externalTicketIdis set correctly - Review upsert logic - Verify tickets are being updated, not created
- Audit sync logs - Look for multiple creates for same ticket
Status not syncing correctly
- Review status mapping - Verify status values map correctly
- Check bidirectional setting - Ensure outbound sync is enabled
- Test manually - Try manual status update to test sync
Conflicts persisting
- Determine source of truth - Decide which system should win
- Force sync - Use force-inbound or force-outbound endpoints
- Clear local modified flag - Reset conflict state if needed
Related Documentation
- Tickets API - Core ticket management
- Automation Rules - Automate ticket workflows
- Webhooks - General webhook configuration
- Integrations - Other integration options