Developer Guide - Getting Started
SkunkCRM is built with extensibility in mind. This guide will help you get started with developing extensions, integrations, and customizations for SkunkCRM.
Architecture Overview
SkunkCRM follows WordPress plugin best practices and provides multiple extension points:
- Hooks & Filters: Over 100 action and filter hooks for maximum extensibility
- REST API: Comprehensive REST API for integrations
- Database Access: Direct database access with custom tables
- Class System: Object-oriented architecture with dependency injection
Quick Start
1. Basic Hook Usage
The simplest way to extend SkunkCRM is through WordPress hooks:
functions.phpphp1// Add to your theme's functions.php or custom plugin 2 3// Log when contacts are created 4add_action('skunkcrm_after_contact_create', function($contact_id, $data) { 5 error_log("New contact created: {$data['name']} (ID: $contact_id)"); 6}); 7 8// Modify contact data before saving 9add_filter('skunkcrm_contact_data_before_save', function($data, $contact_id) { 10 // Auto-format phone numbers 11 if (isset($data['phone'])) { 12 $data['phone'] = preg_replace('/[^0-9]/', '', $data['phone']); 13 $data['phone'] = preg_replace('/(\d{3})(\d{3})(\d{4})/', '($1) $2-$3', $data['phone']); 14 } 15 return $data; 16}, 10, 2); 17 18// Add custom contact statuses 19add_filter('skunkcrm_contact_status_options', function($statuses) { 20 $statuses['vip'] = 'VIP Customer'; 21 $statuses['partner'] = 'Business Partner'; 22 return $statuses; 23});
2. REST API Integration
Powerful REST API Ready for Integration
SkunkCRM provides a comprehensive REST API designed for seamless integrations and external access. Build powerful integrations with full CRUD operations, secure authentication, and real-time webhook support.
Complete control over contacts, deals, and activities
WordPress Application Passwords with permission controls
Built-in protection and performance optimization
Clean, RESTful endpoints with consistent responses
Real-time integrations and event notifications
Get started with examples, authentication guides, and interactive endpoint testing.
3. Custom Plugin Structure
Create a structured plugin to extend SkunkCRM:
functions.phpphp1<?php 2/** 3 * Plugin Name: SkunkCRM Extension 4 * Description: Custom extensions for SkunkCRM 5 * Version: 1.0.0 6 */ 7 8// Prevent direct access 9if (!defined('ABSPATH')) { 10 exit; 11} 12 13class SkunkCRM_Extension { 14 15 public function __construct() { 16 add_action('plugins_loaded', [$this, 'init']); 17 } 18 19 public function init() { 20 // Ensure SkunkCRM is active 21 if (!class_exists('SkunkCRM')) { 22 add_action('admin_notices', [$this, 'missing_skunkcrm_notice']); 23 return; 24 } 25 26 // Initialize your extensions 27 $this->init_hooks(); 28 $this->init_rest_api(); 29 $this->init_admin_pages(); 30 } 31 32 private function init_hooks() { 33 // Contact management hooks 34 add_action('skunkcrm_after_contact_create', [$this, 'on_contact_created'], 10, 2); 35 add_filter('skunkcrm_contact_data_before_save', [$this, 'modify_contact_data'], 10, 2); 36 37 // Automation hooks 38 add_filter('skunkcrm_automation_action_types', [$this, 'add_custom_actions']); 39 add_action('skunkcrm_before_automation_execute', [$this, 'log_automation'], 10, 3); 40 } 41 42 private function init_rest_api() { 43 add_action('rest_api_init', [$this, 'register_custom_endpoints']); 44 } 45 46 private function init_admin_pages() { 47 add_action('admin_menu', [$this, 'add_admin_menu']); 48 } 49 50 public function on_contact_created($contact_id, $data) { 51 // Custom logic when contact is created 52 $this->sync_to_external_system($contact_id, $data); 53 } 54 55 public function modify_contact_data($data, $contact_id) { 56 // Add custom validation or data transformation 57 if (isset($data['email']) && !empty($data['email'])) { 58 $data['email_domain'] = substr(strrchr($data['email'], "@"), 1); 59 } 60 return $data; 61 } 62 63 public function add_custom_actions($actions) { 64 $actions['send_to_crm'] = 'Send to External CRM'; 65 $actions['create_calendar_event'] = 'Create Calendar Event'; 66 return $actions; 67 } 68 69 public function register_custom_endpoints() { 70 register_rest_route('skunkcrm-extension/v1', '/sync', [ 71 'methods' => 'POST', 72 'callback' => [$this, 'sync_endpoint'], 73 'permission_callback' => [$this, 'check_permissions'] 74 ]); 75 } 76 77 public function sync_endpoint($request) { 78 // Custom API endpoint 79 $contact_id = $request->get_param('contact_id'); 80 81 // Sync logic here 82 $result = $this->sync_contact($contact_id); 83 84 return new WP_REST_Response($result, 200); 85 } 86 87 public function missing_skunkcrm_notice() { 88 echo '<div class="notice notice-error"><p>SkunkCRM Extension requires SkunkCRM to be installed and activated.</p></div>'; 89 } 90 91 private function sync_to_external_system($contact_id, $data) { 92 // Implement external system integration 93 } 94 95 private function sync_contact($contact_id) { 96 // Implement sync logic 97 return ['success' => true, 'contact_id' => $contact_id]; 98 } 99 100 public function check_permissions() { 101 return current_user_can('manage_options'); 102 } 103} 104 105new SkunkCRM_Extension();
Common Extension Patterns
1. External System Integration
functions.phpphp1class SkunkCRM_Mailchimp_Integration { 2 3 private $api_key; 4 private $list_id; 5 6 public function __construct($api_key, $list_id) { 7 $this->api_key = $api_key; 8 $this->list_id = $list_id; 9 10 // Sync contacts to Mailchimp when created/updated 11 add_action('skunkcrm_after_contact_create', [$this, 'sync_to_mailchimp'], 10, 2); 12 add_action('skunkcrm_after_contact_update', [$this, 'update_in_mailchimp'], 10, 3); 13 add_action('skunkcrm_after_contact_delete', [$this, 'remove_from_mailchimp'], 10, 2); 14 } 15 16 public function sync_to_mailchimp($contact_id, $data) { 17 if (empty($data['email'])) { 18 return; 19 } 20 21 $mailchimp_data = [ 22 'email_address' => $data['email'], 23 'status' => 'subscribed', 24 'merge_fields' => [ 25 'FNAME' => $this->get_first_name($data['name']), 26 'LNAME' => $this->get_last_name($data['name']), 27 'COMPANY' => $data['company'] ?? '', 28 'PHONE' => $data['phone'] ?? '' 29 ], 30 'tags' => ['skunkcrm'] 31 ]; 32 33 $this->mailchimp_api_call("lists/{$this->list_id}/members", $mailchimp_data); 34 } 35 36 public function update_in_mailchimp($contact_id, $new_data, $old_data) { 37 // Update subscriber in Mailchimp 38 if (!empty($new_data['email'])) { 39 $subscriber_hash = md5(strtolower($new_data['email'])); 40 $this->mailchimp_api_call( 41 "lists/{$this->list_id}/members/{$subscriber_hash}", 42 ['merge_fields' => ['FNAME' => $this->get_first_name($new_data['name'])]], 43 'PATCH' 44 ); 45 } 46 } 47 48 private function mailchimp_api_call($endpoint, $data, $method = 'POST') { 49 $url = "https://us1.api.mailchimp.com/3.0/{$endpoint}"; 50 51 return wp_remote_request($url, [ 52 'method' => $method, 53 'headers' => [ 54 'Authorization' => 'Basic ' . base64_encode('user:' . $this->api_key), 55 'Content-Type' => 'application/json' 56 ], 57 'body' => json_encode($data) 58 ]); 59 } 60}
2. Custom Automation Actions
functions.phpphp1class SkunkCRM_Custom_Automation_Actions { 2 3 public function __construct() { 4 add_filter('skunkcrm_automation_action_types', [$this, 'add_action_types']); 5 add_filter('skunkcrm_custom_action_type', [$this, 'handle_custom_action'], 10, 4); 6 } 7 8 public function add_action_types($actions) { 9 $actions['send_sms'] = 'Send SMS'; 10 $actions['create_deal'] = 'Create Deal'; 11 $actions['assign_to_user'] = 'Assign to User'; 12 $actions['add_to_sequence'] = 'Add to Email Sequence'; 13 return $actions; 14 } 15 16 public function handle_custom_action($result, $action_type, $config, $contact) { 17 switch ($action_type) { 18 case 'send_sms': 19 return $this->send_sms($config, $contact); 20 case 'create_deal': 21 return $this->create_deal($config, $contact); 22 case 'assign_to_user': 23 return $this->assign_to_user($config, $contact); 24 case 'add_to_sequence': 25 return $this->add_to_sequence($config, $contact); 26 } 27 return $result; 28 } 29 30 private function send_sms($config, $contact) { 31 if (empty($contact['phone'])) { 32 return ['success' => false, 'error' => 'No phone number']; 33 } 34 35 $message = str_replace('{name}', $contact['name'], $config['message']); 36 37 // Use Twilio, TextMagic, or other SMS service 38 return $this->sms_service_send($contact['phone'], $message); 39 } 40 41 private function create_deal($config, $contact) { 42 global $wpdb; 43 44 $deal_data = [ 45 'title' => str_replace('{contact_name}', $contact['name'], $config['title']), 46 'amount' => $config['amount'] ?? 0, 47 'stage' => $config['stage'] ?? 'prospecting', 48 'contact_id' => $contact['id'], 49 'assigned_to' => $config['assigned_to'] ?? get_current_user_id(), 50 'created_at' => current_time('mysql') 51 ]; 52 53 $result = $wpdb->insert( 54 $wpdb->prefix . 'skunk_deals', 55 $deal_data, 56 ['%s', '%f', '%s', '%d', '%d', '%s'] 57 ); 58 59 return [ 60 'success' => $result !== false, 61 'deal_id' => $wpdb->insert_id 62 ]; 63 } 64}
3. Contact Data Enhancement
functions.phpphp1class SkunkCRM_Data_Enrichment { 2 3 public function __construct() { 4 add_filter('skunkcrm_contact_data_before_save', [$this, 'enrich_contact_data'], 20, 2); 5 add_action('skunkcrm_after_contact_create', [$this, 'background_enrichment'], 10, 2); 6 } 7 8 public function enrich_contact_data($data, $contact_id) { 9 // Real-time enrichment for critical fields 10 if (isset($data['email']) && !empty($data['email'])) { 11 $domain = substr(strrchr($data['email'], "@"), 1); 12 $data['email_domain'] = $domain; 13 14 // Check if it's a business email 15 $personal_domains = ['gmail.com', 'yahoo.com', 'hotmail.com', 'outlook.com']; 16 $data['is_business_email'] = !in_array($domain, $personal_domains); 17 } 18 19 // Auto-format phone numbers 20 if (isset($data['phone']) && !empty($data['phone'])) { 21 $data['phone'] = $this->format_phone_number($data['phone']); 22 } 23 24 // Capitalize names 25 if (isset($data['name'])) { 26 $data['name'] = ucwords(strtolower($data['name'])); 27 } 28 29 return $data; 30 } 31 32 public function background_enrichment($contact_id, $data) { 33 // Schedule background enrichment 34 wp_schedule_single_event(time() + 60, 'skunkcrm_enrich_contact', [$contact_id]); 35 } 36 37 private function format_phone_number($phone) { 38 // Remove all non-numeric characters 39 $phone = preg_replace('/[^0-9]/', '', $phone); 40 41 // Format as (XXX) XXX-XXXX for US numbers 42 if (strlen($phone) === 10) { 43 return preg_replace('/(\d{3})(\d{3})(\d{4})/', '($1) $2-$3', $phone); 44 } 45 46 return $phone; 47 } 48} 49 50// Register the enrichment hook 51add_action('skunkcrm_enrich_contact', function($contact_id) { 52 // Fetch additional data from external APIs 53 $contact = SkunkCRM_Contacts::get_by_id($contact_id); 54 55 if ($contact && !empty($contact['email'])) { 56 // Example: Enrich with Clearbit, FullContact, etc. 57 $enriched_data = fetch_from_clearbit($contact['email']); 58 59 if ($enriched_data) { 60 SkunkCRM_Contacts::update($contact_id, [ 61 'company' => $enriched_data['company'] ?? $contact['company'], 62 'title' => $enriched_data['title'] ?? null, 63 'linkedin' => $enriched_data['linkedin'] ?? null 64 ]); 65 } 66 } 67});
Development Guidelines
1. Hook Usage Best Practices
- Use appropriate priority: Default is 10, use higher numbers to run later
- Check for plugin existence: Always verify SkunkCRM is active
- Handle errors gracefully: Don't break core functionality
- Follow WordPress standards: Use WordPress coding standards
2. Performance Considerations
- Avoid heavy operations in hooks: Use background processing for intensive tasks
- Cache external API calls: Implement proper caching mechanisms
- Use transients: For temporary data storage
- Database optimization: Use proper indexes and queries
3. Security Best Practices
- Sanitize all inputs: Use WordPress sanitization functions
- Validate permissions: Check user capabilities
- Escape outputs: Prevent XSS attacks
- Use nonces: For form submissions
4. Testing Your Extensions
functions.phpphp1// Example test for contact creation hook 2class Test_SkunkCRM_Extension extends WP_UnitTestCase { 3 4 public function test_contact_creation_hook() { 5 // Create a contact 6 $contact_data = [ 7 'name' => 'Test Contact', 8 'email' => 'test@example.com', 9 'status' => 'prospect' 10 ]; 11 12 $contact_id = SkunkCRM_Contacts::create($contact_data); 13 14 // Verify your hook ran 15 $this->assertTrue(did_action('skunkcrm_after_contact_create') > 0); 16 17 // Verify your modifications 18 $contact = SkunkCRM_Contacts::get_by_id($contact_id); 19 $this->assertEquals('example.com', $contact['email_domain']); 20 } 21}
Next Steps
- Explore the Hooks & Filters Reference for complete documentation
- Check out the REST API Documentation for integration details
- Contact our support team for developer assistance and questions