> For the complete documentation index, see [llms.txt](https://ctracker-docs.keywordrush.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://ctracker-docs.keywordrush.com/for-developers/events-and-hooks.md).

# Events and hooks

Cashback Tracker fires an action whenever something happens to money, and offers filters wherever a decision is worth overriding. This page covers the data side — orders, payouts, referrals — and the filters that change how the plugin behaves.

For adding markup to a shop page, see [Adding your own blocks to a shop page](/for-developers/shop-page-blocks.md); the template hooks live there.

{% hint style="info" %}
**Where to put this code.** A small [must-use plugin](https://wordpress.org/documentation/article/must-use-plugins/) — `wp-content/mu-plugins/my-cashback-hooks.php` — survives theme changes and cannot be deactivated by accident. Your theme's `functions.php` works too, at the cost of losing it the day you change theme.
{% endhint %}

### Order events

Every one receives the order as an array, with at least `id` and `user_id`.

| Action                 | Fires when                                                |
| ---------------------- | --------------------------------------------------------- |
| `cbtrkr_order_create`  | An order arrives from a network for the first time        |
| `cbtrkr_order_update`  | An existing order's details change                        |
| `cbtrkr_order_pending` | An order moves to pending                                 |
| `cbtrkr_order_approve` | An order is approved — the member's cashback becomes real |
| `cbtrkr_order_decline` | An order is declined                                      |

```php
add_action('cbtrkr_order_approve', function ($order) {
    // $order['user_id'], $order['id']
    my_send_congratulations((int) $order['user_id']);
});
```

**`cbtrkr_order_create` fires for every imported order**, including ones that arrive already approved or already declined. If you are rewarding a member for their first purchase, hang it on `cbtrkr_order_approve` instead — an order that arrives declined never earned anything.

### Payout events

Same shape: the payout as an array, with `id` and `user_id`.

| Action                    | Fires when                   |
| ------------------------- | ---------------------------- |
| `cbtrkr_payout_requested` | A member asks to be paid     |
| `cbtrkr_payout_paid`      | You mark a payout paid       |
| `cbtrkr_payout_rejected`  | You refuse one               |
| `cbtrkr_payout_failed`    | You sent it and it came back |

Remember that **Cashback Tracker never moves money** — these fire when a *record* changes, because you told it what you did. See [What the plugin never does](/how-cashback-works/what-the-plugin-never-does.md).

### Referral events

These carry ids directly rather than a row.

```php
add_action('cbtrkr_referral_attached', function ($referee_id, $referrer_id) { }, 10, 2);
add_action('cbtrkr_referral_bonus',    function ($referrer_id, $referee_id, $delta) { }, 10, 3);
add_action('cbtrkr_signup_bonus',      function ($referee_id, $referrer_id, $delta) { }, 10, 3);
```

`cbtrkr_referral_attached` fires once, when the link between two members is first made — at registration, and only there. The two bonus actions fire when a balance actually moves, and `$delta` is the amount.

Note that `cbtrkr_referral_attached` names the **referee** first, because it is their relationship being recorded. The plugin's own listener on it emails the other one.

{% hint style="warning" %}
**The argument order differs between the two bonus hooks**, because each names the person being paid first. `cbtrkr_referral_bonus` pays the referrer; `cbtrkr_signup_bonus` pays the referee. Getting them the wrong way round credits the wrong member, and nothing will warn you.
{% endhint %}

### Other events

| Action                             | Fires when                                    |
| ---------------------------------- | --------------------------------------------- |
| `cbtrkr_advertiser_page_create`    | A shop page is created, with the page id      |
| `cbtrkr_advertiser_status_changed` | A shop goes active or inactive at the network |
| `cbtrkr_settings_saved`            | Any settings screen is saved                  |

### Filters worth knowing

**Where a click goes:**

| Filter                | Changes                                                                                                    |
| --------------------- | ---------------------------------------------------------------------------------------------------------- |
| `cbtrkr_go_shop_link` | The Go to shop destination — and the Website row in the shop facts, which resolves through the same helper |
| `cbtrkr_subid_prefix` | The prefix on the sub id sent to networks. Default `cbtrkr`                                                |

**How often things run:**

| Filter                           | Default                                               |
| -------------------------------- | ----------------------------------------------------- |
| `cbtrkr_orders_refresh_interval` | One day                                               |
| `cbtrkr_coupon_ttl`              | How long an imported coupon is trusted before refresh |
| `cbtrkr_coupon_prune_days`       | 30 — also bounds the *Recently expired* section       |
| `cbtrkr_coupon_update_limit`     | How many shops one coupon run touches                 |
| `cbtrkr_expired_coupons_limit`   | 10 — the size of the expired section                  |

**Renaming a shortcode**, if one clashes with another plugin:

```php
add_filter('cbtrkr_coupons_shortcode', function () { return 'my-coupons'; });
```

The same exists for every shortcode: `cbtrkr_advertisers_shortcode`, `cbtrkr_shop_search_shortcode`, `cbtrkr_advertiser_shortcode`, `cbtrkr_tracking_link_shortcode`, `cbtrkr_account_shortcode`, `cbtrkr_balance_shortcode`.

**The member area:**

| Filter                       | Changes                                                                                                                                                                                |
| ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `cbtrkr_account_tabs`        | Which tabs appear, and their order                                                                                                                                                     |
| `cbtrkr_account_tab_content` | The body of a tab                                                                                                                                                                      |
| `cbtrkr_account_row_meta`    | The meta line on one activity row                                                                                                                                                      |
| `cbtrkr_account_per_page`    | Rows per page in the member's history                                                                                                                                                  |
| `cbtrkr_account_shops_url`   | Where the empty states send a member with nothing yet                                                                                                                                  |
| `cbtrkr_invite_base_url`     | The address a referral link points at, before the code is appended. Defaults to your Registration URL, else the WordPress signup form, else your home page when registration is closed |

The member area also has eight action slots for printing your own content into it — in the rail, on every tab, or on one named tab. They have a page of their own: [Adding your own content to the member area](/for-developers/member-area-hooks.md).

**Assets and SEO:**

| Filter                                        | Changes                                                                                                                                                                                      |
| --------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `cbtrkr_enqueue_frontend_style`               | Whether the stylesheet loads on this page. It loads on shop pages, the directory, the account page and any post carrying one of our shortcodes or blocks — return true to force it elsewhere |
| `cbtrkr_seo_head_enabled`                     | Whether the plugin prints its own meta tags. Off automatically when an SEO plugin is active                                                                                                  |
| `cbtrkr_jsonld_shop`, `cbtrkr_jsonld_archive` | The structured data graphs, before they are printed                                                                                                                                          |

**Email:**

| Filter                | Changes                             |
| --------------------- | ----------------------------------- |
| `cbtrkr_email_body`   | The body of any message to a member |
| `cbtrkr_email_layout` | The wrapper around it               |

### A worked example

Award a GamiPress point the first time a member's cashback is approved:

```php
add_action('cbtrkr_order_approve', function ($order) {
    $user_id = (int) ($order['user_id'] ?? 0);
    if (!$user_id || get_user_meta($user_id, '_my_first_cashback', true))
        return;

    update_user_meta($user_id, '_my_first_cashback', 1);

    if (function_exists('gamipress_award_points_to_user'))
        gamipress_award_points_to_user($user_id, 100, 'points');
});
```

The user-meta guard matters: an order can be approved, declined and approved again as a network revises it, and each of those fires the action.

{% hint style="info" %}
myCred and GamiPress are already wired up without any of this — see [myCred](/integrations/mycred.md) and [GamiPress](/integrations/gamipress.md). Write a hook when you want something those integrations do not cover.
{% endhint %}

### Related

* [Adding your own blocks to a shop page](/for-developers/shop-page-blocks.md)
* [Adding your own content to the member area](/for-developers/member-area-hooks.md)
* [Template reference](/for-developers/template-reference.md)
* [REST API](/for-developers/rest-api.md)
