# Staff Reply Hooks

https://dev.wisecp.com/es/staff-reply-hooks

The eight hooks over staff replies, internal notes and assignment.

## Overview

Every point where staff touch a ticket lives here: writing a reply, editing it, deleting it, adding an internal note and assigning.

There are three reply hooks and they should not be confused: the one staff write, the one a customer writes and the one a **scheduled task** writes. The last carries its parameters in a single array.

## Reference

### Stopping a staff reply

gateticket.reply

`AdminTickets` signature attached

Runs before a staff reply is sent.

Parameters 3

$ticketarrayThe ticket being replied to.

$messagestringThe final text. It carries markup and the **signature is already attached**: allow for that if you measure length.

$admin_idintThe staff member writing it.

Return 1

string|null**A non-empty text blocks the operation** and is shown as the error. An empty return lets it carry on.

Listener PHP

```php
Hook::add('gate:ticket.reply', 10, function ($ticket, $message, $admin_id) {
    // The signature is already in: allow for it when measuring length.
    if (Acme::containsSecret($message)) return 'The reply holds a value that must not be shared.';

    return null;
});
```

### Following a staff reply

actionticket.reply_added

`AdminTickets` hydrated reply

Runs after a staff reply is added.

Parameters 3

$ticketarrayThe fresh ticket after the reply.

$reply_idintThe id of the reply added.

$replyarrayThe whole reply: its text, author, attachments and address.

Return 1

voidThe return is ignored.

Listener PHP

```php
Hook::add('action:ticket.reply_added', 10, function ($ticket, $reply_id, $reply) {
    Acme::stopSlaClock((int) ($ticket['id'] ?? 0));
});
```

### Following an automatic reply

actionticket.replied

`cronjobs` one context array

Runs when a scheduled task adds a reply to a ticket. Unlike its siblings the parameters arrive in **a single array**.

Parameters 1

$contextarrayEverything is here: the source, the current ticket and the reply added. Expect no separate parameters; this hook carries one array.

Return 1

voidThe return is ignored.

Listener PHP

```php
Hook::add('action:ticket.replied', 10, function ($context) {
    // One array: different from its siblings.
    $ticket = $context['request'] ?? [];
    Acme::noteAutoReply((int) ($ticket['id'] ?? 0));
});
```

### Following a reply being edited

actionticket.reply_updated

`AdminTickets` after the edit

Runs after a reply is edited.

Parameters 3

$ticketIdintThe ticket id.

$replyIdintThe id of the edited reply.

$replyarrayThe updated reply. The previous version is not passed: to compare, you must have kept a copy yourself.

Return 1

voidThe return is ignored.

Listener PHP

```php
Hook::add('action:ticket.reply_updated', 10, function ($ticketId, $replyId, $reply) {
    // The previous version is not passed.
    Acme::reindexReply($replyId, $reply['message'] ?? '');
});
```

### Following a reply being deleted

actionticket.reply_deleted

`AdminTickets` after deletion

Runs after a reply is deleted.

Parameters 3

$ticketIdintThe ticket id.

$replyIdintThe id of the deleted reply.

$replyarrayThe deleted reply record: its owner and whether it was from staff.

Return 1

voidThe return is ignored.

Listener PHP

```php
Hook::add('action:ticket.reply_deleted', 10, function ($ticketId, $replyId, $reply) {
    Acme::dropFromIndex('reply', $replyId);
});
```

### Stopping an internal note

gateticket.note_add

`AdminTickets` the customer never sees it

Runs before staff add an internal note. Notes are **never shown to the customer**.

Parameters 3

$ticketarrayThe ticket the note goes on.

$messagestringThe note content.

$admin_idintThe staff member adding it.

Return 1

string|null**A non-empty text blocks the operation** and is shown as the error. An empty return lets it carry on.

Listener PHP

```php
Hook::add('gate:ticket.note_add', 10, function ($ticket, $message, $admin_id) {
    if (Acme::tooLong($message)) return 'The note is too long.';

    return null;
});
```

### Following an internal note

actionticket.note_added

`AdminTickets` the note may be empty

Runs after an internal note is added.

Parameters 3

$ticketarrayThe ticket the note went on.

$admin_idintThe staff member who added it.

$notearrayThe note data: its text, whether it is pinned, and attachments. ? It **can arrive empty** when the note could not be read. Test before reaching into it.

Return 1

voidThe return is ignored.

Listener PHP

```php
Hook::add('action:ticket.note_added', 10, function ($ticket, $admin_id, $note) {
    // The note CAN be empty.
    if (!$note) return;

    Acme::mirrorNote((int) ($ticket['id'] ?? 0), $note['message'] ?? '');
});
```

### Following an assignment

actionticket.assigned

`AdminTickets` zero means unassigned

Runs when a ticket is assigned to staff or unassigned.

Parameters 3

$ticketarrayThe ticket **before** the assignment.

$old_assigned_idintThe previous assignee; a **zero** means none.

$new_assigned_idintThe new assignee; a **zero** means it was unassigned.

Return 1

voidThe return is ignored.

Listener PHP

```php
Hook::add('action:ticket.assigned', 10,
    function ($ticket, $old_assigned_id, $new_assigned_id) {
        // Zero means it was unassigned.
        if (!$new_assigned_id) { Acme::backToPool((int) ($ticket['id'] ?? 0)); return; }

        Acme::notifyAgent($new_assigned_id, (int) ($ticket['id'] ?? 0));
    });
```

## Pitfalls

> **The note data can be empty**
> 
> The third parameter of the internal note hook is **empty** when the note could not be read. A listener reaching straight into it fails there; test on the first line.

> **The automatic reply hook carries one array**
> 
> Its siblings give the ticket, the reply id and the reply as separate parameters; the scheduled-task reply carries all of it in **a single context array**. A listener expecting the same signature works with empty values here.

## Related Articles

- Support Ticket Hooks
- [Customer Account Hooks](https://dev.wisecp.com/en/customer-account-hooks)
- [How Hooks Work](https://dev.wisecp.com/en/how-hooks-work)
