# Personal Data Hooks

https://dev.wisecp.com/es/personal-data-hooks

The nine hooks over consent, exports, data requests and identity documents.

## Overview

The four moments of personal data: a customer **gives consent**, **exports** their data, **asks for it to go**, and **submits identity documents**.

Two hooks deliberately withhold content: the document on export, the identity paper on submission. Listeners get only a summary, because spreading that data to a second place works against the point.

## Reference

### Following a consent change

actionuser.gdpr_consent_changed

`AccountPrivacy` proof stamp

Runs when a customer gives or withdraws consent for data processing. It fires only on a **real change**: saving the same value again does not raise it.

Parameters 3

$uidintThe customer whose consent changed.

$statusintThe new state: one for given, zero for withdrawn. Since the hook fires only on a change, the previous state is always the opposite.

$given_atstringThe stamp written to the record. This is the time part of the consent proof: use this value rather than reading it back.

Return 1

voidThe return is ignored. The consent is already written; this cannot block it.

Listener PHP

```php
Hook::add('action:user.gdpr_consent_changed', 10, function ($uid, $status, $given_at) {
    // Take the proof stamp from here rather than reading it back.
    Acme::recordConsent($uid, (bool) $status, $given_at);
});
```

### Following a data export

actionuser.gdpr_exported

`AccountPrivacy` summary only

Runs when a customer exports their own data.

Parameters 2

$uidintThe customer exporting.

$metaarrayA **summary** of the export: its format and size. The document content is withheld on purpose: passing it would hand every listener the customer’s personal data.

Return 1

voidThe return is ignored.

Listener PHP

```php
Hook::add('action:user.gdpr_exported', 10, function ($uid, $meta) {
    // The document content is deliberately withheld.
    Acme::auditExport($uid, (int) ($meta['size'] ?? 0));
});
```

### Following a data request being opened

actionuser.gdpr_request_created

`AccountPrivacy` two kinds

Runs when a customer opens a deletion or anonymisation request.

Parameters 2

$uidintThe customer opening it.

$typestringThe kind: `remove` for deletion, `anonymize` for anonymisation.

Return 1

voidThe return is ignored.

Listener PHP

```php
Hook::add('action:user.gdpr_request_created', 10, function ($uid, $type) {
    Acme::openComplianceCase($uid, $type);
});
```

### Following a data request being cancelled

actionuser.gdpr_request_cancelled

`AccountPrivacy` withdrawn

Runs when a customer withdraws a request they opened.

Parameters 2

$uidintThe owner of the request.

$request_idintThe id of the cancelled request.

Return 1

voidThe return is ignored.

Listener PHP

```php
Hook::add('action:user.gdpr_request_cancelled', 10, function ($uid, $request_id) {
    Acme::closeComplianceCase($request_id);
});
```

### Following a data request being carried out

actionuser.gdpr_processed

`AdminUsers` cannot be undone

Runs after a data request is carried out. **Deletion and anonymisation cannot be undone**: by this point the data is already gone.

Parameters 3

$request_idintThe request id.

$user_idintThe customer concerned.

$actionstringWhat was carried out: removal, anonymisation, destruction or cancellation.

Return 1

voidThe return is ignored.

Listener PHP

```php
Hook::add('action:user.gdpr_processed', 10, function ($request_id, $user_id, $action) {
    // The data is gone: clear your own copy as well.
    if ($action !== 'cancelled') Acme::purgeMirror($user_id);
});
```

### Following documents being submitted

actionuser.documents_submitted

`AccountDocuments` reference only

Runs when a customer submits identity documents.

Parameters 2

$uidintThe customer submitting. This flow is for the person themselves: a sub-user cannot submit on somebody else’s behalf.

$doc_metaarrayA summary of the submission. ? **No document content** is here, only references and metadata. It is deliberate, so identity documents are not handed to listeners.

Return 1

voidThe return is ignored. The records are already written; this cannot block the submission.

Listener PHP

```php
Hook::add('action:user.documents_submitted', 10, function ($uid, $doc_meta) {
    // No document content, only the reference.
    Acme::queueReview($uid);
});
```

### Widening the document field types

filteruser.document_field_types

`AdminUsers` passed by link

Runs while the list of types offered when defining a document field is built. Add a field type of your own here.

Parameters 1

$typesarrayby linkType key against the visible label.

Return 1

voidThe return is ignored; you write over the data.

Listener PHP

```php
Hook::add('filter:user.document_field_types', 10, function (&$types) {
    $types['acme-iban'] = 'Acme IBAN check';
});
```

### Changing a document field definition

filteruser.document_field_input

`AdminUsers` passed by link

Runs before a document field is saved. Fill in the settings of a type you added here.

Parameters 2

$valuesarrayby linkThe row to be written: status, type, labels, options, accepted extensions and size limit.

$typestringby linkThe field type. It is there so you can tell whether this is your type: check it first.

Return 1

voidThe return is ignored; you write over the data.

Listener PHP

```php
Hook::add('filter:user.document_field_input', 10, function (&$values, &$type) {
    // Check whether it is your type first.
    if ($type !== 'acme-iban') return;

    $values['max_size'] = 512;
});
```

### Changing the document records view

filteruser.document_records_view

`AdminUsers` personal data

Runs when an administrator looks at a customer’s documents, before the records reach the screen.

Parameters 2

$recordsarrayby linkThe document records: field name, value, status. They carry identity details: this is where masking belongs.

$user_idintby linkThe customer being looked at; for reading only.

Return 1

voidThe return is ignored; you write over the data.

Listener PHP

```php
Hook::add('filter:user.document_records_view', 10, function (&$records, &$user_id) {
    // Hide the identity number from eyes without clearance.
    if (Acme::canSeeFullId()) return;

    foreach ($records as $i => $r) $records[$i]['field_value'] = Acme::mask($r['field_value'] ?? '');
});
```

## Pitfalls

> **Content is withheld on purpose**
> 
> The export hook does **not** carry the document, and the submission hook does not carry the identity paper; both give a summary only. That is design, not omission: handing the data to listeners works against the protection. If you need the content, read it through your own permitted path.

> **A processed request cannot be undone**
> 
> By the time the removal or anonymisation hook runs the data is **already gone**. You must clear your own copy as well, or the customer's data lives on with you.

## Related Articles

- [Customer Account Hooks](https://dev.wisecp.com/en/customer-account-hooks)
- [Invoice Lifecycle Hooks](https://dev.wisecp.com/en/invoice-lifecycle-hooks)
- [How Hooks Work](https://dev.wisecp.com/en/how-hooks-work)
