Action Hooks Reference
Stable, public action hooks integrators can hook into to react to ticket lifecycle events.
All actions live in the global do_action namespace and follow WordPress conventions: register listeners with add_action(), use the documented signature, and treat the action as fire-and-forget (return values are ignored).
Filter hooks live in filters.md (forthcoming). This page covers actions only.
Pre-existing actions
swh_pre_ticket_create
Since 2.1.0.
Fires immediately before a new ticket is inserted, after submission data has been sanitized and the swh_submission_data filter has run. Use this to validate or reject submissions before they hit the database.
add_action( 'swh_pre_ticket_create', function ( array $data ) {
// Sniff for obvious abuse before the post is created.
if ( str_contains( strtolower( $data['title'] ?? '' ), 'http://' ) ) {
wp_die( 'Links in titles are not permitted.', 'Submission blocked', array( 'response' => 400 ) );
}
}, 10, 1 );
Args:
array<string,string> $data— sanitized submission fields:name,email,title,message,priority,status.
swh_ticket_created
Since 2.1.0.
Fires after a new ticket has been fully created — meta written, attachments stored, assignment rules applied, and confirmation emails dispatched.
add_action( 'swh_ticket_created', function ( int $ticket_id, array $data ) {
// Push a card into a Trello inbox.
wp_remote_post(
'https://api.trello.com/1/cards',
array(
'body' => array(
'idList' => '5f1234abcd',
'name' => sprintf( '[%s] %s', $data['ticket_id'], $data['title'] ),
'desc' => $data['admin_url'],
'key' => MY_TRELLO_KEY,
'token' => MY_TRELLO_TOKEN,
),
)
);
}, 10, 2 );
Args:
int $ticket_id— the new ticket post ID.array<string,string> $data— submission data includingticket_id(UID),ticket_url,email.
Lifecycle actions (v3.7.0+)
swh_ticket_replied
Since 3.7.0.
Fires after a helpdesk_reply comment is inserted on a ticket. Triggered for staff replies, client replies, and system-generated comments (auto-close notes, retention purge notes, merge breadcrumbs). Use the $is_staff_reply argument to distinguish staff from non-staff.
$is_staff_reply is true only when the comment author has the edit_post capability on the ticket (administrators and technicians). Portal client replies, inbound-email replies, and system comments report false.
add_action( 'swh_ticket_replied', function ( int $ticket_id, int $comment_id, bool $is_staff_reply ) {
// Slack-notify the team when a client replies on a ticket that has gone quiet.
if ( $is_staff_reply ) {
return;
}
$uid = get_post_meta( $ticket_id, '_ticket_uid', true );
wp_remote_post(
MY_SLACK_INBOX_WEBHOOK,
array(
'headers' => array( 'Content-Type' => 'application/json' ),
'body' => wp_json_encode(
array( 'text' => sprintf( ':speech_balloon: Client reply on %s', $uid ) )
),
)
);
}, 10, 3 );
Args:
int $ticket_id— ticket post ID.int $comment_id— the inserted comment ID.bool $is_staff_reply—truewhen the comment author hasedit_postcap on the ticket.
swh_ticket_status_changed
Since 3.7.0.
Fires whenever a ticket’s _ticket_status post meta changes to a different value. Does not fire on initial ticket creation (use swh_ticket_created for that) or when the new status matches the existing one (no-op).
add_action( 'swh_ticket_status_changed', function ( int $ticket_id, string $old_status, string $new_status ) {
// Mirror status to an external CRM.
my_crm_set_ticket_status( $ticket_id, $new_status );
}, 10, 3 );
Args:
int $ticket_id— ticket post ID.string $old_status— previous status label.string $new_status— new status label.
swh_ticket_closed
Since 3.7.0.
Fires after swh_ticket_status_changed when the transition takes the ticket from a non-closed status into the configured closed status (swh_closed_status option).
add_action( 'swh_ticket_closed', function ( int $ticket_id, string $previous_status ) {
// Stop the SLA timer in PagerDuty.
pagerduty_resolve_incident( get_post_meta( $ticket_id, '_pagerduty_incident_id', true ) );
}, 10, 2 );
Args:
int $ticket_id— ticket post ID.string $previous_status— status the ticket transitioned from.
swh_ticket_reopened
Since 3.7.0.
Fires after swh_ticket_status_changed when the transition takes the ticket from the closed status back into any non-closed status.
add_action( 'swh_ticket_reopened', function ( int $ticket_id, string $previous_status ) {
// Re-open the linked PagerDuty incident.
pagerduty_trigger_incident_from_ticket( $ticket_id );
}, 10, 2 );
Args:
int $ticket_id— ticket post ID.string $previous_status— closed status the ticket transitioned from.
swh_ticket_assigned
Since 3.7.0.
Fires whenever the _ticket_assigned_to user changes. Does not fire on a 0 → 0 no-op (unassigned → unassigned). Fires on 0 → user, user → 0, and userA → userB.
add_action( 'swh_ticket_assigned', function ( int $ticket_id, int $old_user_id, int $new_user_id ) {
// DM the new assignee in Slack.
if ( $new_user_id > 0 ) {
$slack_id = get_user_meta( $new_user_id, 'slack_user_id', true );
my_slack_dm( $slack_id, sprintf( 'Ticket #%d is yours.', $ticket_id ) );
}
}, 10, 3 );
Args:
int $ticket_id— ticket post ID.int $old_user_id— previous assignee user ID (0 if unassigned).int $new_user_id— new assignee user ID (0 if unassigned).
swh_sla_breached
Since 3.7.0.
Fires the first time a ticket transitions to SLA breach state during the hourly swh_sla_check_event cron run. Will not fire again on subsequent runs for the same ticket unless the breach flag is manually cleared.
add_action( 'swh_sla_breached', function ( int $ticket_id, int $minutes_over ) {
// Page the on-call engineer when a ticket goes red.
pagerduty_trigger_incident(
sprintf( 'Ticket %d is %d minutes over SLA.', $ticket_id, $minutes_over )
);
}, 10, 2 );
Args:
int $ticket_id— ticket post ID.int $minutes_over— minutes the ticket is over the configured breach threshold.
swh_csat_submitted
Since 3.7.0.
Fires after a client successfully submits a CSAT (customer satisfaction) rating via the post-close prompt.
add_action( 'swh_csat_submitted', function ( int $ticket_id, int $rating ) {
// Forward the score to a metrics warehouse.
metrics_warehouse_record_event(
'helpdesk.csat',
array(
'ticket_id' => $ticket_id,
'rating' => $rating,
'assignee' => (int) get_post_meta( $ticket_id, '_ticket_assigned_to', true ),
)
);
}, 10, 2 );
Args:
int $ticket_id— ticket post ID.int $rating— submitted rating, integer 1–5.
Stability policy
These action signatures are public API. They will not change within the v3.x line. Removal or signature changes follow the deprecation policy (forthcoming): two-minor-release deprecation window before removal.