# Quick Start

## Welcome to the Signitic API

Welcome to the Signitic API. Here you will find everything you need to get up and running.

## Get your API keys

Your API requests are authenticated using API keys. Any request that doesn't include an API key will return an error.

You can retrieve and regenerate your API key from the [**Signitic app**](https://app.signitic.com).


# Core API

Use the Core API to manage users, generate signatures, and receive public webhook payloads for a standard Signitic workspace.

{% content-ref url="/pages/3eIsSjTRj5N3kv8xk8VK" %}
[Users](/reference/core-api/users)
{% endcontent-ref %}

{% content-ref url="/pages/GG6vxN2BoGHVqwzLVub8" %}
[User requests](/reference/core-api/user-requests)
{% endcontent-ref %}

{% content-ref url="/pages/DwtP4dz4UEWV7VX3KIGs" %}
[Signatures](/reference/core-api/signatures)
{% endcontent-ref %}

{% content-ref url="/pages/VwAP3YWSmlq2oGY3UryJ" %}
[Desktop](/reference/core-api/desktop)
{% endcontent-ref %}

{% content-ref url="/pages/FM0tgzWJK0pFfOP1Qsps" %}
[Webhooks](/reference/core-api/webhooks)
{% endcontent-ref %}


# Users

Manage users from a standard Signitic workspace.

Use these endpoints to:

* list users available in the workspace
* retrieve one user by email
* create users in batch
* update users in batch
* delete users in batch

{% content-ref url="/pages/ayLeXyaQ0KSaV0uu3PlJ" %}
[Get users](/reference/core-api/users/list-users)
{% endcontent-ref %}

{% content-ref url="/pages/YD0afj6ne5hdIaZXNCZU" %}
[Get one user](/reference/core-api/users/get-user)
{% endcontent-ref %}

{% content-ref url="/pages/4nnzmt8N102pyqBPy8nH" %}
[Creating users](/reference/core-api/users/creating-users)
{% endcontent-ref %}

{% content-ref url="/pages/0ZYSyOxX5EEeArG8Bn91" %}
[Updating users](/reference/core-api/users/updating-users)
{% endcontent-ref %}

{% content-ref url="/pages/DdPMFfa4nx2AAFEY2wfF" %}
[Delete users](/reference/core-api/users/delete-users)
{% endcontent-ref %}


# Get users

Retrieve the list of users.

## Get users

<mark style="color:blue;">`GET`</mark> `https://api.signitic.app/users`

## Headers

| Name                                        | Type   | Description  |
| ------------------------------------------- | ------ | ------------ |
| x-api-key<mark style="color:red;">\*</mark> | String | Your API key |

## Success response

{% tabs %}
{% tab title="200: OK Return JSON users" %}

```json
{
  "success": true,
  "users": [
    {
      "email": "user1@signitic.fr",
      "enabled": 1
    },
    {
      "email": "user2@signitic.fr",
      "enabled": 2
    }
  ]
}
```

{% endtab %}
{% endtabs %}

## Error responses

| Status | Internal code | When                               |
| ------ | ------------- | ---------------------------------- |
| 401    | `99`          | The API key is missing or invalid. |

In the list response, `enabled` returns the API status value: `1` for active users and `2` for inactive users.


# Get one user

Retrieve one user.

## Get one user

<mark style="color:blue;">`GET`</mark> `https://api.signitic.app/users/:email`

## Headers

| Name                                        | Type   | Description  |
| ------------------------------------------- | ------ | ------------ |
| x-api-key<mark style="color:red;">\*</mark> | String | Your API key |

## Path parameters

| Name                                    | Type | Description |
| --------------------------------------- | ---- | ----------- |
| email<mark style="color:red;">\*</mark> |      | User email  |

## Success response

{% tabs %}
{% tab title="200: OK Return JSON user" %}

```json
{
  "success": true,
  "code": 200,
  "data": {
    "id": 1234567,
    "email": "user1@signitic.fr",
    "firstname": "Johanna",
    "lastname": "Doe",
    "enabled": true,
    "group": "Group",
    "picture": null,
    "phone": "+33 1 34 33 22 33",
    "mobile": "+33 6 34 33 22 33",
    "title": "CEO",
    "unit": "Department",
    "address": "1600 Pennsylvania Avenue NW<br/>Washington, DC 20500,<br/>USA",
    "postal_code": "75008",
    "city": "Paris",
    "formula": "Kind regards,<br/>John",
    "vcard_url": "https://signitic.cards/placeholder/johanna.doe",
    "calendar_link": "calendar.com",
    "github_link": "github.com",
    "twitter_link": "twitter.com",
    "facebook_link": "facebook.com",
    "linkedin_link": "linkedin.com",
    "instagram_link": "instagram.com",
    "xing_link": "xing.com",
    "messenger_link": null,
    "threads_link": "threads.net",
    "strava_link": "strava.com",
    "whatsapp_link": "whatsapp.com",
    "pending_change_requests": [],
    "extra_1": "extra field 1",
    "extra_2": "extra field 2",
    "extra_3": "extra field 3",
    "extra_4": "extra field 4",
    "extra_5": "extra field 5",
    "extra_6": "extra field 6",
    "extra_7": "extra field 7",
    "extra_8": "extra field 8",
    "extra_9": "extra field 9",
    "extra_10": "extra field 10",
    "extra_20": "extra field 20",
    "path": {
      "parent_entity": "Company",
      "entity": "Washington Branch",
      "group": "Marketing Direction"
    }
  }
}
```

{% endtab %}
{% endtabs %}

## Error responses

| Status | Internal code | When                                              |
| ------ | ------------- | ------------------------------------------------- |
| 400    | `101`         | The email format is invalid.                      |
| 401    | `99`          | The API key is missing or invalid.                |
| 404    | `102`         | The email does not match a user in the workspace. |


# Creating users

The User object represents the data you can send to Signitic.

{% hint style="danger" %}
**`POST /users` requests are limited to a raw JSON payload size of 3 MB.**
{% endhint %}

## Endpoint

<mark style="color:blue;">`POST`</mark> `https://api.signitic.app/users`

## Headers

| Name                                        | Type   | Description  |
| ------------------------------------------- | ------ | ------------ |
| x-api-key<mark style="color:red;">\*</mark> | String | Your API key |

`POST /users` creates new users and also updates existing users matched by email. The limit applies to the raw request body size, not to a fixed number of users.

## Request body

### Example

```json
{
  "users": [
    {
      "email": "user1@signitic.fr",
      "enabled": true,
      "path": {
        "group": "Marketing Direction",
        "parent_entity": "Company",
        "entity": "Paris Office"
      },
      "firstname": "Johanna",
      "lastname": "Doe",
      "phone": "+33 1 34 33 22 33",
      "mobile": "+33 6 34 33 22 33",
      "department": "Marketing",
      "jobtitle": "CEO",
      "formula": "Kind regards,<br/>John",
      "vcard_user": true,
      "calendar_link": "calendar.com",
      "github_link": "github.com",
      "linkedin_link": "linkedin.com",
      "whatsapp_link": "whatsapp.com",
      "facebook_link": "facebook.com",
      "instagram_link": "instagram.com",
      "twitter_link": "twitter.com",
      "xing_link": "xing.com",
      "messenger_link": "messenger.com",
      "threads_link": "threads.net",
      "strava_link": "strava.com",
      "address": "1600 Pennsylvania Avenue NW<br/>Washington, DC 20500,<br/>USA",
      "postal_code": "75008",
      "city": "Paris",
      "extra_1": "extra field 1",
      "extra_2": "extra field 2",
      "extra_10": "extra field 10",
      "extra_20": "extra field 20"
    }
  ]
}
```

### Path dispatch

`path` is resolved from top to bottom: `parent_entity` (or legacy `parent`), then `entity`, then `group`. Empty strings are ignored. Signitic reuses the deepest existing matching branch in the group tree and creates only the missing groups. If no part of the path exists, the full branch is created from the workspace root. The user is then assigned to the lowest group in the resolved path. Groups created this way are blank new groups, not copies of existing ones. Skipping a level is allowed, but not recommended because it makes dispatch harder to understand and debug.

Because dispatch is path-based, different segment combinations can resolve to the same final group.

### Notes

* `extra_1` to `extra_20` are supported.
* Social fields such as `xing_link`, `messenger_link`, `threads_link`, `strava_link`, and `bluesky_link` are accepted in the payload.

## Success response

### `200 OK`

```json
{
  "success": true,
  "code": 200,
  "value": {
    "added": 1,
    "edited": 0
  }
}
```

## Error responses

| Status | Internal code | When                                |
| ------ | ------------- | ----------------------------------- |
| 400    | `103`         | The request body is not valid JSON. |
| 401    | `99`          | The API key is missing or invalid.  |
| 422    | `104`         | The raw JSON payload exceeds 3 MB.  |


# Updating users

{% hint style="danger" %}
**`POST /users` requests are limited to a raw JSON payload size of 3 MB.**
{% endhint %}

## Endpoint

<mark style="color:blue;">`POST`</mark> `https://api.signitic.app/users`

## Headers

| Name                                        | Type   | Description  |
| ------------------------------------------- | ------ | ------------ |
| x-api-key<mark style="color:red;">\*</mark> | String | Your API key |

This endpoint supports partial updates. Only the fields included in the payload are updated. The limit applies to the raw request body size, not to a fixed number of users.

## Request body

### Example

```json
{
  "users": [
    {
      "email": "user1@signitic.fr",
      "firstname": "Johanna",
      "postal_code": "75008",
      "city": "Paris"
    },
    {
      "email": "user2@signitic.fr",
      "jobtitle": "Internship",
      "threads_link": "threads.net"
    }
  ]
}
```

### Path dispatch

If `path` is included in the payload, it is resolved from top to bottom: `parent_entity` (or legacy `parent`), then `entity`, then `group`. Empty strings are ignored. Signitic reuses the deepest existing matching branch in the group tree and creates only the missing groups. If no part of the path exists, the full branch is created from the workspace root. The user is then assigned to the lowest group in the resolved path. Groups created this way are blank new groups, not copies of existing ones. Skipping a level is allowed, but not recommended because it makes dispatch harder to understand and debug.

Because dispatch is path-based, different segment combinations can resolve to the same final group.

## Success response

### `200 OK`

```json
{
  "success": true,
  "code": 200,
  "value": {
    "added": 0,
    "edited": 2
  }
}
```

## Error responses

| Status | Internal code | When                                |
| ------ | ------------- | ----------------------------------- |
| 400    | `103`         | The request body is not valid JSON. |
| 401    | `99`          | The API key is missing or invalid.  |
| 422    | `104`         | The raw JSON payload exceeds 3 MB.  |


# Delete users

{% hint style="danger" %}
**`DELETE /users` requests are limited to a raw JSON payload size of 3 MB.**
{% endhint %}

## Endpoint

<mark style="color:blue;">`DELETE`</mark> `https://api.signitic.app/users`

## Headers

| Name                                        | Type   | Description  |
| ------------------------------------------- | ------ | ------------ |
| x-api-key<mark style="color:red;">\*</mark> | String | Your API key |

## Request body

### Example

```json
{
  "users": [
    {
      "email": "user1@signitic.fr"
    },
    {
      "email": "user2@signitic.fr"
    }
  ]
}
```

Deleting a user marks it for deletion one month later. The limit applies to the raw request body size, not to a fixed number of users.

## Success response

### `200 OK`

```json
{
  "success": true,
  "value": {
    "deleted": 2
  }
}
```

## Error responses

| Status | Internal code | When                                |
| ------ | ------------- | ----------------------------------- |
| 400    | `103`         | The request body is not valid JSON. |
| 401    | `99`          | The API key is missing or invalid.  |
| 422    | `104`         | The raw JSON payload exceeds 3 MB.  |


# User requests

Track and process requests initiated by users.

Use these endpoints when a customer wants to route pending profile changes through an internal review, approval, or ITSM workflow.

{% content-ref url="/pages/bibqMOcp07RQdFy34Xii" %}
[List user change requests](/reference/core-api/user-requests/list-user-change-requests)
{% endcontent-ref %}

{% content-ref url="/pages/vOsoFvMrYbl7UBldjvK4" %}
[Get user change requests](/reference/core-api/user-requests/get-user-change-requests)
{% endcontent-ref %}

{% content-ref url="/pages/RGftFXAM53kH8Sg44kNx" %}
[Approve user change request](/reference/core-api/user-requests/approve-user-change-request)
{% endcontent-ref %}

{% content-ref url="/pages/1KP0AkQKTZM7VeCP5aKG" %}
[Reject user change request](/reference/core-api/user-requests/reject-user-change-request)
{% endcontent-ref %}


# List user change requests

Retrieve all pending user change requests for attributes configured with admin approval.

## Endpoint

<mark style="color:blue;">`GET`</mark> `https://api.signitic.app/user-change-requests`

## Headers

| Name                                        | Type   | Description  |
| ------------------------------------------- | ------ | ------------ |
| x-api-key<mark style="color:red;">\*</mark> | String | Your API key |

## Success response

### `200 OK`

```json
{
  "success": true,
  "code": 200,
  "data": [
    {
      "email": "john.doe@example.com",
      "display_name": "John Doe",
      "change_requests": [
        {
          "field": "phone",
          "old_value": "0102030405",
          "new_value": "0607080910",
          "status": "pending"
        },
        {
          "field": "title",
          "old_value": "Sales",
          "new_value": "Head of Sales",
          "status": "pending"
        }
      ]
    }
  ]
}
```

## Error responses

| Status | Internal code | When                               |
| ------ | ------------- | ---------------------------------- |
| 401    | `99`          | The API key is missing or invalid. |


# Get user change requests

Retrieve pending change requests for one user.

## Endpoint

<mark style="color:blue;">`GET`</mark> `https://api.signitic.app/user-change-requests/:email`

## Headers

| Name                                        | Type   | Description  |
| ------------------------------------------- | ------ | ------------ |
| x-api-key<mark style="color:red;">\*</mark> | String | Your API key |

## Path Parameters

| Name                                    | Type   | Description |
| --------------------------------------- | ------ | ----------- |
| email<mark style="color:red;">\*</mark> | String | User email  |

## Success response

### `200 OK`

```json
{
  "success": true,
  "code": 200,
  "data": {
    "email": "john.doe@example.com",
    "display_name": "John Doe",
    "change_requests": [
      {
        "field": "phone",
        "old_value": "0102030405",
        "new_value": "0607080910",
        "status": "pending"
      }
    ]
  }
}
```

## Error responses

| Status | Internal code | When                                              |
| ------ | ------------- | ------------------------------------------------- |
| 400    | `101`         | The email format is invalid.                      |
| 401    | `99`          | The API key is missing or invalid.                |
| 404    | `102`         | The email does not match a user in the workspace. |


# Approve user change request

Approve one pending change request for a user.

## Endpoint

<mark style="color:orange;">`PATCH`</mark> `https://api.signitic.app/user-change-requests/:email/:field`

## Headers

| Name                                        | Type   | Description  |
| ------------------------------------------- | ------ | ------------ |
| x-api-key<mark style="color:red;">\*</mark> | String | Your API key |

## Path Parameters

| Name                                    | Type   | Description          |
| --------------------------------------- | ------ | -------------------- |
| email<mark style="color:red;">\*</mark> | String | User email           |
| field<mark style="color:red;">\*</mark> | String | Requested field slug |

## Request body

No request body is required.

## Success response

### `200 OK`

```json
{
  "success": true,
  "code": 200,
  "data": {
    "operation": "approved",
    "field": "phone",
    "user": {
      "email": "john.doe@example.com",
      "display_name": "John Doe",
      "change_requests": []
    }
  }
}
```

## Error responses

| Status | Internal code | When                                              |
| ------ | ------------- | ------------------------------------------------- |
| 400    | `101`         | The email format is invalid.                      |
| 401    | `99`          | The API key is missing or invalid.                |
| 404    | `102`         | The email does not match a user in the workspace. |
| 404    | `104`         | The field is not pending for the target user.     |


# Reject user change request

Reject one pending change request for a user.

## Endpoint

<mark style="color:red;">`DELETE`</mark> `https://api.signitic.app/user-change-requests/:email/:field`

## Headers

| Name                                        | Type   | Description  |
| ------------------------------------------- | ------ | ------------ |
| x-api-key<mark style="color:red;">\*</mark> | String | Your API key |

## Path Parameters

| Name                                    | Type   | Description          |
| --------------------------------------- | ------ | -------------------- |
| email<mark style="color:red;">\*</mark> | String | User email           |
| field<mark style="color:red;">\*</mark> | String | Requested field slug |

## Request body

No request body is required.

## Success response

### `200 OK`

```json
{
  "success": true,
  "code": 200,
  "data": {
    "operation": "rejected",
    "field": "phone",
    "user": {
      "email": "john.doe@example.com",
      "display_name": "John Doe",
      "change_requests": []
    }
  }
}
```

## Error responses

| Status | Internal code | When                                              |
| ------ | ------------- | ------------------------------------------------- |
| 400    | `101`         | The email format is invalid.                      |
| 401    | `99`          | The API key is missing or invalid.                |
| 404    | `102`         | The email does not match a user in the workspace. |
| 404    | `104`         | The field is not pending for the target user.     |


# Signatures

Retrieve generated email signatures for workspace users.

{% content-ref url="/pages/u9dBudbQhw1caspgjPgN" %}
[Get signature](/reference/core-api/signatures/get-signature)
{% endcontent-ref %}


# Get signature

Retrieve a user's signature.

## Get signature

<mark style="color:blue;">`GET`</mark> `https://api.signitic.app/signatures/:email/:mode`

## Path parameters

| Name                                    | Type   | Description      |
| --------------------------------------- | ------ | ---------------- |
| email<mark style="color:red;">\*</mark> |        | User email       |
| mode<mark style="color:red;">\*</mark>  | String | `json` or `html` |

## Headers

| Name                                        | Type   | Description  |
| ------------------------------------------- | ------ | ------------ |
| x-api-key<mark style="color:red;">\*</mark> | String | Your API key |

## Success response

{% tabs %}
{% tab title="200: OK Return JSON signature" %}

```json
{
  "success": true,
  "html": "<body>signature</body>"
}
```

{% endtab %}

{% tab title="200: OK Return HTML signature" %}

```html
<body>signature</body>
```

{% endtab %}
{% endtabs %}

If `mode=json`, the API returns a JSON object with the generated signature HTML in the `html` field.

If `mode=html`, the API returns the raw HTML signature with a `text/html` content type.

## Error responses

| Status | Internal code | When                                                                                                                                                                                                     |
| ------ | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 401    | `99`          | The `x-api-key` header is missing.                                                                                                                                                                       |
| 401    | n/a           | The API key does not match a workspace with API access. This error is returned in a legacy payload using `error` instead of `success`.                                                                   |
| 405    | `100`         | The reserved `users` path segment is used instead of a user email.                                                                                                                                       |
| 422    | `106`         | The user cannot receive a signature in the current state, for example because the user is inactive. Some business-rule errors are still returned in a legacy payload using `error` instead of `success`. |


# Desktop

Use these endpoints to configure or support the desktop agent experience.

{% content-ref url="/pages/0Ft3eJUcZOQG57K67FHF" %}
[Desktop agent configuration](/reference/core-api/desktop/desktop-agent-configuration)
{% endcontent-ref %}


# Desktop agent configuration

Use this endpoint to retrieve the configuration file consumed by the Signitic desktop agent on Windows and macOS.

This is also the endpoint used in centralized Windows deployments such as RDS or GPO-based rollouts.

## Endpoint

<mark style="color:blue;">`GET`</mark> `https://api.signitic.app/desktop/:email`

## Headers

| Name                                        | Type   | Description  |
| ------------------------------------------- | ------ | ------------ |
| x-api-key<mark style="color:red;">\*</mark> | String | Your API key |

## Path parameters

| Name                                    | Type   | Description                                                 |
| --------------------------------------- | ------ | ----------------------------------------------------------- |
| email<mark style="color:red;">\*</mark> | String | User email used to resolve the desktop agent configuration. |

## Success response

### `200 OK`

```json
{
  "cron_m": 17,
  "cron_h": "*/2",
  "2ec9c7c3d2d54f1f9f68d4d9b7c9d6c1": {
    "key": "2ec9c7c3d2d54f1f9f68d4d9b7c9d6c1",
    "email": "john.doe@signitic.com",
    "service": 3,
    "name": "John Doe"
  },
  "aa51c9115c3c4f31bc16507cb9fe5f32": {
    "key": "aa51c9115c3c4f31bc16507cb9fe5f32",
    "email": "john.alias@signitic.com",
    "service": 3,
    "name": "John Alias"
  }
}
```

### Response fields

| Name             | Type    | Description                                                 |
| ---------------- | ------- | ----------------------------------------------------------- |
| cron\_m          | Integer | Minute used by the desktop agent refresh schedule.          |
| cron\_h          | String  | Hour expression used by the desktop agent refresh schedule. |
| `<hash>.key`     | String  | Internal key used by the desktop app to request signatures. |
| `<hash>.email`   | String  | Email address attached to this configuration entry.         |
| `<hash>.service` | Integer | Internal service identifier used by the desktop app.        |
| `<hash>.name`    | String  | Display name shown by the desktop app.                      |

The response can include multiple entries:

* the main user
* aliases
* delegated users

## Error responses

| Status | Internal code | When                                                                                                                                   |
| ------ | ------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| 401    | `99`          | The `x-api-key` header is missing.                                                                                                     |
| 401    | n/a           | The API key does not match a workspace with API access. This error is returned in a legacy payload using `error` instead of `success`. |
| 404    | `102`         | The user cannot be resolved.                                                                                                           |
| 405    | `100`         | The reserved `users` path segment is used instead of a user identifier.                                                                |

## Deployment notes

For Windows RDS or GPO deployments, Signitic provides deployment guidance and example scripts here:

[Déploiement sur RDS ou par GPO](https://support.signitic.com/fr/articles/8224344-deploiement-sur-rds-ou-par-gpo)


# Webhooks

Receive events pushed by Signitic to your systems.

{% content-ref url="/pages/ATGkmznyXF4mxDXwnUim" %}
[Contact data webhook](/reference/core-api/webhooks/contact-data-webhook)
{% endcontent-ref %}


# Contact data webhook

## Webhook

When the contact form is enabled in your vCard settings, you can configure a webhook that Signitic sends each time a visitor submits the form on one of your vCards.

The webhook is sent to the URL configured in your vCard settings and contains the information submitted by the contact.

### Payload

| Name              | Type                                                               | Description                                |
| ----------------- | ------------------------------------------------------------------ | ------------------------------------------ |
| date              | Datetime [RFC 3339](https://datatracker.ietf.org/doc/html/rfc3339) | The moment the contact filled out the form |
| contact.email     | String or Null                                                     | The email of the contact                   |
| contact.firstname | String or Null                                                     | The first name of the contact              |
| contact.lastname  | String or Null                                                     | The last name of the contact               |
| contact.phone     | String or Null                                                     | The phone number of the contact            |
| contact.company   | String or Null                                                     | The company name of the contact            |
| employee.email    | String                                                             | The email of the owner of the vCard        |
| employee.fullName | String                                                             | The full name of the owner of the vCard    |

### Headers

| Name      | Type   | Description  |
| --------- | ------ | ------------ |
| x-api-key | String | Your API key |

You can use the `x-api-key` header as an additional verification layer to confirm that the payload was sent by Signitic.

{% tabs %}
{% tab title="Payload (example)" %}

```json
{
  "contact": {
    "firstname": "Jean",
    "lastname": "Dupont",
    "email": "jean.dupont@signitic.com",
    "phone": null,
    "company": null
  },
  "date": "2025-03-31T11:18:20+00:00",
  "employee": {
    "email": "john.doe@signitic.com",
    "fullName": "John Doe"
  }
}
```

{% endtab %}
{% endtabs %}


# Reseller API

Use the Reseller API to provision customer workspaces and monitor license consumption for reseller-managed customers.

{% content-ref url="/pages/UqH6wnTNLeEFh02gczNa" %}
[Create a reseller customer](/reference/reseller-api/reseller-create-customer)
{% endcontent-ref %}

{% content-ref url="/pages/YapQEraCwzmsBSTK3q3s" %}
[Get reseller consumption](/reference/reseller-api/reseller-consumption)
{% endcontent-ref %}


# Create a reseller customer

Create a new customer workspace from a reseller account.

## Endpoint

<mark style="color:blue;">`POST`</mark> `https://api.signitic.app/reseller/workspace`

## Headers

| Name                                        | Type   | Description             |
| ------------------------------------------- | ------ | ----------------------- |
| x-api-key<mark style="color:red;">\*</mark> | String | Your reseller API key.  |
| Content-Type                                | String | Use `application/json`. |

## Request body

| Name           | Type    | Required | Description                                                                                 |
| -------------- | ------- | -------- | ------------------------------------------------------------------------------------------- |
| email          | String  | Yes      | Main account email for the new customer workspace.                                          |
| firstName      | String  | No       | First name of the main account.                                                             |
| lastName       | String  | No       | Last name of the main account.                                                              |
| socialReason   | String  | No       | Company name. When omitted, the workspace name is derived from the email domain.            |
| language       | String  | No       | Account language. Default: `fr`.                                                            |
| customerAccess | Boolean | No       | When `true`, Signitic sends the onboarding email to the customer account. Default: `false`. |
| demoAccount    | Boolean | No       | When `true`, the workspace is created as a demo account. Default: `false`.                  |

### Example

```json
{
  "email": "admin@acme.com",
  "firstName": "Jane",
  "lastName": "Doe",
  "socialReason": "Acme",
  "language": "en",
  "customerAccess": true,
  "demoAccount": false
}
```

## Success response

### Example request

```http
POST /reseller/workspace HTTP/1.1
Host: api.signitic.app
x-api-key: <your-reseller-api-key>
Content-Type: application/json

{
  "email": "admin@acme.com",
  "firstName": "Jane",
  "lastName": "Doe",
  "socialReason": "Acme",
  "language": "en",
  "customerAccess": true,
  "demoAccount": false
}
```

### `200 OK`

```json
{
  "success": true,
  "message": "Workspace Acme has been created."
}
```

## Error responses

| Status | Internal code | When                                                                                                                                       |
| ------ | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| 200    | n/a           | The payload is rejected by the reseller provisioning flow. This endpoint can return `success: false` while still responding with `200 OK`. |
| 401    | n/a           | The reseller API key is missing or invalid.                                                                                                |


# Get reseller consumption

Retrieve the monthly license consumption for all direct customer workspaces attached to a reseller.

## Endpoint

<mark style="color:blue;">`GET`</mark> `https://api.signitic.app/reseller/consumption`

## Headers

| Name                                        | Type   | Description            |
| ------------------------------------------- | ------ | ---------------------- |
| x-api-key<mark style="color:red;">\*</mark> | String | Your reseller API key. |

## Query parameters

| Name         | Type   | Required | Description                      |
| ------------ | ------ | -------- | -------------------------------- |
| start\_month | String | Yes      | Start month in `YYYY-MM` format. |
| end\_month   | String | Yes      | End month in `YYYY-MM` format.   |

## Success response

### Example request

```http
GET /reseller/consumption?start_month=2026-01&end_month=2026-03 HTTP/1.1
Host: api.signitic.app
x-api-key: <your-reseller-api-key>
```

### `200 OK`

```json
[
  {
    "id": "01JQ2X2S6P8C8V8F6F0M6G9R6W",
    "name": "Acme",
    "consumption": {
      "2026-01": 12,
      "2026-02": 12,
      "2026-03": 14
    }
  },
  {
    "id": "01JQ2X6A4Q2JQ1X5HZ6A8V2A0N",
    "name": "Globex",
    "consumption": {
      "2026-01": 3
    }
  }
]
```

Only months with recorded license usage are returned in the `consumption` object.

## Error responses

| Status | Internal code | When                                        |
| ------ | ------------- | ------------------------------------------- |
| 401    | n/a           | The reseller API key is missing or invalid. |


# MCP Server

> Beta feature: this MCP Server is currently not open to the public.

Use the Signitic MCP Server to connect AI clients over HTTP and query read-only business data for a single Signitic workspace. The public MCP surface is intentionally bounded to support and operational use cases around users, campaigns, groups, workspace coverage, and business statistics.

Access is not open to the public yet. The documentation below describes the planned public MCP surface, but it is not available for customers at this time.

## Endpoint

<mark style="color:blue;">HTTP</mark> `https://mcp.signitic.com/mcp`

## Headers

| Name                                        | Type   | Description                                                   |
| ------------------------------------------- | ------ | ------------------------------------------------------------- |
| x-api-key<mark style="color:red;">\*</mark> | String | A Signitic API key belonging to a workspace with MCP enabled. |

## Transport

The current MCP exposure is HTTP-only. `stdio` is not supported on this server.

Use an MCP client that supports HTTP transport and custom headers, then send `x-api-key` with every request.

## Authentication and scope

* Every MCP session is resolved from the `x-api-key` header.
* The API key is mapped to exactly one workspace and MCP must be enabled on that workspace.
* All tools are read-only.
* Cross-workspace access is blocked. The optional `workspacePublicId` accepted by `stats_get_workspace_overview` must match the workspace resolved from the API key.

## Server guarantees

* Server name: `Signitic MCP`
* Version: `1.0.0`
* Default path: `/mcp`
* Session TTL: 1 hour
* Rate limit: 30 tool calls per minute, per tool, per workspace, per actor
* Audit logging: every successful and failed tool call is logged server-side

## Available tool domains

{% content-ref url="/pages/X34iDBnWlsrBtjL0piXd" %}
[User Tools](/reference/mcp-server/mcp-users)
{% endcontent-ref %}

{% content-ref url="/pages/sT09kJPbkd6kfjYbxZi9" %}
[Campaign Tools](/reference/mcp-server/mcp-campaigns)
{% endcontent-ref %}

{% content-ref url="/pages/HT31hHkJvozqimXhxe9n" %}
[Group Tools](/reference/mcp-server/mcp-groups)
{% endcontent-ref %}

{% content-ref url="/pages/C4KWhyd5CA3EZVZb9hqL" %}
[Workspace Tools](/reference/mcp-server/mcp-workspace)
{% endcontent-ref %}

{% content-ref url="/pages/4BBzaov3UyPl9mXp28jG" %}
[Stats Tools](/reference/mcp-server/mcp-stats)
{% endcontent-ref %}

## Public tool catalog

| Domain      | Tools                                                                                                                       |
| ----------- | --------------------------------------------------------------------------------------------------------------------------- |
| `user`      | `user_list`, `user_count`, `user_find`, `user_get_summary`, `user_get_support_context`                                      |
| `campaign`  | `campaign_list`, `campaign_find`, `campaign_get_summary`, `campaign_get_support_context`                                    |
| `group`     | `group_list`, `group_find`, `group_get_summary`, `group_get_support_context`                                                |
| `workspace` | `workspace_get_user_profile_completeness`, `workspace_get_campaign_assignment_overview`, `workspace_get_group_completeness` |
| `stats`     | `stats_get_workspace_overview`, `stats_get_campaign_performance`, `stats_get_user_metrics`                                  |

## Authentication failure

If the API key is missing or invalid, the server returns a JSON-RPC error response such as:

```json
{
  "jsonrpc": "2.0",
  "id": "",
  "error": {
    "code": -32001,
    "message": "Invalid API key.",
    "data": {
      "reason": "invalid_api_key"
    }
  }
}
```

with HTTP status `401 Unauthorized`.

If the API key resolves a workspace where MCP is disabled, the same JSON-RPC envelope is returned with:

* `message = "MCP is disabled for this workspace."`
* `error.data.reason = "mcp_disabled"`

## Tool execution errors

Tool-level validation, access, not-found, and rate-limit failures are returned as MCP execution errors by the server.

Common cases:

* Invalid parameters: for example an invalid public ID format, an invalid date, or `from > to`
* Not found: the requested user or campaign does not belong to the current workspace
* Rate limited: too many MCP tool requests

## Identifier formats

| Identifier          | Format                            |
| ------------------- | --------------------------------- |
| `userPublicId`      | `usr_<26 lowercase base32 chars>` |
| `campaignPublicId`  | `cp_<26 lowercase base32 chars>`  |
| `groupPublicId`     | `grp_<26 lowercase base32 chars>` |
| `workspacePublicId` | `<26 lowercase base32 chars>`     |

## Notes for clients

* Search tools are bounded to a maximum of 10 results per request.
* `user_list`, `campaign_list`, and `group_list` are capped to the first 25 readable records.
* `stats_get_campaign_performance` and `stats_get_user_metrics` default to a 30-day window, and infer the missing bound when only `from` or `to` is provided.
* Stats periods are limited to 90 inclusive days when both `from` and `to` are provided.
* `stats_get_workspace_overview` requires both `from` and `to`.
* On the dedicated MCP host, only `/` and `/mcp` are exposed.


# User Tools

Use the user tools to search users and retrieve support-oriented user context from the workspace bound to your API key.

## Available tools

| Tool                       | Purpose                                              |
| -------------------------- | ---------------------------------------------------- |
| `user_list`                | List readable users in the current workspace.        |
| `user_count`               | Count readable users with optional filters.          |
| `user_find`                | Search users with optional status and group filters. |
| `user_get_summary`         | Return the main operational summary for one user.    |
| `user_get_support_context` | Return support-specific context for one user.        |

## `user_list`

List readable users in the current workspace.

This tool does not take any parameters.

### Response

```json
{
  "items": [
    {
      "publicId": "usr_01hj5kq4k4a2v3x7p9m8n6c2dz",
      "email": "john.doe@example.com",
      "displayName": "John Doe",
      "status": "active",
      "group": "Sales"
    }
  ]
}
```

### Notes

* The response is capped to the first 25 readable users.
* Only users with a concrete status are returned: `active`, `inactive`, `hidden`, or `not_present`.
* Returned items use the same compact user reference shape as `user_find`.

## `user_count`

Count available users in the current workspace.

### Parameters

| Name     | Type   | Required | Description                                                                                      |
| -------- | ------ | -------- | ------------------------------------------------------------------------------------------------ |
| `query`  | String | No       | Optional search query. Maximum length: `190`.                                                    |
| `status` | String | No       | One of `active`, `inactive`, `hidden`, `not_present`.                                            |
| `field`  | String | No       | One of `any`, `email`, `total`. Default: `any`. `total` is accepted as a legacy alias for `any`. |

### Response

```json
{
  "count": 128,
  "query": "john",
  "field": "email",
  "status": "active"
}
```

### Notes

* When `query` is omitted or empty, `query` is returned as `null`.
* `count` only covers readable, non-deleted users with a non-null status in the workspace resolved from the API key.

## `user_find`

Search users by free text. Results are limited to available users in the current workspace.

### Parameters

| Name            | Type    | Required | Description                                           |
| --------------- | ------- | -------- | ----------------------------------------------------- |
| `query`         | String  | Yes      | Search query. Maximum length: `190`.                  |
| `status`        | String  | No       | One of `active`, `inactive`, `hidden`, `not_present`. |
| `groupPublicId` | String  | No       | Group public ID in `grp_<lowercase_ulid>` format.     |
| `limit`         | Integer | No       | Result limit. Default: `10`. Maximum: `10`.           |

### Response

```json
{
  "items": [
    {
      "publicId": "usr_01hj5kq4k4a2v3x7p9m8n6c2dz",
      "email": "john.doe@example.com",
      "displayName": "John Doe",
      "status": "active",
      "group": "Sales"
    }
  ]
}
```

### Notes

* Results only include readable, non-deleted users with a non-null status.
* `groupPublicId` must belong to a readable group in the current workspace.

## `user_get_summary`

Return the main operational summary for a single user.

### Parameters

| Name           | Type   | Required | Description                                      |
| -------------- | ------ | -------- | ------------------------------------------------ |
| `userPublicId` | String | Yes      | User public ID in `usr_<lowercase_ulid>` format. |

### Response

```json
{
  "publicId": "usr_01hj5kq4k4a2v3x7p9m8n6c2dz",
  "email": "john.doe@example.com",
  "displayName": "John Doe",
  "status": "active",
  "group": {
    "name": "Sales"
  },
  "workspace": {
    "name": "Acme"
  },
  "profile": {
    "identity": {
      "firstName": "John",
      "lastName": "Doe",
      "title": "Account Executive",
      "unit": "Revenue"
    },
    "contact": {
      "phone": "+33123456789",
      "mobile": "+33612345678",
      "secondEmail": "john.alt@example.com"
    },
    "address": {
      "streetAddr": "10 rue de Paris",
      "postalCode": "75001",
      "city": "Paris"
    },
    "formula": "Regards",
    "supplementaryLinks": {
      "calendar": "https://cal.example.com/john",
      "linkedin": "john-doe"
    },
    "extraFields": {
      "extra1": "Custom value"
    }
  },
  "connectors": {
    "tenant": "Microsoft 365",
    "sourceConnector": "azure",
    "enabledConnectors": [
      "desktop_app",
      "chrome_extension"
    ]
  }
}
```

### Field notes

* `workspace` always describes the workspace resolved from the API key.
* `group` currently returns only the group name.
* `profile` is compacted server-side: empty sub-objects and empty values are omitted.
* `formula` is sanitized before being returned.
* `enabledConnectors` is a derived list and can contain values such as `salesforce`, `pipedrive`, `smartlead`, `outreach`, `datananas`, `desktop_app`, and `chrome_extension`.
* `supplementaryLinks` and `extraFields` can appear in the profile payload, but they are excluded from workspace completeness metrics.

## `user_get_support_context`

Return support-focused information about licensing, activation, and connector state for one user.

### Parameters

| Name           | Type   | Required | Description                                      |
| -------------- | ------ | -------- | ------------------------------------------------ |
| `userPublicId` | String | Yes      | User public ID in `usr_<lowercase_ulid>` format. |

### Response

```json
{
  "user": {
    "publicId": "usr_01hj5kq4k4a2v3x7p9m8n6c2dz",
    "email": "john.doe@example.com",
    "displayName": "John Doe",
    "status": "active",
    "group": "Sales"
  },
  "status": "active",
  "group": {
    "name": "Sales"
  },
  "license": {
    "hasLicense": true,
    "expiresAt": "2026-12-31T23:59:59+00:00"
  },
  "activation": {
    "desktopAppActivated": true,
    "vcardEnabled": true,
    "isAlias": false,
    "hasPhoto": true
  },
  "connectors": {
    "tenant": "Microsoft 365",
    "sourceConnector": "azure",
    "enabledConnectors": [
      "desktop_app",
      "chrome_extension"
    ]
  },
  "flags": {
    "hasGroup": true,
    "hasTenant": true,
    "hasSecondEmail": false,
    "hasLicense": true
  }
}
```

### Field notes

* `user` merges the lightweight user reference with the compacted profile payload.
* `license.hasLicense` is the primary support flag for licensing.
* `license.expiresAt` is `null` when there is no expiration date.
* `activation.isAlias` is `true` for alias users.
* `activation.desktopAppActivated` is a boolean, not a timestamp.
* `flags` provides fast booleans for support workflows without extra client-side inference.


# Campaign Tools

Use the campaign tools to search campaigns and retrieve campaign support context in the workspace bound to your API key.

## Available tools

| Tool                           | Purpose                                               |
| ------------------------------ | ----------------------------------------------------- |
| `campaign_list`                | List readable campaigns in the current workspace.     |
| `campaign_find`                | Search campaigns by query and optional status.        |
| `campaign_get_summary`         | Return the main operational summary for one campaign. |
| `campaign_get_support_context` | Return support-specific context for one campaign.     |

## `campaign_list`

List readable campaigns in the current workspace.

This tool does not take any parameters.

### Response

```json
{
  "items": [
    {
      "publicId": "cp_01hj5kq4k4a2v3x7p9m8n6c2dz",
      "name": "Spring Launch",
      "status": "running",
      "isUnlimited": false,
      "startDate": "2026-03-01",
      "endDate": "2026-04-15"
    }
  ]
}
```

### Notes

* The response is capped to the first 25 readable campaigns.
* Deleted campaigns are excluded from `campaign_list`.
* Returned items use the same compact campaign reference shape as `campaign_find`.
* Results are ordered for operational triage: running first, then planned, paused, completed, and deleted last when applicable in filtered searches.

## `campaign_find`

Search campaigns by free text.

### Parameters

| Name     | Type    | Required | Description                                                 |
| -------- | ------- | -------- | ----------------------------------------------------------- |
| `query`  | String  | Yes      | Search query. Maximum length: `120`.                        |
| `status` | String  | No       | One of `active`, `paused`, `deleted`, `planned`, `running`. |
| `limit`  | Integer | No       | Result limit. Default: `10`. Maximum: `10`.                 |

### Response

```json
{
  "items": [
    {
      "publicId": "cp_01hj5kq4k4a2v3x7p9m8n6c2dz",
      "name": "Spring Launch",
      "status": "running",
      "isUnlimited": false,
      "startDate": "2026-03-01",
      "endDate": "2026-04-15"
    }
  ]
}
```

### Notes

* When no `status` filter is provided, deleted campaigns are excluded.
* The returned `status` can be `deleted`, `paused`, `planned`, `running`, or `completed`.
* `isUnlimited = true` means the campaign has no end date.

## `campaign_get_summary`

Return the main operational summary for one campaign.

### Parameters

| Name               | Type   | Required | Description                                         |
| ------------------ | ------ | -------- | --------------------------------------------------- |
| `campaignPublicId` | String | Yes      | Campaign public ID in `cp_<lowercase_ulid>` format. |

### Response

```json
{
  "publicId": "cp_01hj5kq4k4a2v3x7p9m8n6c2dz",
  "name": "Spring Launch",
  "status": "running",
  "dates": {
    "startDate": "2026-03-01",
    "endDate": "2026-04-15"
  },
  "assignments": {
    "directUsers": 25,
    "groups": 3,
    "totalUsers": 148
  },
  "flags": {
    "hasLink": true,
    "hasBanner": true,
    "hasMetadata": true,
    "isUnlimited": false,
    "hasActiveAnomalies": false
  },
  "metadata": {
    "description": "Main campaign for the spring launch.",
    "direction": "internal",
    "priority": 10,
    "activeAnomalyCount": 0
  }
}
```

## `campaign_get_support_context`

Return support-focused targeting and anomaly information for one campaign.

### Parameters

| Name               | Type   | Required | Description                                         |
| ------------------ | ------ | -------- | --------------------------------------------------- |
| `campaignPublicId` | String | Yes      | Campaign public ID in `cp_<lowercase_ulid>` format. |

### Response

```json
{
  "campaign": {
    "publicId": "cp_01hj5kq4k4a2v3x7p9m8n6c2dz",
    "name": "Spring Launch",
    "status": "running",
    "isUnlimited": false,
    "startDate": "2026-03-01",
    "endDate": "2026-04-15"
  },
  "status": "running",
  "dates": {
    "startDate": "2026-03-01",
    "endDate": "2026-04-15"
  },
  "targeting": {
    "directUsers": 25,
    "excludedUsers": 2,
    "groups": 3,
    "domains": 1,
    "contactLists": 0
  },
  "anomalies": [
    {
      "type": "missing_banner",
      "state": "active"
    }
  ],
  "operations": {
    "hasLink": true,
    "hasBanner": true,
    "isUnlimited": false,
    "priority": 10,
    "direction": "internal"
  }
}
```

### Field notes

* `anomalies` returns a compact summary of active anomalies with `type` and `state`.
* `targeting` is designed for support and operational triage, not for full targeting export.
* `operations.isUnlimited = true` means the campaign has no end date.


# Group Tools

Use the group tools to browse the readable organization tree of the workspace bound to your API key.

## Available tools

| Tool                        | Purpose                                               |
| --------------------------- | ----------------------------------------------------- |
| `group_list`                | List readable groups in the current workspace.        |
| `group_find`                | Search groups by query.                               |
| `group_get_summary`         | Return the main organizational summary for one group. |
| `group_get_support_context` | Return support-oriented context for one group.        |

## `group_list`

List readable groups in the current workspace.

This tool does not take any parameters.

### Response

```json
{
  "items": [
    {
      "publicId": "grp_01hj5kq4k4a2v3x7p9m8n6c2dz",
      "name": "Sales",
      "isDefault": false
    }
  ]
}
```

### Notes

* The response is capped to the first 25 readable groups.
* `isDefault` identifies the default group configured in the workspace.

## `group_find`

Search groups by name.

### Parameters

| Name    | Type    | Required | Description                                 |
| ------- | ------- | -------- | ------------------------------------------- |
| `query` | String  | Yes      | Search query. Maximum length: `190`.        |
| `limit` | Integer | No       | Result limit. Default: `10`. Maximum: `10`. |

### Response

```json
{
  "items": [
    {
      "publicId": "grp_01hj5kq4k4a2v3x7p9m8n6c2dz",
      "name": "Sales",
      "isDefault": false
    }
  ]
}
```

## `group_get_summary`

Return the main organizational summary for a single group.

### Parameters

| Name            | Type   | Required | Description                                       |
| --------------- | ------ | -------- | ------------------------------------------------- |
| `groupPublicId` | String | Yes      | Group public ID in `grp_<lowercase_ulid>` format. |

### Response

```json
{
  "publicId": "grp_01hj5kq4k4a2v3x7p9m8n6c2dz",
  "name": "Sales",
  "workspace": {
    "name": "Acme"
  },
  "hierarchy": {
    "parent": {
      "publicId": "grp_01hj5jq4k4a2v3x7p9m8n6c2da",
      "name": "Europe"
    },
    "childrenCount": 3,
    "depth": 2
  },
  "configuration": {
    "fixedFieldsCount": 4,
    "hasLogo": true,
    "hasPrimaryColor": true,
    "hasSecondaryColor": true,
    "hasVcardLogo": false,
    "hasVcardHeader": true,
    "hasVcardFooter": false,
    "hasVcardBackgroundImage": false
  },
  "metrics": {
    "users": 42
  },
  "flags": {
    "isDefault": false,
    "hasParent": true,
    "hasChildren": true
  }
}
```

### Field notes

* `hierarchy.depth` is based on the number of ancestors in the group tree.
* `metrics.users` is only returned when the caller can read user data for that workspace.
* `configuration` exposes compact booleans and counts, not the underlying design payloads.

## `group_get_support_context`

Return support-oriented context for a single group.

### Parameters

| Name            | Type   | Required | Description                                       |
| --------------- | ------ | -------- | ------------------------------------------------- |
| `groupPublicId` | String | Yes      | Group public ID in `grp_<lowercase_ulid>` format. |

### Response

```json
{
  "group": {
    "publicId": "grp_01hj5kq4k4a2v3x7p9m8n6c2dz",
    "name": "Sales",
    "isDefault": false
  },
  "workspace": {
    "name": "Acme"
  },
  "hierarchy": {
    "parent": {
      "publicId": "grp_01hj5jq4k4a2v3x7p9m8n6c2da",
      "name": "Europe"
    },
    "childrenCount": 3,
    "children": [
      {
        "publicId": "grp_01hj5mq4k4a2v3x7p9m8n6c2db",
        "name": "Field Sales"
      }
    ],
    "depth": 2
  },
  "configuration": {
    "fixedFieldsCount": 4,
    "hasLogo": true,
    "hasPrimaryColor": true,
    "hasSecondaryColor": true,
    "hasVcardLogo": false,
    "hasVcardHeader": true,
    "hasVcardFooter": false,
    "hasVcardBackgroundImage": false
  },
  "metrics": {
    "users": 42
  },
  "flags": {
    "isDefault": false,
    "hasParent": true,
    "hasChildren": true
  }
}
```

### Field notes

* `hierarchy.children` is capped to the first 10 readable direct children.
* This tool is intended for support and operational triage, not full organization export.


# Workspace Tools

Use the workspace tools to retrieve read-only coverage and assignment metrics aggregated at workspace level.

These tools intentionally expose bounded operational summaries, not raw user exports.

## Available tools

| Tool                                         | Purpose                                                                                 |
| -------------------------------------------- | --------------------------------------------------------------------------------------- |
| `workspace_get_user_profile_completeness`    | Return profile completeness metrics for readable active users.                          |
| `workspace_get_campaign_assignment_overview` | Return campaign assignment counts, percentages, and averages for readable active users. |
| `workspace_get_group_completeness`           | Return profile completeness metrics grouped by readable group.                          |

## `workspace_get_user_profile_completeness`

Return the completeness coverage of tracked live profile fields for readable active users in the current workspace.

This tool does not take any parameters.

### Response

```json
{
  "workspace": {
    "name": "Acme"
  },
  "totals": {
    "activeReadableUsers": 128
  },
  "coverageModel": "tracked_live_fields",
  "trackedSections": [
    {
      "name": "identity",
      "fields": ["firstName", "lastName", "title", "unit"]
    },
    {
      "name": "contact",
      "fields": ["phone", "mobile", "secondEmail"]
    },
    {
      "name": "address",
      "fields": ["streetAddr", "postalCode", "city"]
    }
  ],
  "coverage": {
    "identity": {
      "fields": {
        "firstName": {
          "filledUsers": 126,
          "percentage": 98
        }
      }
    }
  }
}
```

### Field notes

* The `tracked_live_fields` model only covers `identity`, `contact`, and `address`.
* `formula`, `supplementaryLinks`, and `extraFields` are intentionally excluded from workspace completeness.
* Percentages are rounded server-side to integers.

## `workspace_get_campaign_assignment_overview`

Return campaign assignment overview metrics for readable active users in the current workspace.

This tool does not take any parameters.

### Response

```json
{
  "workspace": {
    "name": "Acme"
  },
  "totals": {
    "activeReadableUsers": 128,
    "totalAssignments": 356,
    "usersWithoutAssignments": 17
  },
  "averages": {
    "campaignsPerUser": 2.78
  },
  "distribution": {
    "usersWithAtLeastOneAssignment": 111,
    "usersWithAtLeastOneAssignmentPercentage": 87
  }
}
```

### Field notes

* `campaignsPerUser` is rounded to two decimals.
* `usersWithAtLeastOneAssignmentPercentage` is rounded to an integer percentage.

## `workspace_get_group_completeness`

Return completeness coverage grouped by readable group in the current workspace.

This tool does not take any parameters.

### Response

```json
{
  "workspace": {
    "name": "Acme"
  },
  "coverageModel": "tracked_live_fields",
  "trackedSections": [
    {
      "name": "identity",
      "fields": ["firstName", "lastName", "title", "unit"]
    }
  ],
  "groups": [
    {
      "publicId": "grp_01hj5kq4k4a2v3x7p9m8n6c2dz",
      "name": "Sales",
      "users": 42,
      "coverage": {
        "identity": {
          "fields": {
            "firstName": {
              "filledUsers": 42,
              "percentage": 100
            }
          }
        }
      }
    }
  ]
}
```

### Field notes

* `groups` is sorted by descending user count.
* Groups with zero readable users can still appear with zeroed coverage metrics.
* The coverage model and tracked sections are the same as `workspace_get_user_profile_completeness`.


# Stats Tools

Use the stats tools to read workspace, campaign, and user metrics for the workspace bound to your API key.

## Available tools

| Tool                             | Purpose                                                  |
| -------------------------------- | -------------------------------------------------------- |
| `stats_get_workspace_overview`   | Return a workspace-level overview for a required period. |
| `stats_get_campaign_performance` | Return read-only metrics for one campaign.               |
| `stats_get_user_metrics`         | Return read-only metrics for one user.                   |

## `stats_get_workspace_overview`

Return the main workspace statistics for a required date range.

### Parameters

| Name                | Type   | Required | Description                                                                     |
| ------------------- | ------ | -------- | ------------------------------------------------------------------------------- |
| `from`              | String | Yes      | Start date in `Y-m-d` format.                                                   |
| `to`                | String | Yes      | End date in `Y-m-d` format.                                                     |
| `breakdown`         | String | No       | One of `none`, `day`, `week`, `month`. Default: `none`.                         |
| `workspacePublicId` | String | No       | Optional workspace public ID. If provided, it must match the current workspace. |

### Response

```json
{
  "workspace": {
    "name": "Acme"
  },
  "period": {
    "from": "2026-03-01",
    "to": "2026-03-31"
  },
  "totals": {
    "campaignClicks": 1250,
    "vcardClicks": 310,
    "totalClicks": 1840
  },
  "breakdown": {
    "chart": {
      "datasets": {
        "current": [],
        "previous": [],
        "campaigns": []
      },
      "labels": {
        "current": [],
        "previous": []
      }
    }
  },
  "signals": [
    {
      "type": "campaign_clicks",
      "count": 1250
    }
  ],
  "generatedAt": "2026-03-28T10:25:00+00:00"
}
```

### Field notes

* `signals` is a compact business summary derived from `totals`.
* Supported signal types are currently `campaign_clicks`, `vcard_clicks`, and `other_clicks`.
* `generatedAt` is returned in RFC 3339 format.
* When `breakdown = none`, `breakdown` is an empty object.
* The selected period must not exceed 90 inclusive days.

## `stats_get_campaign_performance`

Return metrics for one campaign.

### Parameters

| Name               | Type   | Required | Description                                         |
| ------------------ | ------ | -------- | --------------------------------------------------- |
| `campaignPublicId` | String | Yes      | Campaign public ID in `cp_<lowercase_ulid>` format. |
| `from`             | String | No       | Optional start date in `Y-m-d` format.              |
| `to`               | String | No       | Optional end date in `Y-m-d` format.                |

### Response

```json
{
  "campaign": {
    "publicId": "cp_01hj5kq4k4a2v3x7p9m8n6c2dz",
    "name": "Spring Launch",
    "status": "running"
  },
  "period": {
    "from": "2026-02-27",
    "to": "2026-03-28"
  },
  "metrics": {
    "clicks": 1250,
    "assignedUsers": 148,
    "groups": 3
  },
  "trend": null,
  "generatedAt": "2026-03-28T10:25:00+00:00"
}
```

### Notes

* When `from` and `to` are both omitted, the server defaults to the last 30 days ending today.
* If only `to` is provided, `from` is inferred as 30 days before `to`.
* If only `from` is provided, `to` is inferred as today.
* `trend` is currently always `null`.
* When both `from` and `to` are provided, the selected period must not exceed 90 inclusive days.

## `stats_get_user_metrics`

Return metrics for one user.

### Parameters

| Name           | Type   | Required | Description                                      |
| -------------- | ------ | -------- | ------------------------------------------------ |
| `userPublicId` | String | Yes      | User public ID in `usr_<lowercase_ulid>` format. |
| `from`         | String | No       | Optional start date in `Y-m-d` format.           |
| `to`           | String | No       | Optional end date in `Y-m-d` format.             |

### Response

```json
{
  "user": {
    "publicId": "usr_01hj5kq4k4a2v3x7p9m8n6c2dz",
    "email": "john.doe@example.com",
    "displayName": "John Doe",
    "status": "active"
  },
  "period": {
    "from": "2026-02-27",
    "to": "2026-03-28"
  },
  "metrics": {
    "clicks": 87,
    "campaignAssignments": 4,
    "aliases": 1
  },
  "generatedAt": "2026-03-28T10:25:00+00:00"
}
```

### Notes

* The same 30-day default period logic applies as for `stats_get_campaign_performance`.
* `campaignAssignments` is the number of campaigns attached to the user.
* `aliases` is the number of aliases attached to the user.
* If only `to` is provided, `from` is inferred as 30 days before `to`.
* If only `from` is provided, `to` is inferred as today.
* When both `from` and `to` are provided, the selected period must not exceed 90 inclusive days.


