Okay
  Public Ticket #4256411
Integrating Sonaar PRO Collected Emails with Email Marketing Platform
Open

Comments

  • Matheus Pereira dos Santos Silva started the conversation

    Dear Sonaar Support Team,

    I'm currently using MP3 Sonaar PRO on my WordPress beatstore and leveraging the built-in “Download” button to collect leads.

    I’d like to automatically send these collected emails to my email marketing service (Brevo, formerly Sendinblue) so they can enter my automation flows there.

    Is there any native functionality, webhook support, or recommended method to push these leads (from the “Collected Emails” post type) directly to an external platform like Brevo?

    Any documentation, tips, or guidance you could provide would be greatly appreciated.

    Thank you for your time and support.

    Best regards

  •  689
    Alexandre replied

    Hi,
    Unfortunately, I don’t have a solution to provide. I don’t have a hook or function for that.
    The emails are listed in wp-admin > MP3 Player > Collected Emails,
    and you can manually export a CSV file with the emails from the dashboard. But I don’t have an automatic solution.

    Thanks,


    Alexandre from the Sonaar.io Crew

  • Arthur replied

    Hello, I'm Arthur from Brazil, and I successfully integrated Brevo with some code generated by GPT's o3 model + Claude. I don't understand much about programming, but I had Claude adapt the texts to English because I believe this opens an interesting precedent for integration not only with Brevo, but with other marketing platforms as well. I used the "Code Snippets" plugin to add this code.

    /**
     * Plugin Name: Sonaar → Brevo (Sendinblue) Sync
     * Description: Automatically sends emails collected by MP3 Audio Player Pro (Sonaar) to a Brevo list, including lead names.
     * Version: 1.2.0
     * Author: Community Contribution
     * 
     * INTEGRATION OVERVIEW:
     * This plugin bridges Sonaar's lead collection system with Brevo's email marketing platform.
     * When users download music/content and provide their email + name, this data is automatically
     * synchronized to your Brevo contact list, enabling automated email marketing campaigns.
     * 
     * COMPATIBILITY:
     * - Sonaar MP3 Audio Player Pro (v5.8+)
     * - Brevo (formerly Sendinblue) API v3
     * - WordPress 5.0+
     * 
     * FEATURES:
     * - Automatic sync on lead capture
     * - Extracts both email and name from Sonaar forms
     * - Prevents duplicate submissions
     * - Manual resend option for troubleshooting
     * - Error logging and tracking
     * - Works with Portuguese and English Brevo accounts
     */

    if (!defined('ABSPATH')) { exit; }

    /**
     * CONFIGURATION — Fill these before activating the plugin
     */
    define('SBREVO_API_KEY', 'YOUR_BREVO_API_KEY_HERE'); // Get from Brevo → API & Webhooks → API Keys
    define('SBREVO_LIST_ID', 0); // Optional: Brevo list ID (number). Use 0 to not force a specific list.

    /**

    SETUP INSTRUCTIONS:
    1. Install the "Code Snippets" plugin from WordPress repository
    2. Go to Snippets → Add New → PHP Snippet  
    3. Paste this code (remove the <?php tags if present)
    4. Get your Brevo API key from: Account → API & Webhooks → API Keys
    5. (Optional) Get your list ID from: Contacts → Lists → [Your List] → Settings
    6. Replace SBREVO_API_KEY and SBREVO_LIST_ID with your actual values
    7. Activate the snippet

    ALTERNATIVE: You can also save this as a plugin file if you prefer the traditional method.


     * 
     * BREVO API DOCUMENTATION:
     * https://developers.brevo.com/reference/createcontact
     */

    /**
     * Detects Sonaar's "Collected Emails" Custom Post Type without relying on specific slugs.
     * Note: Since v5.8, Sonaar migrated "Collected Emails" to its own CPT.
     * Source: Plugin changelog.
     * 
     * @param string $post_type The post type to check
     * @return bool True if this is Sonaar's collected emails CPT
     */
    function sbrevo_is_sonaar_collected_emails_cpt($post_type){
        if (!$post_type) return false;

        // Heuristics: Try common slugs and labels (English/Portuguese)
        $possible_slugs = array(
            'sr_collected_emails', 'sr_collected_email',
            'sonaar_collected_emails', 'sonaar_collected_email',
            'collected_emails', 'collected_email'
        );
        if (in_array($post_type, $possible_slugs, true)) return true;

        $obj = get_post_type_object($post_type);
        if (!$obj || empty($obj->labels)) return false;

        $label = isset($obj->labels->name) ? sanitize_text_field($obj->labels->name) : '';
        $label = mb_strtolower($label);

        // Probable labels in English/Portuguese
        $candidates = array('collected emails','e-mails coletados','emails coletados','e-mails recolhidos');
        foreach ($candidates as $cand){
            if (false !== mb_strpos($label, $cand)) return true;
        }

        return false;
    }

    /**
     * Extracts email address from Sonaar lead post (title/meta/content).
     * Tries multiple common field names used by Sonaar forms.
     * 
     * @param int $post_id The post ID to extract email from
     * @return string The email address or empty string if not found
     */
    function sbrevo_extract_email_from_post($post_id){
        $post = get_post($post_id);
        if (!$post) return '';

        // 1) Common meta fields (including Sonaar-specific fields)
        $meta_keys = array(
            'user_email', '_user_email',                   // Sonaar form field
            'email','_email','sonaar_email','sr_email','contact_email','lead_email'
        );
        foreach ($meta_keys as $k){
            $v = get_post_meta($post_id, $k, true);
            if ($v && filter_var($v, FILTER_VALIDATE_EMAIL)) return strtolower(trim($v));
        }

        // 2) Post title (if it's an email)
        if ($post->post_title && filter_var($post->post_title, FILTER_VALIDATE_EMAIL)){
            return strtolower(trim($post->post_title));
        }

        // 3) Search post content for email pattern
        if ($post->post_content){
            if (preg_match('/[A-Z0-9._%+\-]+@[A-Z0-9.\-]+\.[A-Z]{2,}/i', $post->post_content, $m)){
                $email = trim($m[0]);
                if (filter_var($email, FILTER_VALIDATE_EMAIL)) return strtolower($email);
            }
        }

        return '';
    }

    /**
     * Extracts lead name from Sonaar post.
     * Tries multiple locations: meta fields, post title, and content.
     * 
     * @param int $post_id The post ID to extract name from
     * @return string The lead name or empty string if not found
     */
    function sbrevo_extract_name_from_post($post_id){
        $post = get_post($post_id);
        if (!$post) return '';

        // 1) Meta fields (including Sonaar-specific fields)
        $name_meta_keys = array(
            'user_firstname', '_user_firstname',           // Sonaar form field
            'name', '_name', 'first_name', '_first_name', 
            'sonaar_name', 'sr_name', 'contact_name', 'lead_name',
            'full_name', '_full_name', 'user_name', '_user_name'
        );
        
        foreach ($name_meta_keys as $k){
            $v = get_post_meta($post_id, $k, true);
            if ($v && is_string($v) && strlen(trim($v)) > 0) {
                return sanitize_text_field(trim($v));
            }
        }

        // 2) Post title (if it's not an email)
        if ($post->post_title && !filter_var($post->post_title, FILTER_VALIDATE_EMAIL)){
            $title = sanitize_text_field(trim($post->post_title));
            // Remove possible prefixes like "Lead:", "Email:", etc.
            $title = preg_replace('/^(lead|email|contact|nome):\s*/i', '', $title);
            if (strlen($title) > 0) {
                return $title;
            }
        }

        // 3) Search content for name patterns (lines that aren't emails)
        if ($post->post_content){
            $lines = explode("\n", $post->post_content);
            foreach ($lines as $line) {
                $line = trim(strip_tags($line));
                // If line is not an email and has reasonable length (2-50 chars)
                if ($line && !filter_var($line, FILTER_VALIDATE_EMAIL) && 
                    strlen($line) >= 2 && strlen($line) <= 50 &&
                    !preg_match('/^(http|www|\.com|\.br)/i', $line)) {
                    
                    // Remove common prefixes
                    $line = preg_replace('/^(nome|name|lead|contact):\s*/i', '', $line);
                    $line = trim($line);
                    
                    if (strlen($line) >= 2) {
                        return sanitize_text_field($line);
                    }
                }
            }
        }

        return '';
    }

    /**
     * Sends/updates contact in Brevo with email and name.
     * Uses Brevo v3 Contacts API (Create/Update + listIds/updateEnabled).
     * 
     * @param string $email Lead email address
     * @param string $name Lead name (optional)
     * @return true|WP_Error True on success, WP_Error on failure
     */
    function sbrevo_push_to_brevo($email, $name = ''){
        if (!defined('SBREVO_API_KEY') || empty(SBREVO_API_KEY) || SBREVO_API_KEY === 'YOUR_BREVO_API_KEY_HERE') {
            return new WP_Error('brevo_missing_key','Brevo API key missing or not configured');
        }

        $endpoint = 'https://api.brevo.com/v3/contacts';

        // Base attributes
        $attributes = array(
            'SOURCE' => 'Sonaar MP3 Player'
        );

        // Add name if available (supports both English and Portuguese Brevo accounts)
        if (!empty($name)) {
            $attributes['FIRSTNAME'] = trim($name);  // International standard field
            $attributes['NOME'] = trim($name);       // Portuguese Brevo accounts
        }

        $payload = array(
            'email' => $email,
            'updateEnabled' => true,               // Update if contact already exists
            'attributes' => $attributes,
        );
        
        // Add to specific list if configured
        if (defined('SBREVO_LIST_ID') && SBREVO_LIST_ID > 0){
            $payload['listIds'] = array((int)SBREVO_LIST_ID);
        }

        $args = array(
            'method'  => 'POST',
            'timeout' => 10,
            'headers' => array(
                'api-key'       => SBREVO_API_KEY,
                'Content-Type'  => 'application/json',
                'Accept'        => 'application/json',
            ),
            'body'    => wp_json_encode($payload),
        );

        $res = wp_remote_post($endpoint, $args);
        if (is_wp_error($res)) return $res;

        $code = wp_remote_retrieve_response_code($res);
        // 201 (created) or 204 (updated) are OK; Brevo may vary.
        if (in_array($code, array(200,201,202,204), true)){
            return true;
        }

        $body = wp_remote_retrieve_body($res);
        return new WP_Error('brevo_api_error', 'Brevo returned code '.$code.'; response: '.$body);
    }

    /**
     * Hook into save_post to automatically send leads when created/updated.
     * This is triggered whenever Sonaar creates a new lead entry.
     */
    add_action('save_post', function($post_id, $post, $update){
        // Avoid loops and inappropriate cases
        if (wp_is_post_revision($post_id) || (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE)) return;

        $post_type = get_post_type($post_id);
        if (!sbrevo_is_sonaar_collected_emails_cpt($post_type)) return;

        // Debounce: already synchronized?
        if (get_post_meta($post_id, '_sbrevo_synced', true)) return;

        $email = sbrevo_extract_email_from_post($post_id);
        if (!$email) return;

        // Extract name
        $name = sbrevo_extract_name_from_post($post_id);

        $result = sbrevo_push_to_brevo($email, $name);

        if (is_wp_error($result)){
            // Log error for inspection
            update_post_meta($post_id, '_sbrevo_error', $result->get_error_message());
            error_log('[Sonaar→Brevo] Error sending '.$email.($name ? ' ('.$name.')' : '').': '.$result->get_error_message());
            return;
        }

        // Mark as synchronized and save sent data
        update_post_meta($post_id, '_sbrevo_synced', current_time('mysql'));
        update_post_meta($post_id, '_sbrevo_sent_email', $email);
        if ($name) {
            update_post_meta($post_id, '_sbrevo_sent_name', $name);
        }
        
        // Success log for debugging
        error_log('[Sonaar→Brevo] Successfully sent: '.$email.($name ? ' ('.$name.')' : ''));
    }, 20, 3);

    /**
     * Optional: Manual resend button in post edit screen (useful for troubleshooting).
     * Appears only on Sonaar collected email posts.
     */
    add_action('post_submitbox_misc_actions', function(){
        global $post;
        if (!$post || !sbrevo_is_sonaar_collected_emails_cpt($post->post_type)) return;

        $url = wp_nonce_url(add_query_arg(array(
            'sbrevo_resend' => $post->ID,
        ), admin_url('post.php?action=edit&post='.$post->ID)), 'sbrevo_resend_'.$post->ID);

        echo '<div class="misc-pub-section"><a class="button" href="'.esc_url($url).'">Resend to Brevo</a></div>';
        
        // Show last sync information
        $synced = get_post_meta($post->ID, '_sbrevo_synced', true);
        $sent_email = get_post_meta($post->ID, '_sbrevo_sent_email', true);
        $sent_name = get_post_meta($post->ID, '_sbrevo_sent_name', true);
        $error = get_post_meta($post->ID, '_sbrevo_error', true);
        
        if ($synced) {
            echo '<div class="misc-pub-section" style="font-size: 11px; color: #666;">';
            echo '<strong>Last sync:</strong> ' . date('m/d/Y H:i', strtotime($synced)) . '<br>';
            if ($sent_email) echo '<strong>Email:</strong> ' . esc_html($sent_email) . '<br>';
            if ($sent_name) echo '<strong>Name:</strong> ' . esc_html($sent_name) . '<br>';
            echo '</div>';
        }
        
        if ($error) {
            echo '<div class="misc-pub-section" style="font-size: 11px; color: #d63638;">';
            echo '<strong>Last error:</strong><br>' . esc_html($error);
            echo '</div>';
        }
    });

    /**
     * Handle manual resend requests from admin interface.
     */
    add_action('admin_init', function(){
        if (empty($_GET['sbrevo_resend'])) return;
        $post_id = absint($_GET['sbrevo_resend']);
        if (!$post_id || !wp_verify_nonce($_GET['_wpnonce'] ?? '', 'sbrevo_resend_'.$post_id)) return;

        $email = sbrevo_extract_email_from_post($post_id);
        if ($email){
            $name = sbrevo_extract_name_from_post($post_id);
            
            delete_post_meta($post_id, '_sbrevo_synced'); // Allow new send
            $result = sbrevo_push_to_brevo($email, $name);
            if (is_wp_error($result)){
                update_post_meta($post_id, '_sbrevo_error', $result->get_error_message());
                wp_safe_redirect(add_query_arg('sbrevo_status','error', get_edit_post_link($post_id,'')));
                exit;
            }
            update_post_meta($post_id, '_sbrevo_synced', current_time('mysql'));
            update_post_meta($post_id, '_sbrevo_sent_email', $email);
            if ($name) {
                update_post_meta($post_id, '_sbrevo_sent_name', $name);
            }
            delete_post_meta($post_id, '_sbrevo_error');
            wp_safe_redirect(add_query_arg('sbrevo_status','ok', get_edit_post_link($post_id,'')));
            exit;
        }
    });

    /**
     * ADAPTATION GUIDE FOR OTHER PLATFORMS:
     * 
     * This code serves as a template for integrating Sonaar with other email marketing platforms.
     * To adapt for other services, modify the sbrevo_push_to_[platform]() function:
     * 
     * MAILCHIMP:
     * - Endpoint: https://[datacenter].api.mailchimp.com/3.0/lists/{list_id}/members
     * - Auth: API Key in Authorization header
     * - Payload: {"email_address": email, "status": "subscribed", "merge_fields": {"FNAME": name}}
     * 
     * ACTIVECAMPAIGN:
     * - Endpoint: https://[account].api-us1.com/api/3/contacts
     * - Auth: API Token in Api-Token header  
     * - Payload: {"contact": {"email": email, "firstName": name}}
     * 
     * CONVERTKIT:
     * - Endpoint: https://api.convertkit.com/v3/forms/{form_id}/subscribe
     * - Auth: API Key in URL parameter
     * - Payload: {"email": email, "first_name": name}
     * 
     * The extraction functions (sbrevo_extract_email_from_post, sbrevo_extract_name_from_post)
     * can remain the same for all platforms - only the API communication needs to be adapted.
     */
    ?>

  •  689
    Alexandre replied

    Hi Arthur,

    I’m increasingly advising clients to ask ChatGPT for help with requests we can’t support, but what you’ve done looks amazing. Well done!

    Thanks,


    Alexandre from the Sonaar.io Crew