# Client Documents

https://dev.wisecp.com/es/client-documents

The six endpoints that read a client's submitted documents, approve or reject them, and manage the canned rejection reasons.

## Overview

A client fills in the fields the document scheme asks for and submits them; these endpoints read those submissions and settle them. The scheme itself is a separate job and lives in **Document Scheme**.

Each record maps to one field and carries its own status. A client can have some documents approved while others are still waiting.

## Reference

### Fetching Documents

get/api/v1/admin/clients/{id}/documents

`Clients/GetClientDocuments` admin marks as read

Returns the document records the client submitted. Records hang off the field id and every filter is merged into one list.

Query parameter 1

filter_idintIf given, the list narrows to the records that came from that filter.

Response fields data — 2

filtersobject[]The filters the returned records came from: `[{ id, name }]`.

recordsobject[] 8 fieldsThe document records.

idintId of the document record. This is the key you use in the review request.

field_keyintId of the field in the shared pool.

field_namestringThe field label. Refreshed from the pool; if the field was deleted, the copy taken at submission comes back.

field_typestringInput type: `input`, `textarea`, `selectbox`, `radio`, `checkbox`, `file`.

field_valuestringThe value the client submitted. On file fields, JSON carrying the upload identifier.

filter_idintThe filter this record came from.

statusstring`awaiting`, `verified` or `unverified`.

status_msgstringThe review note; a rejection reason goes here.

Errors 2

not_found404No such client or record.

insufficient_scope403The key lacks the required scope.

Request cURL JavaScript PHP (HTTP) PHP (Internal)

```bash
curl 'https://panel.example.com/api/v1/admin/clients/42/documents' \
  -H "Authorization: Bearer $API_KEY"
```

```javascript
const res  = await fetch('https://panel.example.com/api/v1/admin/clients/42/documents', {
  headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();

const waiting = body.data.records.filter((r) => r.status === 'awaiting');
```

```php
$ch = curl_init('https://panel.example.com/api/v1/admin/clients/42/documents');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
```

```php
$response = Api::Clients()->GetClientDocuments(['id' => 42]);

$waiting = [];
foreach ($response['data']['records'] as $record) {
    if ($record['status'] === 'awaiting') {
        $waiting[] = $record['id'];
    }
}
```

### Reviewing Documents

patch/api/v1/admin/clients/{id}/documents

`Clients/ReviewClientDocuments` admin notifies the client

Updates the verification status of several records at once. Every record whose status changes sends the client a notification.

Body 1

statusesobjectrequired 2 fieldsA map keyed by record id: `{"<recordId>": {"status": …, "message": …}}`.

statusstringrequired`verified`, `unverified` or `awaiting`. A record carrying anything else is skipped in silence.

messagestringThe review note. Put the reason here when rejecting; the client sees it.

Response fields data — 3

reviewedboolWhether the review was processed.

verifiedintHow many records were approved.

rejectedintHow many records were rejected.

Errors 2

not_found404No such client, or the client has no records at all.

insufficient_scope403The key lacks the required scope.

Request cURL JavaScript PHP (HTTP) PHP (Internal)

```bash
curl -X PATCH 'https://panel.example.com/api/v1/admin/clients/42/documents' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"statuses":{"51":{"status":"verified"},"52":{"status":"unverified","message":"The document is unreadable, please send a clearer copy"}}}'
```

```javascript
const res = await fetch('https://panel.example.com/api/v1/admin/clients/42/documents', {
  method: 'PATCH',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    statuses: {
      51: { status: 'verified' },
      52: { status: 'unverified', message: 'The document is unreadable, please send a clearer copy' },
    },
  }),
});

const body = await res.json();
```

```php
$ch = curl_init('https://panel.example.com/api/v1/admin/clients/42/documents');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST  => 'PATCH',
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'statuses' => [
            51 => ['status' => 'verified'],
            52 => ['status' => 'unverified', 'message' => 'The document is unreadable, please send a clearer copy'],
        ],
    ]),
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
```

```php
$response = Api::Clients()->ReviewClientDocuments([
    'id'       => 42,
    'statuses' => [
        51 => ['status' => 'verified'],
        52 => ['status' => 'unverified', 'message' => 'The document is unreadable, please send a clearer copy'],
    ],
]);

$rejected = $response['data']['rejected'] ?? 0;
```

Response 200

```json
{
  "data": {
    "reviewed": true,
    "verified": 1,
    "rejected": 1
  }
}
```

### Deleting a Document Record

delete/api/v1/admin/clients/{id}/documents/{record_id}

`Clients/DeleteClientDocument` admin

Deletes a single document record.

Response fields data — 2

deletedboolWhether the delete succeeded.

idintId of the deleted record.

Errors 3

not_found404No such client.

record_required422The record id was missing.

insufficient_scope403The key lacks the required scope.

Request cURL JavaScript PHP (HTTP) PHP (Internal)

```bash
curl -X DELETE 'https://panel.example.com/api/v1/admin/clients/42/documents/51' \
  -H "Authorization: Bearer $API_KEY"
```

```javascript
const res = await fetch('https://panel.example.com/api/v1/admin/clients/42/documents/51', {
  method: 'DELETE',
  headers: { Authorization: `Bearer ${apiKey}` },
});

const body = await res.json();
```

```php
$ch = curl_init('https://panel.example.com/api/v1/admin/clients/42/documents/51');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST  => 'DELETE',
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
```

```php
$response = Api::Clients()->DeleteClientDocument([
    'id'        => 42,
    'record_id' => 51,
]);
```

### Listing Rejection Reasons

get/api/v1/admin/clients/document-rejection-reasons

`Clients/GetDocumentRejectionReasons` admin

Returns the saved rejection reasons. These are the canned texts used while reviewing.

Response fields data[]

datastring[]A plain list of reason texts — there are no ids here, the text itself is the identity.

Errors 1

insufficient_scope403The key lacks the required scope.

Request cURL JavaScript PHP (HTTP) PHP (Internal)

```bash
curl 'https://panel.example.com/api/v1/admin/clients/document-rejection-reasons' \
  -H "Authorization: Bearer $API_KEY"
```

```javascript
const res  = await fetch('https://panel.example.com/api/v1/admin/clients/document-rejection-reasons', {
  headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();
```

```php
$ch = curl_init('https://panel.example.com/api/v1/admin/clients/document-rejection-reasons');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
```

```php
$response = Api::Clients()->GetDocumentRejectionReasons();
```

### Adding a Rejection Reason

post/api/v1/admin/clients/document-rejection-reasons

`Clients/AddDocumentRejectionReason` admin

Adds a new canned reason to the list.

Body 1

valuestringrequiredThe reason text.

Response fields data[]

datastring[]The whole list after the add, not the added text alone.

Errors 2

value_required422`value` was empty.

insufficient_scope403The key lacks the required scope.

Request cURL JavaScript PHP (HTTP) PHP (Internal)

```bash
curl -X POST 'https://panel.example.com/api/v1/admin/clients/document-rejection-reasons' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"value":"The document is unreadable, please send a clearer copy"}'
```

```javascript
const res = await fetch('https://panel.example.com/api/v1/admin/clients/document-rejection-reasons', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ value: 'The document is unreadable, please send a clearer copy' }),
});

const body = await res.json();
```

```php
$ch = curl_init('https://panel.example.com/api/v1/admin/clients/document-rejection-reasons');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode(['value' => 'The document is unreadable, please send a clearer copy']),
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
```

```php
$response = Api::Clients()->AddDocumentRejectionReason(['value' => 'The document is unreadable, please send a clearer copy']);
```

### Deleting a Rejection Reason

delete/api/v1/admin/clients/document-rejection-reasons

`Clients/DeleteDocumentRejectionReason` admin deleted by text

Removes a reason from the list. You send the text itself, not an id.

Body 1

valuestringrequiredThe text of the reason to remove. It has to match the stored one exactly.

Response fields data[]

datastring[]The reasons that remain. A text that did not match leaves the list unchanged — compare it to know whether anything went.

Errors 2

value_required422`value` was empty.

insufficient_scope403The key lacks the required scope.

Request cURL JavaScript PHP (HTTP) PHP (Internal)

```bash
curl -X DELETE 'https://panel.example.com/api/v1/admin/clients/document-rejection-reasons' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"value":"The document is unreadable, please send a clearer copy"}'
```

```javascript
const res = await fetch('https://panel.example.com/api/v1/admin/clients/document-rejection-reasons', {
  method: 'DELETE',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ value: 'The document is unreadable, please send a clearer copy' }),
});

const body = await res.json();
```

```php
$ch = curl_init('https://panel.example.com/api/v1/admin/clients/document-rejection-reasons');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST  => 'DELETE',
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode(['value' => 'The document is unreadable, please send a clearer copy']),
]);

$body = json_decode(curl_exec($ch), true);
curl_close($ch);
```

```php
$response = Api::Clients()->DeleteDocumentRejectionReason(['value' => 'The document is unreadable, please send a clearer copy']);
```

## Pitfalls

> **Listing marks the records as read**
> 
> The fetch endpoint is not read-only: calling it marks the records as read. An integration polling this while watching the pending-document badge will clear that badge on every poll.

> **A review notifies the client**
> 
> Every record whose status changes sends an approval or rejection notice. Sending the same status twice counts as no change, but rejecting a record by mistake and fixing it produces **two** notices.

> **A rejection reason is deleted by its text**
> 
> The delete request takes `value`, not an id, and the text has to match exactly. Even one space of difference will not find the record.

## Related Articles

- [Document Scheme](https://dev.wisecp.com/en/document-scheme)
- [Client Endpoints](https://dev.wisecp.com/en/client-endpoints)
