# Document Scheme

https://dev.wisecp.com/es/document-scheme

The ten endpoints that define which document is asked of whom: the field pool and the filters that use it.

## Overview

The document scheme has two layers. **Fields** are a shared pool — single inputs such as "passport copy" or "tax certificate". **Filters** pick a set out of that pool and carry the rules that decide *who* is asked for it.

One field can sit in several filters; that is what the pool is for. Updating a field reaches every filter using it at once.

## Reference

### Listing Filters

get/api/v1/admin/clients/document-filters

`Clients/GetDocumentFilters` admin

Returns the document filters. Each one carries a set of fields and the rules that decide who is asked for them.

Query parameters 3

statusstring`active` or `inactive`.

pageintDefaults to 1.

limitintDefaults to 25, maximum 100.

Response fields data[] — 5

idintFilter id.

namestringFilter name.

statusstring`active` or `inactive`.

fieldsint[]An **ordered** list of ids from the field pool. The order is what the client sees.

rulesobject[] 3 fieldsThe rules that decide who the filter applies to.

typestringWhat the rule looks at: `email_provider`, `vpn_proxy`, `account_age`, `service_count`, `total_spending`, `country_mismatch`, `country`.

valuestringDepends on the type: a list of domains, `yes`, a numeric threshold or a country id.

extrastringAn extra value for some types. Empty on most rules.

Errors 1

insufficient_scope403The key lacks the required scope.

Request cURL JavaScript PHP (HTTP) PHP (Internal)

```bash
curl -G 'https://panel.example.com/api/v1/admin/clients/document-filters' \
  -H "Authorization: Bearer $API_KEY" \
  -d status=active
```

```javascript
const url = new URL('https://panel.example.com/api/v1/admin/clients/document-filters');
url.searchParams.set('status', 'active');

const res  = await fetch(url, { headers: { Authorization: `Bearer ${apiKey}` } });
const body = await res.json();
```

```php
$url = 'https://panel.example.com/api/v1/admin/clients/document-filters?' . http_build_query(['status' => 'active']);

$ch = curl_init($url);
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()->GetDocumentFilters([], ['status' => 'active']);
```

### Adding a Filter

post/api/v1/admin/clients/document-filters

`Clients/CreateDocumentFilter` admin

Defines a new filter. The field ids have to exist in the pool.

Body 4

namestringrequiredFilter name.

fieldsint[]requiredOrdered field ids, for example `[3, 1]`.

activeboolDefaults to `false`; the filter is born `inactive`.

rulesobject[] 3 fieldsThe rules that apply it.

typestringWhat the rule looks at: `email_provider`, `vpn_proxy`, `account_age`, `service_count`, `total_spending`, `country_mismatch`, `country`.

valuestringDepends on the type: a list of domains, `yes`, a numeric threshold or a country id.

extrastringAn extra value for some types. Empty on most rules.

Response fields data — 5

idintId of the new filter.

namestringFilter name.

statusstring`active` or `inactive`. A new filter is born inactive unless you sent `active`.

fieldsint[]The ordered field ids, as stored.

rulesobject[]The stored rules — same `type` / `value` / `extra` shape as the listing.

Errors 5

name_required422`name` was empty.

fields_required422`fields` was empty or malformed.

fields_invalid422None of the ids exist in the field pool.

filter_add_failed500The record could not be created.

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-filters' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"name":"High spend","fields":[3,1],"active":true,"rules":[{"type":"total_spending","value":"5000","extra":""}]}'
```

```javascript
const res = await fetch('https://panel.example.com/api/v1/admin/clients/document-filters', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({"name":"High spend","fields":[3,1],"active":true,"rules":[{"type":"total_spending","value":"5000","extra":""}]}),
});

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

```php
$ch = curl_init('https://panel.example.com/api/v1/admin/clients/document-filters');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'name'   => 'High spend',
        'fields' => [3, 1],
        'active' => true,
    ]),
]);

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

```php
$response = Api::Clients()->CreateDocumentFilter([
    'name'   => 'High spend',
    'fields' => [3, 1],
    'active' => true,
    'rules'  => [
        ['type' => 'total_spending', 'value' => '5000', 'extra' => ''],
    ],
]);
```

### Filter Detail

get/api/v1/admin/clients/document-filters/{fid}

`Clients/GetDocumentFilter` admin

Returns one filter; the schema is the same as in the list.

Response fields data — 5

idintFilter id.

namestringFilter name.

statusstring`active` or `inactive`.

fieldsint[]The ordered field ids, exactly as the listing returns them.

rulesobject[]The rules, same `type` / `value` / `extra` shape as the listing.

Errors 2

not_found404No such 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/document-filters/7' \
  -H "Authorization: Bearer $API_KEY"
```

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

```php
$ch = curl_init('https://panel.example.com/api/v1/admin/clients/document-filters/7');
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()->GetDocumentFilter(['fid' => 7]);
```

### Updating a Filter

patch/api/v1/admin/clients/document-filters/{fid}

`Clients/UpdateDocumentFilter` admin

Changes the filter name, the field order, the status or the rules.

Body at least one

namestringFilter name.

fieldsint[]The new ordered field list. What you send replaces the old list.

activeboolTurns the filter on or off.

rulesobject[]The new rule list. This replaces as well, it does not append.

Response fields data — 5

idintFilter id.

namestringFilter name after the update.

statusstring`active` or `inactive`.

fieldsint[]The stored field order — read it back to see what the replace actually left.

rulesobject[]The stored rules; an empty array means the filter now applies to everyone.

Errors 5

not_found404No such filter.

name_required422`name` was sent empty.

fields_required422`fields` was sent empty or malformed.

fields_invalid422An id does not exist in the field pool.

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/document-filters/7' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"active":false}'
```

```javascript
const res = await fetch('https://panel.example.com/api/v1/admin/clients/document-filters/7', {
  method: 'PATCH',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ active: false }),
});

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

```php
$ch = curl_init('https://panel.example.com/api/v1/admin/clients/document-filters/7');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST  => 'PATCH',
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode(['active' => false]),
]);

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

```php
$response = Api::Clients()->UpdateDocumentFilter([
    'fid'    => 7,
    'active' => false,
]);
```

### Deleting a Filter

delete/api/v1/admin/clients/document-filters/{fid}

`Clients/DeleteDocumentFilter` admin

Deletes the filter. The field pool is untouched; the fields stay in whatever other filters use them.

Response fields data — 2

deletedboolWhether the delete succeeded.

idintId of the deleted filter.

Errors 1

not_found404No such filter.

Request cURL JavaScript PHP (HTTP) PHP (Internal)

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

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

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

```php
$ch = curl_init('https://panel.example.com/api/v1/admin/clients/document-filters/7');
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()->DeleteDocumentFilter(['fid' => 7]);
```

### Listing Fields

get/api/v1/admin/clients/document-fields

`Clients/GetDocumentFields` admin

Returns the shared field pool. Filters pick their fields from here.

Query parameters 4

statusstring`active` or `inactive`.

pageintDefaults to 1.

limitintDefaults to 25, maximum 100.

typestringFilters by input type.

Response fields data[] — 8

idintField id. A filter's `fields` list points here.

statusstring`active` or `inactive`. An inactive field is not shown to the client.

typestring`input`, `textarea`, `selectbox`, `radio`, `checkbox`, `file`.

labelsobjectLabel per language, for example `{"en": "Passport copy"}`.

optionsobjectOptions per language. Only meaningful on the choice types.

allowed_extstringAllowed file extensions. Only on the `file` type.

max_sizeintMaximum file size in MB. Only on the `file` type.

used_inobject[]The filters using this field: `[{ id, name }]`.

Errors 1

insufficient_scope403The key lacks the required scope.

Request cURL JavaScript PHP (HTTP) PHP (Internal)

```bash
curl -G 'https://panel.example.com/api/v1/admin/clients/document-fields' \
  -H "Authorization: Bearer $API_KEY" \
  -d type=file
```

```javascript
const url = new URL('https://panel.example.com/api/v1/admin/clients/document-fields');
url.searchParams.set('type', 'file');

const res  = await fetch(url, { headers: { Authorization: `Bearer ${apiKey}` } });
const body = await res.json();
```

```php
$url = 'https://panel.example.com/api/v1/admin/clients/document-fields?' . http_build_query(['type' => 'file']);

$ch = curl_init($url);
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()->GetDocumentFields([], ['type' => 'file']);
```

### Adding a Field

post/api/v1/admin/clients/document-fields

`Clients/CreateDocumentField` admin

Adds a field to the pool. The label has to be given in at least one language.

Body 6

typestringrequiredInput type.

labelsobjectrequiredLabel per language; at least one.

activeboolDefaults to `true`.

optionsobjectOptions per language. In practice required on the choice types.

allowed_extstringAllowed extensions. Only on the `file` type.

max_sizeintLimit in MB. Only on the `file` type.

Response fields data — 7

idintId of the new field.

statusstring`active` or `inactive`.

typestringThe stored input type.

labelsobjectThe stored labels, per language.

optionsobjectThe stored choices, per language.

allowed_extstringAllowed file extensions.

max_sizeintMaximum file size in MB.

used_in—Not part of this answer: a newly created field belongs to no filter yet.

Errors 4

type_invalid422`type` is not one of the allowed input types.

labels_required422No language carried a non-empty label.

field_add_failed500The record could not be created.

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-fields' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"type":"file","labels":{"en":"Passport copy"},"allowed_ext":"jpg,png,pdf","max_size":5}'
```

```javascript
const res = await fetch('https://panel.example.com/api/v1/admin/clients/document-fields', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    type: 'file',
    labels: {"en":"Passport copy"},
    allowed_ext: 'jpg,png,pdf',
    max_size: 5,
  }),
});

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

```php
$ch = curl_init('https://panel.example.com/api/v1/admin/clients/document-fields');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'type'        => 'file',
        'labels'      => ['en' => 'Passport copy'],
        'allowed_ext' => 'jpg,png,pdf',
        'max_size'    => 5,
    ]),
]);

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

```php
$response = Api::Clients()->CreateDocumentField([
    'type'        => 'file',
    'labels'      => ['en' => 'Passport copy'],
    'allowed_ext' => 'jpg,png,pdf',
    'max_size'    => 5,
]);
```

### Field Detail

get/api/v1/admin/clients/document-fields/{fid}

`Clients/GetDocumentField` admin

Returns one field. `used_in` comes with it, showing which filters use it.

Response fields data — 8

idintField id — this is what a filter's `fields` list refers to.

statusstring`active` or `inactive`. An inactive field is not shown to clients even where a filter still lists it.

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

labelsobjectThe label per language, for example `{ "en": "Passport copy" }`.

optionsobjectPer-language choices for the list types. Empty on the other types.

allowed_extstringAllowed file extensions — `file` type only.

max_sizeintMaximum file size in MB — `file` type only.

used_inobject[]The filters referencing this field, as `{ id, name }`. Read it before deleting — every one of them loses the field.

Errors 2

not_found404No such field.

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-fields/3' \
  -H "Authorization: Bearer $API_KEY"
```

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

```php
$ch = curl_init('https://panel.example.com/api/v1/admin/clients/document-fields/3');
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()->GetDocumentField(['fid' => 3]);

// Before deleting: which filters hold this field?
$usedIn = $response['data']['used_in'] ?? [];
```

### Updating a Field

patch/api/v1/admin/clients/document-fields/{fid}

`Clients/UpdateDocumentField` admin

Updates the field. The change reaches every filter using it at once.

Body 6

typestringrequiredInput type. Required even on an update — this endpoint rewrites the field rather than patching single keys.

labelsobjectrequiredLabel per language; at least one must be non-empty.

activeboolTurns the field on or off. Defaults to `true`.

optionsobjectChoices per language. In practice required on the choice types.

allowed_extstringAllowed extensions. Only on the `file` type.

max_sizeintLimit in MB. Only on the `file` type.

Response fields data — 7

idintField id.

statusstring`active` or `inactive`.

typestringThe stored input type.

labelsobjectThe stored labels, per language.

optionsobjectThe stored choices, per language.

allowed_extstringAllowed file extensions.

max_sizeintMaximum file size in MB.

Errors 4

not_found404No such field.

type_invalid422`type` is not one of the allowed input types.

labels_required422No language carried a non-empty label.

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/document-fields/3' \
  -H "Authorization: Bearer $API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"max_size":10}'
```

```javascript
const res = await fetch('https://panel.example.com/api/v1/admin/clients/document-fields/3', {
  method: 'PATCH',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ max_size: 10 }),
});

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

```php
$ch = curl_init('https://panel.example.com/api/v1/admin/clients/document-fields/3');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST  => 'PATCH',
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode(['max_size' => 10]),
]);

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

```php
$response = Api::Clients()->UpdateDocumentField([
    'fid'      => 3,
    'max_size' => 10,
]);
```

### Deleting a Field

delete/api/v1/admin/clients/document-fields/{fid}

`Clients/DeleteDocumentField` admin affects filters

Removes the field from the pool.

Response fields data — 2

deletedboolWhether the delete succeeded.

idintId of the deleted field.

Errors 1

not_found404No such field.

Request cURL JavaScript PHP (HTTP) PHP (Internal)

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

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

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

```php
$ch = curl_init('https://panel.example.com/api/v1/admin/clients/document-fields/3');
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()->DeleteDocumentField(['fid' => 3]);
```

## Pitfalls

> **The field list is ordered**
> 
> `fields` is not a set but an **ordered** list, and the client sees the fields in that order. On update the list you send replaces the old one; it does not append to it.

> **A field is shared, not copied**
> 
> Updating or deleting a field affects **every** filter that uses it. Look at `used_in` on the field detail before you delete.

> **A new filter is born switched off**
> 
> Leave `active` out and the filter is created as `inactive`, asking nobody for anything. Creating one and walking away leaves a verification that never runs.

## Related Articles

- [Client Endpoints](https://dev.wisecp.com/en/client-endpoints)
- [Request and Response Format](https://dev.wisecp.com/en/request-and-response-format)
