Developers

Run your own code when an import completes

Published · Last updated

Use WordPress actions to connect an import’s result to your own code: notify another system, warm a cache or start a dependent task. Super Speedy Imports supplies a structured report with the outcome, counters, timings and issues for that run.

For email notifications, use Schedule → Email reports. You can choose recipients and events, preview the report and send a test without writing PHP. See Scheduling imports and getting alerts.

To keep another plugin’s index or actions in step with what an import writes, see Adding compatibility for other plugins.

Choose the right action

ActionWhen it runsCallback arguments
superspeedyimports_import_run_completeA run reaches a terminal outcome: succeeded, failed or cancelled. Covers browser, CLI and scheduled imports.$import_id, $history_id, $report
superspeedyimports_import_run_succeededThe data import succeeds, including success with row issues or After Import warnings.$import_id, $history_id, $report
superspeedyimports_import_run_failedThe import reaches a failed outcome.$import_id, $history_id, $report
superspeedyimports_scheduled_import_completeA scheduled run reaches a terminal outcome, after the schedule’s final state is saved.$import_id, $schedule, $report

Use superspeedyimports_import_run_succeeded for work that depends on a successful import. A stopped run has outcome = cancelled; it invokes the general completion action, without invoking the succeeded or failed action.

A recoverable browser interruption or a scheduled slice ending is not a terminal outcome. Recovery can continue that run. Fatal or stopped work is reconciled against the run’s ownership and active work before it is finalised.

PHP fatal errors, including memory exhaustion, record a diagnostic for the accepted run. If the operating system kills the CLI process before PHP can handle the error, the background reconciliation task can verify its death on Linux using the saved process, boot and host identity. It waits for active import SQL to finish. Unreadable process information, another host or another process namespace is not evidence of failure; a stale heartbeat alone never closes the run. Reconciliation and queued reports need WordPress cron to run.

Common mistake

Completion means the attempt ended. Check the outcome before treating it as success, and check has_issues when your next task needs a clean result.

Read the structured report

Every general and outcome-specific callback receives three arguments. Set the final add_action() argument to 3 so WordPress passes all of them.

Report fieldMeaning
schema_versionReport format version.
import_id, history_id, import_nameSaved import and this particular attempt.
source_nameThe import’s saved source file name (its csv_file_name).
outcomesucceeded, failed or cancelled.
has_issuesRow issues, warnings or images that could not be downloaded need attention, independently of the outcome.
runnerRun origin, such as browser, cli or scheduled. Historical records may have an unknown origin.
kindimport or maintenance.
started_at, finished_atUTC timestamps.
rows_processedSource rows processed.
countersRecorded item and database counters, including posts_inserted, posts_updated, posts_trashed (moved to the trash), posts_deleted (deleted permanently) and db_rows_written where available. When images could not be downloaded, also failed_images and failed_image_download_attempts.
total_time_s, peak_mem_mbElapsed seconds and measured peak PHP worker memory in MB.
stagesStage details, including name and duration_s.
image_download_issuesOne entry per image stage with images that could not be downloaded: stage, failed_images and failed_download_attempts. An image that downloaded on a retry is not listed. Empty when every image downloaded.
row_error_classes, after_import_warningsGrouped row issues and separate After Import warnings.
after_import_noticesAfter Import notices that ask you to take a further step, shown as Next steps in the email report.
errorDiagnostic message and stage; empty for a run without an error.
history_urlLink to the run details, requiring normal administrator authentication.

Created and updated counts are inside $report['counters']. Use null for an unavailable measurement; do not turn a missing value into a measured zero. Source rows, imported records and database rows written are different quantities.

Treat raw diagnostic messages as private administrator data. They can contain paths or details from a failed operation. The built-in email renderer uses a safe cause summary rather than copying raw diagnostics into email.

Dispatch order and delivery guarantees

Super Speedy Imports saves the terminal report and recipient-specific email intent together. It schedules notification processing before invoking external completion callbacks. Email does not depend on a listener attached to the completion action.

The general completion action runs first, followed by the succeeded or failed action where applicable. Scheduled completion receives the final saved schedule state after those actions. Its $schedule includes fields such as cadence, enabled, next_run, last_run, last_result, status and history_id. Use $report['history_id'] to identify the completed attempt.

Dispatch state is recorded so repeated terminal callbacks do not routinely repeat these actions. This does not make an external HTTP request exactly once: a process can stop after the receiver accepts a request but before the callback finishes. Include history_id as an idempotency key and have the receiving system deduplicate it.

A callback exception is recorded in the run’s hook diagnostics. It does not change a successful data outcome into a failure or remove queued mail. Within one WordPress action, a throwing callback can prevent later callbacks on that same action from running. Keep callbacks short and use your own durable queue for work that needs retries.

Where to put your code

Create wp-content/mu-plugins/ssi-completion-hooks.php, creating the mu-plugins directory if needed. A must-use plugin loads for browser, CLI and scheduled requests and remains active across theme changes.

Example: send a successful supplier import to a webhook

This complete example handles import 7. Replace the import ID and endpoint with your own. It sends the report’s recorded counters, includes the run ID for receiver-side deduplication and surfaces HTTP errors.

<?php
/**
 * Plugin Name: Supplier import webhook
 * Description: Reports successful supplier imports to a downstream system.
 */
defined( 'ABSPATH' ) || exit;

add_action( 'superspeedyimports_import_run_succeeded', function ( $import_id, $history_id, $report ) {
    if ( 7 !== (int) $import_id ) {
        return;
    }

    $counters = $report['counters'] ?? array();
    $response = wp_remote_post( 'https://dashboard.example.com/hooks/feed-complete', array(
        'timeout' => 10,
        'headers' => array(
            'Content-Type'    => 'application/json',
            'Idempotency-Key' => 'ssi-run-' . (int) $history_id,
        ),
        'body' => wp_json_encode( array(
            'import_id'   => (int) $import_id,
            'history_id'  => (int) $history_id,
            'outcome'     => $report['outcome'],
            'has_issues'  => (bool) $report['has_issues'],
            'rows'        => $report['rows_processed'] ?? null,
            'created'     => $counters['posts_inserted'] ?? null,
            'updated'     => $counters['posts_updated'] ?? null,
            'duration_s'  => $report['total_time_s'] ?? null,
        ) ),
    ) );

    if ( is_wp_error( $response ) ) {
        throw new RuntimeException( $response->get_error_message() );
    }
    $status = wp_remote_retrieve_response_code( $response );
    if ( $status < 200 || $status >= 300 ) {
        throw new RuntimeException( 'Supplier webhook returned HTTP ' . (int) $status );
    }
}, 10, 3 );

This callback also runs for successful manual imports. To restrict it to scheduled imports, add if ( 'scheduled' !== $report['runner'] ) { return; } before the HTTP request. To exclude success with issues, check $report['has_issues'] and decide how your integration should handle that result.

Example: inspect the final schedule state

Use the scheduled action when the next occurrence matters. The third argument supplies the report directly; no database lookup is needed.

<?php
/** Plugin Name: Supplier schedule completion log */
defined( 'ABSPATH' ) || exit;

add_action( 'superspeedyimports_scheduled_import_complete', function ( $import_id, $schedule, $report ) {
    if ( 7 !== (int) $import_id ) {
        return;
    }
    error_log( sprintf(
        'SSI import %d, run %d: %s. Next scheduled occurrence: %s.',
        (int) $import_id,
        (int) $report['history_id'],
        $report['outcome'],
        $schedule['next_run'] ?? 'none'
    ) );
}, 10, 3 );

This example records success, failure and cancellation. For a success-only dependent task, use the succeeded action instead.

Customise the email through PHP filters

The report and its identifiers let you scope changes to one import. Return the documented value; do not send mail from a rendering filter, because preview and test use the same renderer.

FilterFirst argument and return valueRemaining arguments
superspeedyimports_email_report_dataStructured report array$import_id, $history_id
superspeedyimports_email_report_subjectSubject string$report, $import_id, $history_id
superspeedyimports_email_report_messageArray containing subject, html and text$report, $import_id, $history_id

The report-data filter receives three arguments in total; the subject and final-message filters receive four. A subject cannot contain line breaks. Keep both HTML and plain-text bodies useful if you replace the final message. Filters do not change the recipient snapshot.

<?php
/** Plugin Name: Supplier report subject */
defined( 'ABSPATH' ) || exit;

add_filter( 'superspeedyimports_email_report_subject', function ( $subject, $report, $import_id, $history_id ) {
    if ( 7 === (int) $import_id ) {
        return 'Supplier feed: ' . $report['outcome'] . ' (run ' . (int) $history_id . ')';
    }
    return $subject;
}, 10, 4 );

Rendering and mail errors appear in administrator diagnostics without changing the data import’s outcome. An accepted email means WordPress accepted it for sending; it is not confirmation of inbox delivery.

Test your listener

  1. Install the mu-plugin on a test site and use a small saved import.
  2. Run the import from Preview & Run or wp ssi <id>.
  3. Inspect the receiver and the run’s History details, including hook diagnostics.
  4. For the scheduled action, save a Once schedule and let the due scan run. You can trigger the scan with wp cron event run superspeedyimports_scheduled_scan_due.
  5. Test a stopped or failed attempt too. Success-only work must stay on the succeeded action.

Further reading

Configure schedules and email reports without PHP.

Written for Super Speedy Imports 3.40.0.

Comments

▲
▼
1/1