Run your own code when an import completes
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
| Action | When it runs | Callback arguments |
|---|---|---|
superspeedyimports_import_run_complete | A run reaches a terminal outcome: succeeded, failed or cancelled. Covers browser, CLI and scheduled imports. | $import_id, $history_id, $report |
superspeedyimports_import_run_succeeded | The data import succeeds, including success with row issues or After Import warnings. | $import_id, $history_id, $report |
superspeedyimports_import_run_failed | The import reaches a failed outcome. | $import_id, $history_id, $report |
superspeedyimports_scheduled_import_complete | A 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.
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 field | Meaning |
|---|---|
schema_version | Report format version. |
import_id, history_id, import_name | Saved import and this particular attempt. |
source_name | The import’s saved source file name (its csv_file_name). |
outcome | succeeded, failed or cancelled. |
has_issues | Row issues, warnings or images that could not be downloaded need attention, independently of the outcome. |
runner | Run origin, such as browser, cli or scheduled. Historical records may have an unknown origin. |
kind | import or maintenance. |
started_at, finished_at | UTC timestamps. |
rows_processed | Source rows processed. |
counters | Recorded 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_mb | Elapsed seconds and measured peak PHP worker memory in MB. |
stages | Stage details, including name and duration_s. |
image_download_issues | One 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_warnings | Grouped row issues and separate After Import warnings. |
after_import_notices | After Import notices that ask you to take a further step, shown as Next steps in the email report. |
error | Diagnostic message and stage; empty for a run without an error. |
history_url | Link 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.
| Filter | First argument and return value | Remaining arguments |
|---|---|---|
superspeedyimports_email_report_data | Structured report array | $import_id, $history_id |
superspeedyimports_email_report_subject | Subject string | $report, $import_id, $history_id |
superspeedyimports_email_report_message | Array 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
- Install the mu-plugin on a test site and use a small saved import.
- Run the import from Preview & Run or
wp ssi <id>. - Inspect the receiver and the run’s History details, including hook diagnostics.
- 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. - Test a stopped or failed attempt too. Success-only work must stay on the succeeded action.
Further reading
Written for Super Speedy Imports 3.40.0.

Comments