# Stats

Part of AppEngine API Documentation. Full index: https://appengine.appmint.io/documentation.md
## POST /stats/{datatype}/{id}/like

**Like a record**

`operationId: StatsController_like`

Records that the caller likes a record.

A per-caller toggle: send `action: "remove"` to undo it. Repeating `add` does not double-count — the signal is recorded once per caller.

#### Signature

```http
POST /stats/{datatype}/{id}/like (datatype: string, id: string, body) -> The updated stats
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `GET /stats/{datatype}/{id}`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `datatype` | path | string | yes | DataType of the record. |
| `id` | path | string | yes | Record id. |

### Request body

Whether to add or remove the signal. Defaults to `add`.

```json
{
  "action": "add"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The updated stats |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /stats/{datatype}/{id}/dislike

**Dislike a record**

`operationId: StatsController_dislike`

Records that the caller dislikes a record. Separate from removing a like — a dislike is its own signal.

A per-caller toggle: send `action: "remove"` to undo it. Repeating `add` does not double-count — the signal is recorded once per caller.

#### Signature

```http
POST /stats/{datatype}/{id}/dislike (datatype: string, id: string, body) -> The updated stats
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `GET /stats/{datatype}/{id}`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `datatype` | path | string | yes | DataType of the record. |
| `id` | path | string | yes | Record id. |

### Request body

Whether to add or remove the signal. Defaults to `add`.

```json
{
  "action": "add"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The updated stats |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /stats/{datatype}/{id}/bookmark

**Bookmark a record**

`operationId: StatsController_bookmark`

Saves a record to the caller's bookmarks.

A per-caller toggle: send `action: "remove"` to undo it. Repeating `add` does not double-count — the signal is recorded once per caller.

#### Signature

```http
POST /stats/{datatype}/{id}/bookmark (datatype: string, id: string, body) -> The updated stats
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `GET /stats/{datatype}/{id}`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `datatype` | path | string | yes | DataType of the record. |
| `id` | path | string | yes | Record id. |

### Request body

Whether to add or remove the signal. Defaults to `add`.

```json
{
  "action": "add"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The updated stats |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /stats/{datatype}/{id}/follow

**Follow a record**

`operationId: StatsController_follow`

Follows a record, so the caller is tracked as interested in it.

A per-caller toggle: send `action: "remove"` to undo it. Repeating `add` does not double-count — the signal is recorded once per caller.

#### Signature

```http
POST /stats/{datatype}/{id}/follow (datatype: string, id: string, body) -> The updated stats
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `GET /stats/{datatype}/{id}`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `datatype` | path | string | yes | DataType of the record. |
| `id` | path | string | yes | Record id. |

### Request body

Whether to add or remove the signal. Defaults to `add`.

```json
{
  "action": "add"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The updated stats |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /stats/{datatype}/{id}/favorite

**Favorite a record**

`operationId: StatsController_favorite`

Marks a record as one of the caller's favourites.

A per-caller toggle: send `action: "remove"` to undo it. Repeating `add` does not double-count — the signal is recorded once per caller.

#### Signature

```http
POST /stats/{datatype}/{id}/favorite (datatype: string, id: string, body) -> The updated stats
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `GET /stats/{datatype}/{id}`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `datatype` | path | string | yes | DataType of the record. |
| `id` | path | string | yes | Record id. |

### Request body

Whether to add or remove the signal. Defaults to `add`.

```json
{
  "action": "add"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The updated stats |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /stats/{datatype}/{id}/rating

**Rate a record**

`operationId: StatsController_rating`

Records the caller's numeric rating. `action: "update"` changes an existing rating rather than adding a second — send it when the caller has already rated, or the aggregate counts them twice.

#### Signature

```http
POST /stats/{datatype}/{id}/rating (datatype: string, id: string, body) -> The updated stats
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Notes

- Use `update` to change an existing rating.

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `GET /stats/{datatype}/{id}`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `datatype` | path | string | yes | DataType of the record. |
| `id` | path | string | yes | Record id. |

### Request body

The rating.

```json
{
  "value": 4
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The updated stats |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /stats/{datatype}/{id}/reaction

**React to a record**

`operationId: StatsController_reaction`

Records a named reaction — the open-ended signal, where like and favourite are fixed ones. The reaction name is whatever the caller sends.

#### Signature

```http
POST /stats/{datatype}/{id}/reaction (datatype: string, id: string, body) -> The updated stats
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `POST /stats/{datatype}/{id}/like`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `datatype` | path | string | yes | DataType of the record. |
| `id` | path | string | yes | Record id. |

### Request body

The reaction.

```json
{
  "value": "celebrate"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The updated stats |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /stats/{datatype}/{id}/view

**Record a view**

`operationId: StatsController_view`

Increments the record's view count. A **counter, not a toggle** — every call adds one, so a client that fires it on each render inflates the figure.

#### Signature

```http
POST /stats/{datatype}/{id}/view (datatype: string, id: string) -> The updated stats
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Notes

- Increments on every call.

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `POST /stats/{datatype}/{id}/share`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `datatype` | path | string | yes | DataType of the record. |
| `id` | path | string | yes | Record id. |

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The updated stats |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /stats/{datatype}/{id}/share

**Record a share**

`operationId: StatsController_share`

Increments the record's share count. Also a counter — it records that a share was initiated, not that anyone received it.

#### Signature

```http
POST /stats/{datatype}/{id}/share (datatype: string, id: string) -> The updated stats
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Notes

- Increments on every call.

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `POST /stats/{datatype}/{id}/view`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `datatype` | path | string | yes | DataType of the record. |
| `id` | path | string | yes | Record id. |

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The updated stats |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /stats/{datatype}/{id}

**Get a record's stats**

`operationId: StatsController_get`

Aggregate signals for one record — counts, average rating, and the caller's own state where relevant.

#### Signature

```http
GET /stats/{datatype}/{id} (datatype: string, id: string) -> The stats
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `GET /stats/by-resource/{datatype}/{id}`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `datatype` | path | string | yes | DataType of the record. |
| `id` | path | string | yes | Record id. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The stats |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /stats/by-resource/{datatype}/{id}

**Get stats by resource**

`operationId: StatsController_byResource`

The signals recorded against one record, listed rather than aggregated — who liked it, who bookmarked it.

#### Signature

```http
GET /stats/by-resource/{datatype}/{id} (datatype: string, id: string) -> Signals on the record
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `GET /stats/by-customer/{datatype}/{id}`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `datatype` | path | string | yes | DataType of the record. |
| `id` | path | string | yes | Record id. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | Signals on the record |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /stats/by-customer/{datatype}/{id}

**Get stats by customer**

`operationId: StatsController_byCustomer`

The signals the **calling customer** has recorded, optionally narrowed to a datatype or a single record. Both path segments are optional — omit them for everything the caller has liked, bookmarked or followed.

#### Signature

```http
GET /stats/by-customer/{datatype}/{id} (datatype: string, id: string) -> The caller's signals
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `GET /stats/by-resource/{datatype}/{id}`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `datatype` | path | string | yes | DataType. Optional. |
| `id` | path | string | yes | Record id. Optional. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The caller's signals |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

