benecaster_promote_grace_extended
Fires after a podcaster manually extends a transferred subscriber’s grace period from the Promotion History panel.
The old deadline and the new one arrive by different routes. $promotion is the row as it was before the extension, so $promotion['promote_grace_period_ends_at'] is still the old date; the new one is the third argument. Reading both is what lets a listener report the change rather than just the result.
$new_expiry_ts is a UTC Unix timestamp, not a formatted string — deliberately, so there is no clock to mistake and no format to parse.
Does not fire on a failed write, when no subscription has that id, or when the row has no grace period to extend.
Keep a CRM in step with manual grace-period decisions
Clearing and extending a grace period are decisions a human makes in the Promotion History panel, one subscriber at a time — the podcaster has checked the destination plugin, or has decided somebody deserves longer. Those decisions leave no trace outside the database on their own.
benecaster_promote_grace_cleared and benecaster_promote_grace_extended fire after each one so an external system can mirror it: close the support ticket that was opened when the migration started, cancel a scheduled “your access ends soon” campaign, or record the extension against the account so the next person to look does not undo it.
Notes
$promotion is the row as it was BEFORE the operation, and for _cleared that is the only reason it is useful. Clearing nulls both promote_grace_period_ends_at and promoted_to_bridge, so a listener that re-read the row itself would find two nulls and would have lost the two facts worth knowing: which plugin the subscriber was moved to, and what deadline was just cancelled. The same applies to _extended — $promotion['promote_grace_period_ends_at'] is the old deadline; the new one is the third argument.
$new_expiry_ts is a UTC Unix timestamp, not a formatted string. That is deliberate: there is no clock to mistake and no format to parse. Format it yourself with wp_date() if you are showing it to a human — date_i18n() expects the site offset already baked in and would render this real UTC epoch as if it were already site-local.
Neither action fires when the underlying write fails, and neither fires for a subscription row that does not exist — so a listener never records an operation that did not happen locally.
The cron expiry path fires neither of these. A grace period that simply runs out is benecaster_promote_grace_expired, and an expiry vetoed through benecaster_promote_grace_expiry_should_revoke fires nothing at all. These two are specifically the manual operations a podcaster performs.
<?php
add_action( 'benecaster_promote_grace_cleared', function ( array $promotion, int $user_id ): void {
my_crm_client()->close_task( 'podcast_migration_reauth', [
'user_id' => $user_id,
'show_id' => $promotion['show_id'],
'tier_slug' => $promotion['tier_slug'],
// Still readable here - the row is the pre-clear snapshot.
'moved_to' => $promotion['promoted_to_bridge'],
] );
}, 10, 2 );
add_action( 'benecaster_promote_grace_extended', function ( array $promotion, int $user_id, int $new_expiry_ts ): void {
my_crm_client()->reschedule_reminder( $user_id, $new_expiry_ts );
}, 10, 3 );
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
$promotion |
array |
— | The subscription row as it stood immediately before the extension. `promote_grace_period_ends_at` is the deadline being replaced. |
$user_id |
int |
— | Subscriber's WordPress user ID. Also present in `$promotion`. |
$new_expiry_ts |
int |
— | The new grace-period deadline as a UTC Unix timestamp. |
Need this built rather than just documented? See our services →